fix(server+health): 修复路由 middleware 泄漏 — FHIR/Gateway 改用 .nest() 隔离
Axum 的 .merge() 会将子 Router 的 middleware 泄漏到整个路由树,
导致 FHIR OAuth middleware 和 Gateway auth middleware 拦截所有请求。
修复方式:
- fhir_routes 内部路径去掉 /fhir 前缀,main.rs 用 .nest("/fhir", ...) 注册
- gateway_routes 内部路径去掉 /health/gateway 前缀,main.rs 用 .nest("/health/gateway", ...) 注册
- 透析患者查询表名 patients → patient(与 Entity 一致)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -75,7 +75,7 @@ pub async fn create_dialysis_record(
|
|||||||
// 患者存在性校验
|
// 患者存在性校验
|
||||||
let patient_sql = sea_orm::Statement::from_sql_and_values(
|
let patient_sql = sea_orm::Statement::from_sql_and_values(
|
||||||
sea_orm::DatabaseBackend::Postgres,
|
sea_orm::DatabaseBackend::Postgres,
|
||||||
"SELECT EXISTS(SELECT 1 FROM patients WHERE id = $1 AND tenant_id = $2 AND deleted_at IS NULL)",
|
"SELECT EXISTS(SELECT 1 FROM patient WHERE id = $1 AND tenant_id = $2 AND deleted_at IS NULL)",
|
||||||
[req.patient_id.into(), tenant_id.into()],
|
[req.patient_id.into(), tenant_id.into()],
|
||||||
);
|
);
|
||||||
if let Ok(row) = state.db.query_one(patient_sql).await {
|
if let Ok(row) = state.db.query_one(patient_sql).await {
|
||||||
|
|||||||
@@ -151,32 +151,32 @@ impl HealthModule {
|
|||||||
use crate::fhir::handler as fhir;
|
use crate::fhir::handler as fhir;
|
||||||
|
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/fhir/R4/metadata", axum::routing::get(fhir::capability_statement))
|
.route("/R4/metadata", axum::routing::get(fhir::capability_statement))
|
||||||
// Patient
|
// Patient
|
||||||
.route("/fhir/R4/Patient", axum::routing::get(fhir::search_patients))
|
.route("/R4/Patient", axum::routing::get(fhir::search_patients))
|
||||||
.route("/fhir/R4/Patient/{id}", axum::routing::get(fhir::get_patient))
|
.route("/R4/Patient/{id}", axum::routing::get(fhir::get_patient))
|
||||||
// Observation
|
// Observation
|
||||||
.route("/fhir/R4/Observation", axum::routing::get(fhir::search_observations))
|
.route("/R4/Observation", axum::routing::get(fhir::search_observations))
|
||||||
// Device
|
// Device
|
||||||
.route("/fhir/R4/Device", axum::routing::get(fhir::search_devices))
|
.route("/R4/Device", axum::routing::get(fhir::search_devices))
|
||||||
.route("/fhir/R4/Device/{id}", axum::routing::get(fhir::get_device))
|
.route("/R4/Device/{id}", axum::routing::get(fhir::get_device))
|
||||||
// Practitioner
|
// Practitioner
|
||||||
.route("/fhir/R4/Practitioner", axum::routing::get(fhir::search_practitioners))
|
.route("/R4/Practitioner", axum::routing::get(fhir::search_practitioners))
|
||||||
.route("/fhir/R4/Practitioner/{id}", axum::routing::get(fhir::get_practitioner))
|
.route("/R4/Practitioner/{id}", axum::routing::get(fhir::get_practitioner))
|
||||||
// Appointment
|
// Appointment
|
||||||
.route("/fhir/R4/Appointment", axum::routing::get(fhir::search_appointments))
|
.route("/R4/Appointment", axum::routing::get(fhir::search_appointments))
|
||||||
.route("/fhir/R4/Appointment/{id}", axum::routing::get(fhir::get_appointment))
|
.route("/R4/Appointment/{id}", axum::routing::get(fhir::get_appointment))
|
||||||
// DiagnosticReport
|
// DiagnosticReport
|
||||||
.route("/fhir/R4/DiagnosticReport", axum::routing::get(fhir::search_diagnostic_reports))
|
.route("/R4/DiagnosticReport", axum::routing::get(fhir::search_diagnostic_reports))
|
||||||
.route("/fhir/R4/DiagnosticReport/{id}", axum::routing::get(fhir::get_diagnostic_report))
|
.route("/R4/DiagnosticReport/{id}", axum::routing::get(fhir::get_diagnostic_report))
|
||||||
// Encounter
|
// Encounter
|
||||||
.route("/fhir/R4/Encounter", axum::routing::get(fhir::search_encounters))
|
.route("/R4/Encounter", axum::routing::get(fhir::search_encounters))
|
||||||
.route("/fhir/R4/Encounter/{id}", axum::routing::get(fhir::get_encounter))
|
.route("/R4/Encounter/{id}", axum::routing::get(fhir::get_encounter))
|
||||||
// Task
|
// Task
|
||||||
.route("/fhir/R4/Task", axum::routing::get(fhir::search_tasks))
|
.route("/R4/Task", axum::routing::get(fhir::search_tasks))
|
||||||
.route("/fhir/R4/Task/{id}", axum::routing::get(fhir::get_task))
|
.route("/R4/Task/{id}", axum::routing::get(fhir::get_task))
|
||||||
// $everything
|
// $everything
|
||||||
.route("/fhir/R4/Patient/{id}/$everything", axum::routing::get(fhir::patient_everything))
|
.route("/R4/Patient/{id}/$everything", axum::routing::get(fhir::patient_everything))
|
||||||
// metadata 端点不需要认证,其他端点需要 OAuth Bearer token
|
// metadata 端点不需要认证,其他端点需要 OAuth Bearer token
|
||||||
.layer(axum::middleware::from_fn(
|
.layer(axum::middleware::from_fn(
|
||||||
crate::oauth::middleware::oauth_auth_middleware,
|
crate::oauth::middleware::oauth_auth_middleware,
|
||||||
@@ -945,8 +945,8 @@ impl HealthModule {
|
|||||||
S: Clone + Send + Sync + 'static,
|
S: Clone + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/health/gateway/upload", axum::routing::post(ble_gateway_handler::gateway_upload))
|
.route("/upload", axum::routing::post(ble_gateway_handler::gateway_upload))
|
||||||
.route("/health/gateway/heartbeat", axum::routing::post(ble_gateway_handler::gateway_heartbeat))
|
.route("/heartbeat", axum::routing::post(ble_gateway_handler::gateway_heartbeat))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -616,8 +616,9 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
}));
|
}));
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.nest("/api/v1", unthrottled_routes.merge(public_routes).merge(protected_routes))
|
.nest("/api/v1", unthrottled_routes.merge(public_routes).merge(protected_routes))
|
||||||
.merge(erp_health::HealthModule::fhir_routes().with_state(state.clone()))
|
.nest("/fhir", erp_health::HealthModule::fhir_routes().with_state(state.clone()))
|
||||||
.merge(
|
.nest(
|
||||||
|
"/health/gateway",
|
||||||
erp_health::HealthModule::gateway_routes()
|
erp_health::HealthModule::gateway_routes()
|
||||||
.layer(axum::middleware::from_fn_with_state(
|
.layer(axum::middleware::from_fn_with_state(
|
||||||
state.clone(),
|
state.clone(),
|
||||||
|
|||||||
Reference in New Issue
Block a user