fix(backend): refresh-token jti + code cleanup (#15,#28,#29,#30)
Refresh-token rotation bug (found via Solaria smoke test): * RefreshClaims now carries a unique random jti (UUID v4). Without it, two refresh tokens issued in the same second (e.g. on rotation) were byte-identical — breaking rotation, colliding on the tokenHash unique index, and making a stolen old token indistinguishable from the new one. Every refresh token is now unique regardless of issue time. Added a unit test asserting consecutive tokens differ. Code cleanup (review items): * #15: removed unused deps tower_governor, slog, thiserror (zero refs in src/). * #30: deleted leftover src/main.rs.restore (a stale git-error-message backup). * #28: removed the 9 single-line-comment stub files under src/db/ (appointment, family, health_data, lab_result, medication, profile, permission, share, user) and their mod declarations; nothing referenced them, real logic lives in mongodb_impl.rs/init.rs. * #29: stripped 61 broad module-level #![allow(...)] suppressions across src/. Fixed the surfaced warnings instead: 4 unused 'claims' extractor bindings -> _claims; removed dead OpenFDAService.client/base_url fields + the never-called query_drug_events method + unused HashMap/reqwest imports; applied clippy's mechanical fixes (Ok(?) -> ?, Copy ObjectId clone, redundant closures, useless conversions); rewrote 'if let Ok(_) = x' -> 'if x.is_ok()'. Result: cargo build + clippy --all-targets are warning-free with no blanket allows. Verified: fmt clean, build clean, clippy 0 warnings, 19 unit tests pass (was 18; +1 jti-uniqueness test).
This commit is contained in:
parent
e9f524671f
commit
cf6fcd4ef2
45 changed files with 54 additions and 297 deletions
158
backend/Cargo.lock
generated
158
backend/Cargo.lock
generated
|
|
@ -280,12 +280,6 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.7"
|
||||
|
|
@ -331,19 +325,6 @@ dependencies = [
|
|||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dashmap"
|
||||
version = "5.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"hashbrown 0.14.5",
|
||||
"lock_api",
|
||||
"once_cell",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "2.10.0"
|
||||
|
|
@ -438,15 +419,6 @@ version = "1.0.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "erased-serde"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c138974f9d5e7fe373eb04df7cae98833802ae4b11c24ac7039a21d5af4b26c"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.14"
|
||||
|
|
@ -505,16 +477,6 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "forwarded-header-value"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8835f84f38484cc86f110a805655697908257fb9a7af005234060891557198e9"
|
||||
dependencies = [
|
||||
"nonempty",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "funty"
|
||||
version = "2.0.0"
|
||||
|
|
@ -592,12 +554,6 @@ version = "0.3.32"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
|
||||
|
||||
[[package]]
|
||||
name = "futures-timer"
|
||||
version = "3.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.32"
|
||||
|
|
@ -665,26 +621,6 @@ dependencies = [
|
|||
"wasip3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "governor"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68a7f542ee6b35af73b06abc0dad1c1bae89964e4e253bc4b587b91c9637867b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"dashmap",
|
||||
"futures",
|
||||
"futures-timer",
|
||||
"no-std-compat",
|
||||
"nonzero_ext",
|
||||
"parking_lot",
|
||||
"portable-atomic",
|
||||
"quanta",
|
||||
"rand 0.8.5",
|
||||
"smallvec",
|
||||
"spinning_top",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.4.13"
|
||||
|
|
@ -704,12 +640,6 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
|
|
@ -1291,24 +1221,6 @@ dependencies = [
|
|||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "no-std-compat"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c"
|
||||
|
||||
[[package]]
|
||||
name = "nonempty"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7"
|
||||
|
||||
[[package]]
|
||||
name = "nonzero_ext"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21"
|
||||
|
||||
[[package]]
|
||||
name = "normogen-backend"
|
||||
version = "0.1.0"
|
||||
|
|
@ -1328,14 +1240,11 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"slog",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"thiserror 1.0.69",
|
||||
"tokio",
|
||||
"tower 0.4.13",
|
||||
"tower-http 0.5.2",
|
||||
"tower_governor",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"uuid",
|
||||
|
|
@ -1544,12 +1453,6 @@ version = "0.3.32"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
|
||||
|
||||
[[package]]
|
||||
name = "potential_utf"
|
||||
version = "0.1.4"
|
||||
|
|
@ -1617,21 +1520,6 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quanta"
|
||||
version = "0.12.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"raw-cpuid",
|
||||
"wasi",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.45"
|
||||
|
|
@ -1718,15 +1606,6 @@ dependencies = [
|
|||
"getrandom 0.3.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "raw-cpuid"
|
||||
version = "11.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.18"
|
||||
|
|
@ -2175,18 +2054,6 @@ version = "0.4.12"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
|
||||
|
||||
[[package]]
|
||||
name = "slog"
|
||||
version = "2.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b3b8565691b22d2bdfc066426ed48f837fc0c5f2c8cad8d9718f7f99d6995c1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"erased-serde",
|
||||
"rustversion",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
|
|
@ -2223,15 +2090,6 @@ dependencies = [
|
|||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spinning_top"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.1"
|
||||
|
|
@ -2626,22 +2484,6 @@ version = "0.3.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
|
||||
|
||||
[[package]]
|
||||
name = "tower_governor"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aea939ea6cfa7c4880f3e7422616624f97a567c16df67b53b11f0d03917a8e46"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"forwarded-header-value",
|
||||
"governor",
|
||||
"http",
|
||||
"pin-project",
|
||||
"thiserror 1.0.69",
|
||||
"tower 0.5.3",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.44"
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ axum = "0.7.9"
|
|||
tokio = { version = "1.41.1", features = ["full"] }
|
||||
tower = { version = "0.4.13", features = ["util"] }
|
||||
tower-http = { version = "0.5.2", features = ["cors", "trace"] }
|
||||
tower_governor = "0.4.3"
|
||||
serde = { version = "1.0.215", features = ["derive"] }
|
||||
serde_json = "1.0.133"
|
||||
mongodb = "2.8.2"
|
||||
|
|
@ -23,11 +22,9 @@ password-hash = "0.5.0"
|
|||
rand = "0.8.5"
|
||||
base64 = "0.22.1"
|
||||
sha2 = "0.10"
|
||||
thiserror = "1.0.69"
|
||||
anyhow = "1.0.94"
|
||||
tracing = "0.1.41"
|
||||
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
|
||||
slog = "2.7.0"
|
||||
strum = { version = "0.26", features = ["derive"] }
|
||||
strum_macros = "0.26"
|
||||
futures = "0.3"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
#![allow(dead_code)]
|
||||
use anyhow::Result;
|
||||
use chrono::{Duration, Utc};
|
||||
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
|
||||
|
|
@ -41,12 +40,17 @@ pub struct RefreshClaims {
|
|||
pub sub: String,
|
||||
pub exp: usize,
|
||||
pub iat: usize,
|
||||
/// Unique per-token id. Without this, two refresh tokens for the same user
|
||||
/// issued in the same second (e.g. on rotation) would be byte-identical,
|
||||
/// breaking rotation/reuse-detection. The jti makes every token unique.
|
||||
pub jti: String,
|
||||
pub user_id: String,
|
||||
pub token_version: i32,
|
||||
}
|
||||
|
||||
impl RefreshClaims {
|
||||
/// Build refresh-token claims. `expiry` is taken from `JwtConfig` by callers.
|
||||
/// A fresh random `jti` is generated so each token is unique.
|
||||
pub fn new(user_id: String, token_version: i32, expiry: Duration) -> Self {
|
||||
let now = Utc::now();
|
||||
let exp = now + expiry;
|
||||
|
|
@ -55,6 +59,7 @@ impl RefreshClaims {
|
|||
sub: user_id.clone(),
|
||||
exp: exp.timestamp() as usize,
|
||||
iat: now.timestamp() as usize,
|
||||
jti: uuid::Uuid::new_v4().to_string(),
|
||||
user_id,
|
||||
token_version,
|
||||
}
|
||||
|
|
@ -175,4 +180,30 @@ mod tests {
|
|||
let refresh_claims = svc.validate_refresh_token(&refresh).unwrap();
|
||||
assert_eq!(refresh_claims.token_version, 3);
|
||||
}
|
||||
|
||||
/// Two refresh tokens issued back-to-back for the same user must be distinct,
|
||||
/// even though everything else (sub, user_id, token_version, iat, exp) is
|
||||
/// identical. The jti claim guarantees this — without it, rotation would
|
||||
/// mint a byte-identical token and break reuse detection.
|
||||
#[test]
|
||||
fn refresh_tokens_are_unique_even_in_same_second() {
|
||||
let svc = JwtService::new(test_config());
|
||||
let claims = Claims::new(
|
||||
"user-3".to_string(),
|
||||
"u3@example.com".to_string(),
|
||||
0,
|
||||
Duration::minutes(15),
|
||||
);
|
||||
let (_, refresh_a) = svc.generate_tokens(claims.clone()).unwrap();
|
||||
let (_, refresh_b) = svc.generate_tokens(claims).unwrap();
|
||||
assert_ne!(
|
||||
refresh_a, refresh_b,
|
||||
"consecutive refresh tokens must differ (jti)"
|
||||
);
|
||||
|
||||
// And the jti values themselves differ.
|
||||
let jti_a = svc.validate_refresh_token(&refresh_a).unwrap().jti;
|
||||
let jti_b = svc.validate_refresh_token(&refresh_b).unwrap().jti;
|
||||
assert_ne!(jti_a, jti_b);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
#![allow(dead_code)]
|
||||
pub mod jwt;
|
||||
pub mod password;
|
||||
pub mod token_version_cache;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(dead_code)]
|
||||
#![allow(unused_imports)]
|
||||
use anyhow::Result;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
// Stub for future appointment operations
|
||||
|
|
@ -1 +0,0 @@
|
|||
// Stub for future family operations
|
||||
|
|
@ -1 +0,0 @@
|
|||
// Stub for future health_data operations
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
#![allow(dead_code)]
|
||||
use mongodb::{bson::doc, options::IndexOptions, Collection, IndexModel};
|
||||
|
||||
use anyhow::Result;
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
// Stub for future lab_result operations
|
||||
|
|
@ -1 +0,0 @@
|
|||
// Stub for future medication operations
|
||||
|
|
@ -1,19 +1,7 @@
|
|||
#![allow(dead_code)]
|
||||
#![allow(clippy::useless_conversion)]
|
||||
use anyhow::Result;
|
||||
use mongodb::{Client, Database};
|
||||
use std::env;
|
||||
|
||||
pub mod appointment;
|
||||
pub mod family;
|
||||
pub mod health_data;
|
||||
pub mod lab_result;
|
||||
pub mod medication;
|
||||
pub mod permission;
|
||||
pub mod profile;
|
||||
pub mod share;
|
||||
pub mod user;
|
||||
|
||||
pub mod init; // Database initialization module
|
||||
|
||||
mod mongodb_impl;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
#![allow(clippy::needless_question_mark)]
|
||||
use anyhow::Result;
|
||||
use mongodb::bson::oid::ObjectId;
|
||||
use mongodb::{bson::doc, options::ClientOptions, Client, Collection, Database};
|
||||
|
|
@ -286,10 +285,9 @@ impl MongoDb {
|
|||
pub async fn get_medication(&self, id: &str) -> Result<Option<Medication>> {
|
||||
let object_id = ObjectId::parse_str(id)?;
|
||||
let repo = MedicationRepository::new(self.medications.clone());
|
||||
Ok(repo
|
||||
.find_by_id(&object_id)
|
||||
repo.find_by_id(&object_id)
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("Failed to get medication: {}", e))?)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to get medication: {}", e))
|
||||
}
|
||||
|
||||
pub async fn list_medications(
|
||||
|
|
@ -318,19 +316,17 @@ impl MongoDb {
|
|||
) -> Result<Option<Medication>> {
|
||||
let object_id = ObjectId::parse_str(id)?;
|
||||
let repo = MedicationRepository::new(self.medications.clone());
|
||||
Ok(repo
|
||||
.update(&object_id, updates)
|
||||
repo.update(&object_id, updates)
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("Failed to update medication: {}", e))?)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to update medication: {}", e))
|
||||
}
|
||||
|
||||
pub async fn delete_medication(&self, id: &str) -> Result<bool> {
|
||||
let object_id = ObjectId::parse_str(id)?;
|
||||
let repo = MedicationRepository::new(self.medications.clone());
|
||||
Ok(repo
|
||||
.delete(&object_id)
|
||||
repo.delete(&object_id)
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("Failed to delete medication: {}", e))?)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to delete medication: {}", e))
|
||||
}
|
||||
|
||||
pub async fn log_medication_dose(&self, dose: &MedicationDose) -> Result<Option<ObjectId>> {
|
||||
|
|
@ -349,9 +345,8 @@ impl MongoDb {
|
|||
days: i64,
|
||||
) -> Result<crate::models::medication::AdherenceStats> {
|
||||
let repo = MedicationRepository::new(self.medications.clone());
|
||||
Ok(repo
|
||||
.calculate_adherence(medication_id, days)
|
||||
repo.calculate_adherence(medication_id, days)
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("Failed to calculate adherence: {}", e))?)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to calculate adherence: {}", e))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
// Permission-related database operations are in MongoDb struct
|
||||
// This file exists for module organization
|
||||
|
|
@ -1 +0,0 @@
|
|||
// Stub for future profile operations
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
// Share-related database operations are in MongoDb struct
|
||||
// This file exists for module organization
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
// User-related database operations are in MongoDb struct
|
||||
// This file exists for module organization
|
||||
|
|
@ -1,12 +1,9 @@
|
|||
#![allow(dead_code)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(clippy::redundant_pattern_matching)]
|
||||
use crate::config::AppState;
|
||||
use axum::{extract::State, response::Json};
|
||||
use serde_json::{json, Value};
|
||||
|
||||
pub async fn health_check(State(state): State<AppState>) -> Json<Value> {
|
||||
let status = if let Ok(_) = state.db.health_check().await {
|
||||
let status = if state.db.health_check().await.is_ok() {
|
||||
"connected"
|
||||
} else {
|
||||
"error"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
#![allow(dead_code)]
|
||||
use crate::auth::jwt::Claims;
|
||||
use crate::config::AppState;
|
||||
use crate::models::health_stats::HealthStatistic;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
#![allow(dead_code)]
|
||||
//! Drug Interaction Handlers (Phase 2.8)
|
||||
|
||||
use axum::{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,3 @@
|
|||
#![allow(dead_code)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(unused_variables)]
|
||||
use axum::{
|
||||
extract::{Extension, Json, Path, Query, State},
|
||||
http::StatusCode,
|
||||
|
|
@ -103,7 +100,7 @@ pub async fn list_medications(
|
|||
|
||||
pub async fn get_medication(
|
||||
State(state): State<AppState>,
|
||||
Extension(claims): Extension<Claims>,
|
||||
Extension(_claims): Extension<Claims>,
|
||||
Path(id): Path<String>,
|
||||
) -> Result<Json<Medication>, StatusCode> {
|
||||
let database = state.db.get_database();
|
||||
|
|
@ -121,7 +118,7 @@ pub async fn get_medication(
|
|||
|
||||
pub async fn update_medication(
|
||||
State(state): State<AppState>,
|
||||
Extension(claims): Extension<Claims>,
|
||||
Extension(_claims): Extension<Claims>,
|
||||
Path(id): Path<String>,
|
||||
Json(req): Json<UpdateMedicationRequest>,
|
||||
) -> Result<Json<Medication>, StatusCode> {
|
||||
|
|
@ -140,7 +137,7 @@ pub async fn update_medication(
|
|||
|
||||
pub async fn delete_medication(
|
||||
State(state): State<AppState>,
|
||||
Extension(claims): Extension<Claims>,
|
||||
Extension(_claims): Extension<Claims>,
|
||||
Path(id): Path<String>,
|
||||
) -> Result<StatusCode, StatusCode> {
|
||||
let database = state.db.get_database();
|
||||
|
|
@ -188,7 +185,7 @@ pub async fn log_dose(
|
|||
|
||||
pub async fn get_adherence(
|
||||
State(state): State<AppState>,
|
||||
Extension(claims): Extension<Claims>,
|
||||
Extension(_claims): Extension<Claims>,
|
||||
Path(id): Path<String>,
|
||||
) -> Result<Json<crate::models::medication::AdherenceStats>, StatusCode> {
|
||||
let database = state.db.get_database();
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
#![allow(dead_code)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(clippy::redundant_closure)]
|
||||
#![allow(clippy::useless_conversion)]
|
||||
#![allow(clippy::clone_on_copy)]
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
http::StatusCode,
|
||||
|
|
@ -171,7 +166,7 @@ pub async fn create_share(
|
|||
});
|
||||
|
||||
let share = Share::new(
|
||||
owner_id.clone(),
|
||||
owner_id,
|
||||
target_user_id,
|
||||
req.resource_type,
|
||||
resource_id,
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
fatal: path 'backend/src/main.rs' exists, but not 'src/main.rs'
|
||||
hint: Did you mean 'a316699:backend/src/main.rs' aka 'a316699:./src/main.rs'?
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(dead_code)]
|
||||
#![allow(unused_imports)]
|
||||
use crate::auth::jwt::Claims;
|
||||
use crate::config::AppState;
|
||||
use axum::{
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
//! 3. The peer address from Axum's `ConnectInfo<SocketAddr>` (direct connection).
|
||||
//! 4. `"0.0.0.0"` only if none of the above are present.
|
||||
|
||||
#![allow(dead_code)]
|
||||
use axum::{
|
||||
extract::ConnectInfo, extract::Request, http::HeaderMap, middleware::Next, response::Response,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(dead_code)]
|
||||
#![allow(unused_imports)]
|
||||
use axum::{extract::Request, http::StatusCode, middleware::Next, response::Response};
|
||||
|
||||
/// Middleware for general rate limiting
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
#![allow(dead_code)]
|
||||
use serde::{Deserialize, Serialize};
|
||||
use mongodb::bson::{oid::ObjectId, DateTime};
|
||||
use super::health_data::EncryptedField;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(dead_code)]
|
||||
#![allow(unused_imports)]
|
||||
use anyhow::Result;
|
||||
use futures::stream::TryStreamExt;
|
||||
use mongodb::{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(dead_code)]
|
||||
#![allow(unused_imports)]
|
||||
use mongodb::{
|
||||
bson::{doc, oid::ObjectId, DateTime},
|
||||
Collection,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,3 @@
|
|||
#![allow(dead_code)]
|
||||
#![allow(clippy::useless_conversion)]
|
||||
#![allow(unused_imports)]
|
||||
use mongodb::bson::{oid::ObjectId, DateTime};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
#![allow(dead_code)]
|
||||
use futures::stream::TryStreamExt;
|
||||
use mongodb::Collection;
|
||||
use mongodb::{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
#![allow(dead_code)]
|
||||
//! Interaction Models
|
||||
//!
|
||||
//! Database models for drug interactions
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(dead_code)]
|
||||
#![allow(unused_imports)]
|
||||
use futures::stream::TryStreamExt;
|
||||
use mongodb::{bson::oid::ObjectId, Collection};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(dead_code)]
|
||||
#![allow(unused_imports)]
|
||||
use super::health_data::EncryptedField;
|
||||
use futures::stream::StreamExt;
|
||||
use mongodb::bson::{doc, oid::ObjectId, DateTime};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
#![allow(dead_code)]
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(dead_code)]
|
||||
#![allow(unused_imports)]
|
||||
use mongodb::{
|
||||
bson::{doc, oid::ObjectId, DateTime},
|
||||
Collection,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
#![allow(dead_code)]
|
||||
use anyhow::Result;
|
||||
use base64::Engine;
|
||||
use mongodb::{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(dead_code)]
|
||||
#![allow(unused_imports)]
|
||||
use anyhow::Result;
|
||||
use futures::stream::TryStreamExt;
|
||||
use mongodb::{
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
#![allow(dead_code)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(clippy::redundant_closure)]
|
||||
#![allow(clippy::useless_conversion)]
|
||||
use mongodb::bson::DateTime;
|
||||
use mongodb::bson::{doc, oid::ObjectId};
|
||||
use mongodb::Collection;
|
||||
|
|
@ -91,7 +87,6 @@ impl ShareRepository {
|
|||
.await?
|
||||
.try_collect()
|
||||
.await
|
||||
.map_err(|e| mongodb::error::Error::from(e))
|
||||
}
|
||||
|
||||
pub async fn find_by_target(
|
||||
|
|
@ -108,7 +103,6 @@ impl ShareRepository {
|
|||
.await?
|
||||
.try_collect()
|
||||
.await
|
||||
.map_err(|e| mongodb::error::Error::from(e))
|
||||
}
|
||||
|
||||
pub async fn update(&self, share: &Share) -> mongodb::error::Result<()> {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(dead_code)]
|
||||
#![allow(unused_imports)]
|
||||
use mongodb::bson::{doc, oid::ObjectId};
|
||||
use mongodb::Collection;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(dead_code)]
|
||||
#![allow(unused_imports)]
|
||||
use crate::models::audit_log::{AuditEventType, AuditLog, AuditLogRepository};
|
||||
use anyhow::Result;
|
||||
use mongodb::bson::oid::ObjectId;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(dead_code)]
|
||||
#![allow(unused_imports)]
|
||||
use crate::models::session::{DeviceInfo, Session, SessionRepository};
|
||||
use anyhow::Result;
|
||||
use mongodb::bson::oid::ObjectId;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(dead_code)]
|
||||
#![allow(unused_imports)]
|
||||
//! Ingredient Mapper Service
|
||||
//!
|
||||
//! Maps EU drug names to US drug names for interaction checking
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
#![allow(dead_code)]
|
||||
//! Interaction Service
|
||||
//!
|
||||
//! Combines ingredient mapping and OpenFDA interaction checking
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
#![allow(dead_code)]
|
||||
#![allow(unused_imports)]
|
||||
use reqwest::Client;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
// NOTE: the real OpenFDA API query (reqwest -> api.fda.gov) was a dead-code
|
||||
// stub (`query_drug_events`, never called) and has been removed for now.
|
||||
// `check_interactions` currently uses a built-in table of known interactions.
|
||||
// When the live OpenFDA integration is wired up, re-add reqwest + the query
|
||||
// method here.
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
|
|
@ -21,17 +23,11 @@ pub struct DrugInteraction {
|
|||
pub description: String,
|
||||
}
|
||||
|
||||
pub struct OpenFDAService {
|
||||
client: Client,
|
||||
base_url: String,
|
||||
}
|
||||
pub struct OpenFDAService;
|
||||
|
||||
impl OpenFDAService {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
client: Client::new(),
|
||||
base_url: "https://api.fda.gov/drug/event.json".to_string(),
|
||||
}
|
||||
Self
|
||||
}
|
||||
|
||||
/// Check for interactions between multiple drugs
|
||||
|
|
@ -110,27 +106,6 @@ impl OpenFDAService {
|
|||
|
||||
None
|
||||
}
|
||||
|
||||
/// Query OpenFDA for drug event reports
|
||||
async fn query_drug_events(
|
||||
&self,
|
||||
drug_name: &str,
|
||||
) -> Result<serde_json::Value, Box<dyn std::error::Error>> {
|
||||
let query = format!(
|
||||
"{}?search=patient.drug.medicinalproduct:{}&limit=10",
|
||||
self.base_url, drug_name
|
||||
);
|
||||
|
||||
let response = self
|
||||
.client
|
||||
.get(&query)
|
||||
.send()
|
||||
.await?
|
||||
.json::<serde_json::Value>()
|
||||
.await?;
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for OpenFDAService {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue