mod.rs
| 1 | use std::sync::Arc; |
| 2 | |
| 3 | use axum::http::HeaderValue; |
| 4 | use axum::routing::{delete, get, post, put}; |
| 5 | use axum::Router; |
| 6 | use tower_http::set_header::SetResponseHeaderLayer; |
| 7 | |
| 8 | use crate::error::AppState; |
| 9 | |
| 10 | /// Content-Security-Policy tuned to the built Trunk frontend. |
| 11 | /// |
| 12 | /// * `script-src 'unsafe-inline'` — Trunk emits one inline bootstrap module |
| 13 | /// in index.html; every real JS file also carries an SRI integrity hash. |
| 14 | /// * `'wasm-unsafe-eval'` — compiling the same-origin WASM module. |
| 15 | /// * `style-src 'unsafe-inline'` — the context menu sets an inline `style=`. |
| 16 | /// * Everything else locked to the same origin; frames/plugins banned. |
| 17 | const CSP: &str = "default-src 'self'; script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; media-src 'self' blob:; connect-src 'self'; font-src 'self' data:; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none';"; |
| 18 | |
| 19 | mod admin; |
| 20 | mod auth; |
| 21 | mod common; |
| 22 | mod files; |
| 23 | mod shares; |
| 24 | mod spa; |
| 25 | |
| 26 | pub fn router(state: Arc<AppState>) -> Router { |
| 27 | Router::new() |
| 28 | .route("/api/auth/login", post(auth::login)) |
| 29 | .route("/api/auth/logout", post(auth::logout)) |
| 30 | .route("/api/auth/me", get(auth::me)) |
| 31 | .route("/api/auth/setup", post(auth::setup)) |
| 32 | .route("/api/files/{root_id}", get(files::list_root)) |
| 33 | .route("/api/files/{root_id}/{*path}", get(files::file_get)) |
| 34 | .route("/api/files/{root_id}/{*path}", put(files::file_put)) |
| 35 | .route("/api/files/{root_id}", post(files::dispatch_root)) |
| 36 | .route("/api/files/{root_id}/{*path}", post(files::dispatch)) |
| 37 | .route("/api/files/{root_id}/{*path}", delete(files::delete)) |
| 38 | .route("/api/shares", get(shares::list)) |
| 39 | .route("/api/shares", post(shares::create)) |
| 40 | .route("/api/shares/{id}", delete(shares::delete)) |
| 41 | .route("/api/share/{token}", get(shares::resolve)) |
| 42 | .route("/api/admin/users", get(admin::list_users)) |
| 43 | .route("/api/admin/users", post(admin::create_user)) |
| 44 | .route("/api/admin/users/{id}", put(admin::update_user)) |
| 45 | .route("/api/admin/users/{id}", delete(admin::delete_user)) |
| 46 | .route("/api/admin/settings", get(admin::get_settings)) |
| 47 | .route("/api/admin/settings", put(admin::update_settings)) |
| 48 | .fallback(spa::fallback) |
| 49 | .with_state(state) |
| 50 | // Hard security headers on every response (API and static alike). |
| 51 | .layer(SetResponseHeaderLayer::overriding( |
| 52 | "content-security-policy".parse().unwrap(), |
| 53 | HeaderValue::from_static(CSP), |
| 54 | )) |
| 55 | .layer(SetResponseHeaderLayer::overriding( |
| 56 | "x-content-type-options".parse().unwrap(), |
| 57 | HeaderValue::from_static("nosniff"), |
| 58 | )) |
| 59 | .layer(SetResponseHeaderLayer::overriding( |
| 60 | "x-frame-options".parse().unwrap(), |
| 61 | HeaderValue::from_static("DENY"), |
| 62 | )) |
| 63 | .layer(SetResponseHeaderLayer::overriding( |
| 64 | "referrer-policy".parse().unwrap(), |
| 65 | HeaderValue::from_static("no-referrer"), |
| 66 | )) |
| 67 | } |
| 68 |