refactor: drop /web/ URL prefix from browser routes
The /web/ prefix predated the /api/ split; today it just clutters every URL
without disambiguating anything. All 16 browser routes move to the apex
(/cases, /login, /magic, /events, /audio/...). The 6 /api/* routes are
unchanged. A new /->>/cases redirect closes the apex 404.
The open-redirect guard in magic.rs and case_actions.rs flips from a
positive whitelist (starts_with("/web/")) to a deny-list: same-origin path,
not protocol-relative, not under /api/, no \. The /api/ exclusion is now
load-bearing and covered by tests.
Pre-production: no transition redirects.
This commit is contained in:
+18
-19
@@ -12,12 +12,14 @@ pub(crate) mod user_web;
|
||||
pub(crate) mod web;
|
||||
|
||||
use axum::Router;
|
||||
use axum::response::Redirect;
|
||||
use axum::routing::{get, post, put};
|
||||
|
||||
use crate::AppState;
|
||||
|
||||
pub fn api_router() -> Router<AppState> {
|
||||
Router::new()
|
||||
.route("/", get(|| async { Redirect::permanent("/cases") }))
|
||||
.route("/api/health", get(health::handle_health))
|
||||
.route("/api/debug/whoami", get(debug::handle_whoami))
|
||||
.route("/api/upload", post(upload::handle_upload))
|
||||
@@ -27,50 +29,47 @@ pub fn api_router() -> Router<AppState> {
|
||||
put(oneliner_override::handle_put_oneliner),
|
||||
)
|
||||
.route("/api/auth/magic-link", post(magic::handle_create))
|
||||
.route("/web/magic", get(magic::handle_consume))
|
||||
.route("/magic", get(magic::handle_consume))
|
||||
.route(
|
||||
"/web/login",
|
||||
"/login",
|
||||
get(login::handle_login_page).post(login::handle_login_submit),
|
||||
)
|
||||
.route("/web/logout", post(login::handle_logout))
|
||||
.route("/web/cases", get(user_web::handle_my_cases))
|
||||
.route("/web/cases/{case_id}", get(user_web::handle_case_page))
|
||||
.route("/logout", post(login::handle_logout))
|
||||
.route("/cases", get(user_web::handle_my_cases))
|
||||
.route("/cases/{case_id}", get(user_web::handle_case_page))
|
||||
.route(
|
||||
"/web/cases/{case_id}/recordings",
|
||||
"/cases/{case_id}/recordings",
|
||||
get(user_web::handle_case_recordings),
|
||||
)
|
||||
.route(
|
||||
"/web/cases/{case_id}/recordings/delete",
|
||||
"/cases/{case_id}/recordings/delete",
|
||||
post(case_actions::handle_delete_recording),
|
||||
)
|
||||
.route(
|
||||
"/web/cases/{case_id}/analyze",
|
||||
"/cases/{case_id}/analyze",
|
||||
post(case_actions::handle_analyze_case),
|
||||
)
|
||||
.route(
|
||||
"/web/cases/{case_id}/close",
|
||||
"/cases/{case_id}/close",
|
||||
post(case_actions::handle_close_case),
|
||||
)
|
||||
.route(
|
||||
"/web/cases/{case_id}/reopen",
|
||||
"/cases/{case_id}/reopen",
|
||||
post(case_actions::handle_reopen_case),
|
||||
)
|
||||
.route(
|
||||
"/web/cases/{case_id}/reset",
|
||||
"/cases/{case_id}/reset",
|
||||
post(case_actions::handle_reset_case),
|
||||
)
|
||||
.route(
|
||||
"/web/cases/{case_id}/oneliner",
|
||||
"/cases/{case_id}/oneliner",
|
||||
post(oneliner_override::handle_web_put_oneliner),
|
||||
)
|
||||
.route(
|
||||
"/web/cases/purge-closed",
|
||||
"/cases/purge-closed",
|
||||
post(case_actions::handle_purge_closed),
|
||||
)
|
||||
.route("/web/cases/bulk", post(bulk::handle_bulk_action))
|
||||
.route(
|
||||
"/web/audio/{user}/{case_id}/{filename}",
|
||||
get(web::handle_audio),
|
||||
)
|
||||
.route("/web/events", get(events::handle_events))
|
||||
.route("/cases/bulk", post(bulk::handle_bulk_action))
|
||||
.route("/audio/{user}/{case_id}/{filename}", get(web::handle_audio))
|
||||
.route("/events", get(events::handle_events))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user