normogen/backend/src/handlers/interactions.rs
goose cf6fcd4ef2 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).
2026-06-27 20:57:24 -03:00

97 lines
2.7 KiB
Rust

//! Drug Interaction Handlers (Phase 2.8)
use axum::{
extract::{Extension, State},
http::StatusCode,
Json,
};
use serde::{Deserialize, Serialize};
use crate::{
auth::jwt::Claims,
config::AppState,
services::openfda_service::{DrugInteraction, InteractionSeverity},
};
#[derive(Debug, Deserialize)]
pub struct CheckInteractionRequest {
pub medications: Vec<String>,
}
#[derive(Debug, Serialize)]
pub struct InteractionResponse {
pub interactions: Vec<DrugInteraction>,
pub has_severe: bool,
pub disclaimer: String,
}
/// Check interactions between medications
pub async fn check_interactions(
_claims: Extension<Claims>,
State(state): State<AppState>,
Json(request): Json<CheckInteractionRequest>,
) -> Result<Json<InteractionResponse>, StatusCode> {
if request.medications.len() < 2 {
return Err(StatusCode::BAD_REQUEST);
}
let interaction_service = state
.interaction_service
.as_ref()
.ok_or(StatusCode::SERVICE_UNAVAILABLE)?;
match interaction_service
.check_eu_medications(&request.medications)
.await
{
Ok(interactions) => {
let has_severe = interactions
.iter()
.any(|i| matches!(i.severity, InteractionSeverity::Severe));
Ok(Json(InteractionResponse {
interactions,
has_severe,
disclaimer: "This information is advisory only. Consult with a physician for detailed information about drug interactions.".to_string(),
}))
}
Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR),
}
}
#[derive(Debug, Deserialize)]
pub struct CheckNewMedicationRequest {
pub new_medication: String,
pub existing_medications: Vec<String>,
}
#[derive(Debug, Serialize)]
pub struct NewMedicationCheckResult {
pub interactions: Vec<DrugInteraction>,
pub has_severe: bool,
pub disclaimer: String,
}
/// Check if a new medication has interactions with existing medications
pub async fn check_new_medication(
_claims: Extension<Claims>,
State(state): State<AppState>,
Json(request): Json<CheckNewMedicationRequest>,
) -> Result<Json<InteractionResponse>, StatusCode> {
let interaction_service = state
.interaction_service
.as_ref()
.ok_or(StatusCode::SERVICE_UNAVAILABLE)?;
match interaction_service
.check_new_medication(&request.new_medication, &request.existing_medications)
.await
{
Ok(result) => Ok(Json(InteractionResponse {
interactions: result.interactions,
has_severe: result.has_severe,
disclaimer: result.disclaimer,
})),
Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR),
}
}