diff options
| author | Owen Jacobson <owen@grimoire.ca> | 2024-10-16 20:14:33 -0400 |
|---|---|---|
| committer | Owen Jacobson <owen@grimoire.ca> | 2024-10-16 20:14:33 -0400 |
| commit | ea74daca4809e4008dd8d01039db9fff3be659d9 (patch) | |
| tree | 5972cabf646e8d5e635e9e2a176bff56c178461a /src/ui/routes | |
| parent | 56e16e29db55dae84549229d24b971f8bcf7da21 (diff) | |
Organizational pass on endpoints and routes.
Diffstat (limited to 'src/ui/routes')
| -rw-r--r-- | src/ui/routes/ch/channel.rs | 61 | ||||
| -rw-r--r-- | src/ui/routes/ch/mod.rs | 1 | ||||
| -rw-r--r-- | src/ui/routes/get.rs | 30 | ||||
| -rw-r--r-- | src/ui/routes/invite/invite.rs | 55 | ||||
| -rw-r--r-- | src/ui/routes/invite/mod.rs | 4 | ||||
| -rw-r--r-- | src/ui/routes/login.rs | 11 | ||||
| -rw-r--r-- | src/ui/routes/mod.rs | 26 | ||||
| -rw-r--r-- | src/ui/routes/path.rs | 12 | ||||
| -rw-r--r-- | src/ui/routes/setup.rs | 43 |
9 files changed, 243 insertions, 0 deletions
diff --git a/src/ui/routes/ch/channel.rs b/src/ui/routes/ch/channel.rs new file mode 100644 index 0000000..353d000 --- /dev/null +++ b/src/ui/routes/ch/channel.rs @@ -0,0 +1,61 @@ +pub mod get { + use axum::{ + extract::{Path, State}, + response::{self, IntoResponse, Redirect}, + }; + + use crate::{ + app::App, + channel, + error::Internal, + login::Login, + ui::{ + assets::{Asset, Assets}, + error::NotFound, + }, + }; + + pub async fn handler( + State(app): State<App>, + login: Option<Login>, + Path(channel): Path<channel::Id>, + ) -> Result<Asset, Error> { + login.ok_or(Error::NotLoggedIn)?; + app.channels() + .get(&channel) + .await + .map_err(Error::internal)? + .ok_or(Error::NotFound)?; + + Assets::index().map_err(Error::Internal) + } + + #[derive(Debug, thiserror::Error)] + pub enum Error { + #[error("requested channel not found")] + NotFound, + #[error("not logged in")] + NotLoggedIn, + #[error("{0}")] + Internal(Internal), + } + + impl Error { + fn internal(err: impl Into<Internal>) -> Self { + Self::Internal(err.into()) + } + } + + impl IntoResponse for Error { + fn into_response(self) -> response::Response { + match self { + Self::NotFound => match Assets::index() { + Ok(asset) => NotFound(asset).into_response(), + Err(internal) => internal.into_response(), + }, + Self::NotLoggedIn => Redirect::temporary("/login").into_response(), + Self::Internal(error) => error.into_response(), + } + } + } +} diff --git a/src/ui/routes/ch/mod.rs b/src/ui/routes/ch/mod.rs new file mode 100644 index 0000000..ff02972 --- /dev/null +++ b/src/ui/routes/ch/mod.rs @@ -0,0 +1 @@ +pub mod channel; diff --git a/src/ui/routes/get.rs b/src/ui/routes/get.rs new file mode 100644 index 0000000..97737e1 --- /dev/null +++ b/src/ui/routes/get.rs @@ -0,0 +1,30 @@ +use axum::response::{self, IntoResponse, Redirect}; + +use crate::{ + error::Internal, + login::Login, + ui::assets::{Asset, Assets}, +}; + +pub async fn handler(login: Option<Login>) -> Result<Asset, Error> { + login.ok_or(Error::NotLoggedIn)?; + + Assets::index().map_err(Error::Internal) +} + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("not logged in")] + NotLoggedIn, + #[error("{0}")] + Internal(Internal), +} + +impl IntoResponse for Error { + fn into_response(self) -> response::Response { + match self { + Self::NotLoggedIn => Redirect::temporary("/login").into_response(), + Self::Internal(error) => error.into_response(), + } + } +} diff --git a/src/ui/routes/invite/invite.rs b/src/ui/routes/invite/invite.rs new file mode 100644 index 0000000..06e5792 --- /dev/null +++ b/src/ui/routes/invite/invite.rs @@ -0,0 +1,55 @@ +pub mod get { + use axum::{ + extract::{Path, State}, + response::{self, IntoResponse}, + }; + + use crate::{ + app::App, + error::Internal, + invite, + ui::{ + assets::{Asset, Assets}, + error::NotFound, + }, + }; + + pub async fn handler( + State(app): State<App>, + Path(invite): Path<invite::Id>, + ) -> Result<Asset, Error> { + app.invites() + .get(&invite) + .await + .map_err(Error::internal)? + .ok_or(Error::NotFound)?; + + Assets::index().map_err(Error::Internal) + } + + #[derive(Debug, thiserror::Error)] + pub enum Error { + #[error("invite not found")] + NotFound, + #[error("{0}")] + Internal(Internal), + } + + impl Error { + fn internal(err: impl Into<Internal>) -> Self { + Self::Internal(err.into()) + } + } + + impl IntoResponse for Error { + fn into_response(self) -> response::Response { + match self { + Self::NotFound => match Assets::index() { + Ok(asset) => NotFound(asset).into_response(), + Err(internal) => internal.into_response(), + }, + Self::Internal(error) => error.into_response(), + } + } + } +} diff --git a/src/ui/routes/invite/mod.rs b/src/ui/routes/invite/mod.rs new file mode 100644 index 0000000..50af8be --- /dev/null +++ b/src/ui/routes/invite/mod.rs @@ -0,0 +1,4 @@ +// In this case, the first redundant `invite` is a literal path segment, and the +// second `invite` reflects a placeholder. +#[allow(clippy::module_inception)] +pub mod invite; diff --git a/src/ui/routes/login.rs b/src/ui/routes/login.rs new file mode 100644 index 0000000..81a874c --- /dev/null +++ b/src/ui/routes/login.rs @@ -0,0 +1,11 @@ +pub mod get { + use crate::{ + error::Internal, + ui::assets::{Asset, Assets}, + }; + + #[allow(clippy::unused_async)] + pub async fn handler() -> Result<Asset, Internal> { + Assets::index() + } +} diff --git a/src/ui/routes/mod.rs b/src/ui/routes/mod.rs new file mode 100644 index 0000000..72d9a4a --- /dev/null +++ b/src/ui/routes/mod.rs @@ -0,0 +1,26 @@ +use axum::{middleware, routing::get, Router}; + +use crate::{app::App, ui::middleware::setup_required}; + +mod ch; +mod get; +mod invite; +mod login; +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("/login", get(login::get::handler)) + .route("/ch/:channel", get(ch::channel::get::handler)) + .route("/invite/:invite", get(invite::invite::get::handler)) + .route_layer(middleware::from_fn_with_state(app.clone(), setup_required)), + ] + .into_iter() + .fold(Router::default(), Router::merge) +} diff --git a/src/ui/routes/path.rs b/src/ui/routes/path.rs new file mode 100644 index 0000000..2e9a657 --- /dev/null +++ b/src/ui/routes/path.rs @@ -0,0 +1,12 @@ +pub mod get { + use axum::extract::Path; + + use crate::ui::{ + assets::{Asset, Assets}, + error::NotFound, + }; + + pub async fn handler(Path(path): Path<String>) -> Result<Asset, NotFound<String>> { + Assets::load(path) + } +} diff --git a/src/ui/routes/setup.rs b/src/ui/routes/setup.rs new file mode 100644 index 0000000..649cc5f --- /dev/null +++ b/src/ui/routes/setup.rs @@ -0,0 +1,43 @@ +pub mod get { + use axum::{ + extract::State, + response::{self, IntoResponse, Redirect}, + }; + + use crate::{ + app::App, + error::Internal, + ui::assets::{Asset, Assets}, + }; + + pub async fn handler(State(app): State<App>) -> Result<Asset, Error> { + if app + .setup() + .completed() + .await + .map_err(Internal::from) + .map_err(Error::Internal)? + { + Err(Error::SetupCompleted) + } else { + Assets::index().map_err(Error::Internal) + } + } + + #[derive(Debug, thiserror::Error)] + pub enum Error { + #[error("setup already completed")] + SetupCompleted, + #[error("{0}")] + Internal(Internal), + } + + impl IntoResponse for Error { + fn into_response(self) -> response::Response { + match self { + Self::SetupCompleted => Redirect::to("/login").into_response(), + Self::Internal(error) => error.into_response(), + } + } + } +} |
