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(
|
||||
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()],
|
||||
);
|
||||
if let Ok(row) = state.db.query_one(patient_sql).await {
|
||||
|
||||
@@ -151,32 +151,32 @@ impl HealthModule {
|
||||
use crate::fhir::handler as fhir;
|
||||
|
||||
Router::new()
|
||||
.route("/fhir/R4/metadata", axum::routing::get(fhir::capability_statement))
|
||||
.route("/R4/metadata", axum::routing::get(fhir::capability_statement))
|
||||
// Patient
|
||||
.route("/fhir/R4/Patient", axum::routing::get(fhir::search_patients))
|
||||
.route("/fhir/R4/Patient/{id}", axum::routing::get(fhir::get_patient))
|
||||
.route("/R4/Patient", axum::routing::get(fhir::search_patients))
|
||||
.route("/R4/Patient/{id}", axum::routing::get(fhir::get_patient))
|
||||
// Observation
|
||||
.route("/fhir/R4/Observation", axum::routing::get(fhir::search_observations))
|
||||
.route("/R4/Observation", axum::routing::get(fhir::search_observations))
|
||||
// Device
|
||||
.route("/fhir/R4/Device", axum::routing::get(fhir::search_devices))
|
||||
.route("/fhir/R4/Device/{id}", axum::routing::get(fhir::get_device))
|
||||
.route("/R4/Device", axum::routing::get(fhir::search_devices))
|
||||
.route("/R4/Device/{id}", axum::routing::get(fhir::get_device))
|
||||
// Practitioner
|
||||
.route("/fhir/R4/Practitioner", axum::routing::get(fhir::search_practitioners))
|
||||
.route("/fhir/R4/Practitioner/{id}", axum::routing::get(fhir::get_practitioner))
|
||||
.route("/R4/Practitioner", axum::routing::get(fhir::search_practitioners))
|
||||
.route("/R4/Practitioner/{id}", axum::routing::get(fhir::get_practitioner))
|
||||
// Appointment
|
||||
.route("/fhir/R4/Appointment", axum::routing::get(fhir::search_appointments))
|
||||
.route("/fhir/R4/Appointment/{id}", axum::routing::get(fhir::get_appointment))
|
||||
.route("/R4/Appointment", axum::routing::get(fhir::search_appointments))
|
||||
.route("/R4/Appointment/{id}", axum::routing::get(fhir::get_appointment))
|
||||
// DiagnosticReport
|
||||
.route("/fhir/R4/DiagnosticReport", axum::routing::get(fhir::search_diagnostic_reports))
|
||||
.route("/fhir/R4/DiagnosticReport/{id}", axum::routing::get(fhir::get_diagnostic_report))
|
||||
.route("/R4/DiagnosticReport", axum::routing::get(fhir::search_diagnostic_reports))
|
||||
.route("/R4/DiagnosticReport/{id}", axum::routing::get(fhir::get_diagnostic_report))
|
||||
// Encounter
|
||||
.route("/fhir/R4/Encounter", axum::routing::get(fhir::search_encounters))
|
||||
.route("/fhir/R4/Encounter/{id}", axum::routing::get(fhir::get_encounter))
|
||||
.route("/R4/Encounter", axum::routing::get(fhir::search_encounters))
|
||||
.route("/R4/Encounter/{id}", axum::routing::get(fhir::get_encounter))
|
||||
// Task
|
||||
.route("/fhir/R4/Task", axum::routing::get(fhir::search_tasks))
|
||||
.route("/fhir/R4/Task/{id}", axum::routing::get(fhir::get_task))
|
||||
.route("/R4/Task", axum::routing::get(fhir::search_tasks))
|
||||
.route("/R4/Task/{id}", axum::routing::get(fhir::get_task))
|
||||
// $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
|
||||
.layer(axum::middleware::from_fn(
|
||||
crate::oauth::middleware::oauth_auth_middleware,
|
||||
@@ -945,8 +945,8 @@ impl HealthModule {
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
Router::new()
|
||||
.route("/health/gateway/upload", axum::routing::post(ble_gateway_handler::gateway_upload))
|
||||
.route("/health/gateway/heartbeat", axum::routing::post(ble_gateway_handler::gateway_heartbeat))
|
||||
.route("/upload", axum::routing::post(ble_gateway_handler::gateway_upload))
|
||||
.route("/heartbeat", axum::routing::post(ble_gateway_handler::gateway_heartbeat))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -616,8 +616,9 @@ async fn main() -> anyhow::Result<()> {
|
||||
}));
|
||||
let app = Router::new()
|
||||
.nest("/api/v1", unthrottled_routes.merge(public_routes).merge(protected_routes))
|
||||
.merge(erp_health::HealthModule::fhir_routes().with_state(state.clone()))
|
||||
.merge(
|
||||
.nest("/fhir", erp_health::HealthModule::fhir_routes().with_state(state.clone()))
|
||||
.nest(
|
||||
"/health/gateway",
|
||||
erp_health::HealthModule::gateway_routes()
|
||||
.layer(axum::middleware::from_fn_with_state(
|
||||
state.clone(),
|
||||
|
||||
Reference in New Issue
Block a user