diff options
| author | Owen Jacobson <owen@grimoire.ca> | 2025-06-17 02:03:28 -0400 |
|---|---|---|
| committer | Owen Jacobson <owen@grimoire.ca> | 2025-06-18 18:31:40 -0400 |
| commit | 5ed96f8e8b9d9f19ee249f5c73a5a21ef6bca09f (patch) | |
| tree | 75aa0287535506aa88f5f6d5b70ad5f99373320e | |
| parent | 23c2f6fbc07f25a11826892d783bddcc93550d25 (diff) | |
Reorganize and consolidate HTTP routes.
HTTP routes are now defined in a single, unified module, pulling them out of the topical modules they were formerly part of.
This is intended to improve the navigability of the codebase. Previously, finding the handler corresponding to a specific endpoint required prior familiarity, though in practice you could usually guess from topic area. Now, all routes are defined in `crate::routes`.
Other than changing visibility, I've avoided making changes to the handlers at the ends of those routes.
| -rw-r--r-- | src/boot/mod.rs | 10 | ||||
| -rw-r--r-- | src/boot/routes/mod.rs | 10 | ||||
| -rw-r--r-- | src/channel/mod.rs | 4 | ||||
| -rw-r--r-- | src/channel/routes/mod.rs | 18 | ||||
| -rw-r--r-- | src/cli.rs | 39 | ||||
| -rw-r--r-- | src/event/mod.rs | 10 | ||||
| -rw-r--r-- | src/event/routes/mod.rs | 10 | ||||
| -rw-r--r-- | src/invite/mod.rs | 8 | ||||
| -rw-r--r-- | src/invite/routes/mod.rs | 18 | ||||
| -rw-r--r-- | src/lib.rs | 1 | ||||
| -rw-r--r-- | src/message/mod.rs | 6 | ||||
| -rw-r--r-- | src/message/routes/mod.rs | 10 | ||||
| -rw-r--r-- | src/routes.rs | 83 | ||||
| -rw-r--r-- | src/setup/mod.rs | 4 | ||||
| -rw-r--r-- | src/setup/routes/mod.rs | 10 | ||||
| -rw-r--r-- | src/ui/mod.rs | 4 | ||||
| -rw-r--r-- | src/ui/routes/mod.rs | 35 | ||||
| -rw-r--r-- | src/user/mod.rs | 6 | ||||
| -rw-r--r-- | src/user/routes/mod.rs | 17 |
19 files changed, 127 insertions, 176 deletions
diff --git a/src/boot/mod.rs b/src/boot/mod.rs index 122bd53..2b74de1 100644 --- a/src/boot/mod.rs +++ b/src/boot/mod.rs @@ -1,11 +1,11 @@ -use crate::{channel::Channel, event::Sequence, message::Message, user::User}; -use serde::Serialize; use std::time::Duration; -pub mod app; -mod routes; +use serde::Serialize; -pub use self::routes::router; +use crate::{channel::Channel, event::Sequence, message::Message, user::User}; + +pub mod app; +pub mod routes; #[derive(serde::Serialize)] pub struct Snapshot { diff --git a/src/boot/routes/mod.rs b/src/boot/routes/mod.rs index 8fd99d3..60ad5d8 100644 --- a/src/boot/routes/mod.rs +++ b/src/boot/routes/mod.rs @@ -1,11 +1,3 @@ -use axum::{Router, routing::get}; - -use crate::app::App; - -mod get; +pub mod get; #[cfg(test)] mod test; - -pub fn router() -> Router<App> { - Router::new().route("/api/boot", get(get::handler)) -} diff --git a/src/channel/mod.rs b/src/channel/mod.rs index d5ba828..feb00a9 100644 --- a/src/channel/mod.rs +++ b/src/channel/mod.rs @@ -3,8 +3,8 @@ pub mod event; mod history; mod id; pub mod repo; -mod routes; +pub mod routes; mod snapshot; mod validate; -pub use self::{event::Event, history::History, id::Id, routes::router, snapshot::Channel}; +pub use self::{event::Event, history::History, id::Id, snapshot::Channel}; diff --git a/src/channel/routes/mod.rs b/src/channel/routes/mod.rs index c917348..bd90721 100644 --- a/src/channel/routes/mod.rs +++ b/src/channel/routes/mod.rs @@ -1,19 +1,5 @@ -use axum::{ - Router, - routing::{delete, post}, -}; - -use crate::app::App; - -mod channel; -mod post; +pub mod channel; +pub mod post; #[cfg(test)] mod test; - -pub fn router() -> Router<App> { - Router::new() - .route("/api/channels", post(post::handler)) - .route("/api/channels/{channel}", post(channel::post::handler)) - .route("/api/channels/{channel}", delete(channel::delete::handler)) -} @@ -6,7 +6,6 @@ use std::{future, io}; use axum::{ - Router, http::header, middleware, response::{IntoResponse, Response}, @@ -15,7 +14,7 @@ use clap::{CommandFactory, Parser}; use sqlx::sqlite::SqlitePool; use tokio::net; -use crate::{app::App, boot, channel, clock, db, event, expire, invite, message, setup, ui, user}; +use crate::{app::App, clock, db, routes}; /// Command-line entry point for running the `pilcrow` server. /// @@ -82,7 +81,7 @@ impl Args { let pool = self.pool().await?; let app = App::from(pool); - let app = routers(&app) + let app = routes::routes(&app) .route_layer(middleware::from_fn(clock::middleware)) .route_layer(middleware::map_response(Self::server_info())) .with_state(app); @@ -123,40 +122,6 @@ impl Args { } } -fn routers(app: &App) -> Router<App> { - [ - [ - // API endpoints that require setup to function - boot::router(), - channel::router(), - event::router(), - invite::router(), - user::router(), - message::router(), - ] - .into_iter() - .fold(Router::default(), Router::merge) - // Run expiry whenever someone accesses the API. This was previously a blanket middleware - // affecting the whole service, but loading the client makes a several requests before the - // client can completely load, each of which was triggering expiry. There is absolutely no - // upside to re-checking expiry tens of times back-to-back like that; the API is accessed - // more regularly and with less of a traffic rush. - // - // This should, probably, be moved to a background job at some point. - .route_layer(middleware::from_fn_with_state( - app.clone(), - expire::middleware, - )) - .route_layer(setup::Required(app.clone())), - // API endpoints that handle setup - setup::router(), - // The UI (handles setup state itself) - ui::router(app), - ] - .into_iter() - .fold(Router::default(), Router::merge) -} - fn started_msg(listener: &net::TcpListener) -> io::Result<String> { let local_addr = listener.local_addr()?; Ok(format!("listening on http://{local_addr}/")) diff --git a/src/event/mod.rs b/src/event/mod.rs index 1f2ec42..ff30dc7 100644 --- a/src/event/mod.rs +++ b/src/event/mod.rs @@ -1,18 +1,18 @@ -use crate::{channel, message, user}; -use axum::response::sse; -use axum::response::sse::KeepAlive; use std::time::Duration; +use axum::response::sse::{self, KeepAlive}; + +use crate::{channel, message, user}; + pub mod app; mod broadcaster; mod extract; pub mod repo; -mod routes; +pub mod routes; mod sequence; pub use self::{ broadcaster::Broadcaster, - routes::router, sequence::{Instant, Sequence, Sequenced}, }; diff --git a/src/event/routes/mod.rs b/src/event/routes/mod.rs index 742d397..60ad5d8 100644 --- a/src/event/routes/mod.rs +++ b/src/event/routes/mod.rs @@ -1,11 +1,3 @@ -use axum::{Router, routing::get}; - -use crate::app::App; - -mod get; +pub mod get; #[cfg(test)] mod test; - -pub fn router() -> Router<App> { - Router::new().route("/api/events", get(get::handler)) -} diff --git a/src/invite/mod.rs b/src/invite/mod.rs index 2d32fda..3932eea 100644 --- a/src/invite/mod.rs +++ b/src/invite/mod.rs @@ -1,11 +1,11 @@ +use crate::{clock::DateTime, normalize::nfc, user}; + pub mod app; mod id; mod repo; -mod routes; - -use crate::{clock::DateTime, normalize::nfc, user}; +pub mod routes; -pub use self::{id::Id, routes::router}; +pub use self::id::Id; #[derive(Debug, serde::Serialize)] pub struct Invite { diff --git a/src/invite/routes/mod.rs b/src/invite/routes/mod.rs index d83efc6..8747a4e 100644 --- a/src/invite/routes/mod.rs +++ b/src/invite/routes/mod.rs @@ -1,18 +1,4 @@ -use axum::{ - Router, - routing::{get, post}, -}; - -use crate::app::App; - -mod invite; -mod post; +pub mod invite; +pub mod post; #[cfg(test)] mod test; - -pub fn router() -> Router<App> { - Router::new() - .route("/api/invite", post(post::handler)) - .route("/api/invite/{invite}", get(invite::get::handler)) - .route("/api/invite/{invite}", post(invite::post::handler)) -} @@ -17,6 +17,7 @@ mod invite; mod message; mod name; mod normalize; +mod routes; mod setup; #[cfg(test)] mod test; diff --git a/src/message/mod.rs b/src/message/mod.rs index c2687bc..fbaa4a3 100644 --- a/src/message/mod.rs +++ b/src/message/mod.rs @@ -4,9 +4,7 @@ pub mod event; mod history; mod id; pub mod repo; -mod routes; +pub mod routes; mod snapshot; -pub use self::{ - body::Body, event::Event, history::History, id::Id, routes::router, snapshot::Message, -}; +pub use self::{body::Body, event::Event, history::History, id::Id, snapshot::Message}; diff --git a/src/message/routes/mod.rs b/src/message/routes/mod.rs index 00b2b1a..e216a50 100644 --- a/src/message/routes/mod.rs +++ b/src/message/routes/mod.rs @@ -1,9 +1 @@ -use axum::{Router, routing::delete}; - -use crate::app::App; - -mod message; - -pub fn router() -> Router<App> { - Router::new().route("/api/messages/{message}", delete(message::delete::handler)) -} +pub mod message; diff --git a/src/routes.rs b/src/routes.rs new file mode 100644 index 0000000..5bb5f91 --- /dev/null +++ b/src/routes.rs @@ -0,0 +1,83 @@ +use axum::{ + Router, middleware, + response::Redirect, + routing::{delete, get, post}, +}; + +use crate::{app::App, boot, channel, event, expire, invite, message, setup, ui, user}; + +pub fn routes(app: &App) -> Router<App> { + // UI routes that can be accessed before the administrator completes setup. + let ui_bootstrap = Router::new() + .route("/{*path}", get(ui::routes::path::get::handler)) + .route("/setup", get(ui::routes::setup::get::handler)); + + // UI routes that require the administrator to complete setup first. + let ui_setup_required = Router::new() + .route("/", get(ui::routes::get::handler)) + .route("/ch/{channel}", get(ui::routes::ch::channel::get::handler)) + .route( + "/invite/{invite}", + get(ui::routes::invite::invite::get::handler), + ) + .route("/login", get(ui::routes::login::get::handler)) + .route("/me", get(ui::routes::me::get::handler)) + .route_layer(crate::setup::Required(app.clone()).with_fallback(Redirect::to("/setup"))); + + // API routes that can run before the administrator completes setup. + let api_bootstrap = Router::new().route("/api/setup", post(setup::routes::post::handler)); + + // API routes that require the administrator to complete setup first. + let api_setup_required = Router::new() + .route("/api/auth/login", post(user::routes::login::post::handler)) + .route( + "/api/auth/logout", + post(user::routes::logout::post::handler), + ) + .route("/api/boot", get(boot::routes::get::handler)) + .route("/api/channels", post(channel::routes::post::handler)) + .route( + "/api/channels/{channel}", + post(channel::routes::channel::post::handler), + ) + .route( + "/api/channels/{channel}", + delete(channel::routes::channel::delete::handler), + ) + .route("/api/events", get(event::routes::get::handler)) + .route("/api/invite", post(invite::routes::post::handler)) + .route( + "/api/invite/{invite}", + get(invite::routes::invite::get::handler), + ) + .route( + "/api/invite/{invite}", + post(invite::routes::invite::post::handler), + ) + .route( + "/api/messages/{message}", + delete(message::routes::message::delete::handler), + ) + .route("/api/password", post(user::routes::password::post::handler)) + // Run expiry whenever someone accesses the API. This was previously a blanket middleware + // affecting the whole service, but loading the client makes a several requests before the + // client can completely load, each of which was triggering expiry. There is absolutely no + // upside to re-checking expiry tens of times back-to-back like that; the API is accessed + // more regularly and with less of a traffic rush. + // + // This should, probably, be moved to a background job at some point. + .route_layer(middleware::from_fn_with_state( + app.clone(), + expire::middleware, + )) + .route_layer(setup::Required(app.clone())); + + [ + ui_bootstrap, + ui_setup_required, + api_bootstrap, + api_setup_required, + ] + .into_iter() + .fold(Router::default(), Router::merge) +} diff --git a/src/setup/mod.rs b/src/setup/mod.rs index a4b821c..e741a60 100644 --- a/src/setup/mod.rs +++ b/src/setup/mod.rs @@ -1,6 +1,6 @@ pub mod app; pub mod repo; mod required; -mod routes; +pub mod routes; -pub use self::{required::Required, routes::router}; +pub use self::required::Required; diff --git a/src/setup/routes/mod.rs b/src/setup/routes/mod.rs index 977a790..e94a249 100644 --- a/src/setup/routes/mod.rs +++ b/src/setup/routes/mod.rs @@ -1,11 +1,3 @@ -use axum::{Router, routing::post}; - -use crate::app::App; - -mod post; +pub mod post; #[cfg(test)] mod test; - -pub fn router() -> Router<App> { - Router::new().route("/api/setup", post(post::handler)) -} diff --git a/src/ui/mod.rs b/src/ui/mod.rs index e834bba..eeaf27a 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -1,6 +1,4 @@ mod assets; mod error; mod mime; -mod routes; - -pub use self::routes::router; +pub mod routes; diff --git a/src/ui/routes/mod.rs b/src/ui/routes/mod.rs index dc94773..2390802 100644 --- a/src/ui/routes/mod.rs +++ b/src/ui/routes/mod.rs @@ -1,28 +1,7 @@ -use axum::{Router, response::Redirect, routing::get}; - -use crate::app::App; - -mod ch; -mod get; -mod invite; -mod login; -mod me; -mod path; -mod setup; - -pub fn router(app: &App) -> Router<App> { - [ - Router::new() - .route("/{*path}", get(path::get::handler)) - .route("/setup", get(setup::get::handler)), - Router::new() - .route("/", get(get::handler)) - .route("/me", get(me::get::handler)) - .route("/login", get(login::get::handler)) - .route("/ch/{channel}", get(ch::channel::get::handler)) - .route("/invite/{invite}", get(invite::invite::get::handler)) - .route_layer(crate::setup::Required(app.clone()).with_fallback(Redirect::to("/setup"))), - ] - .into_iter() - .fold(Router::default(), Router::merge) -} +pub mod ch; +pub mod get; +pub mod invite; +pub mod login; +pub mod me; +pub mod path; +pub mod setup; diff --git a/src/user/mod.rs b/src/user/mod.rs index f4c66ab..7ea3d26 100644 --- a/src/user/mod.rs +++ b/src/user/mod.rs @@ -6,10 +6,8 @@ mod history; mod id; pub mod password; pub mod repo; -mod routes; +pub mod routes; mod snapshot; mod validate; -pub use self::{ - event::Event, history::History, id::Id, password::Password, routes::router, snapshot::User, -}; +pub use self::{event::Event, history::History, id::Id, password::Password, snapshot::User}; diff --git a/src/user/routes/mod.rs b/src/user/routes/mod.rs index ade96cb..f9bbed7 100644 --- a/src/user/routes/mod.rs +++ b/src/user/routes/mod.rs @@ -1,14 +1,3 @@ -use axum::{Router, routing::post}; - -use crate::app::App; - -mod login; -mod logout; -mod password; - -pub fn router() -> Router<App> { - Router::new() - .route("/api/password", post(password::post::handler)) - .route("/api/auth/login", post(login::post::handler)) - .route("/api/auth/logout", post(logout::post::handler)) -} +pub mod login; +pub mod logout; +pub mod password; |
