From ea74daca4809e4008dd8d01039db9fff3be659d9 Mon Sep 17 00:00:00 2001 From: Owen Jacobson Date: Wed, 16 Oct 2024 20:14:33 -0400 Subject: Organizational pass on endpoints and routes. --- src/ui/routes/ch/channel.rs | 61 ++++++++++++++++++++++++++++++++++++++++++ src/ui/routes/ch/mod.rs | 1 + src/ui/routes/get.rs | 30 +++++++++++++++++++++ src/ui/routes/invite/invite.rs | 55 +++++++++++++++++++++++++++++++++++++ src/ui/routes/invite/mod.rs | 4 +++ src/ui/routes/login.rs | 11 ++++++++ src/ui/routes/mod.rs | 26 ++++++++++++++++++ src/ui/routes/path.rs | 12 +++++++++ src/ui/routes/setup.rs | 43 +++++++++++++++++++++++++++++ 9 files changed, 243 insertions(+) create mode 100644 src/ui/routes/ch/channel.rs create mode 100644 src/ui/routes/ch/mod.rs create mode 100644 src/ui/routes/get.rs create mode 100644 src/ui/routes/invite/invite.rs create mode 100644 src/ui/routes/invite/mod.rs create mode 100644 src/ui/routes/login.rs create mode 100644 src/ui/routes/mod.rs create mode 100644 src/ui/routes/path.rs create mode 100644 src/ui/routes/setup.rs (limited to 'src/ui/routes') 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, + login: Option, + Path(channel): Path, + ) -> Result { + 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) -> 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) -> Result { + 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, + Path(invite): Path, + ) -> Result { + 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) -> 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 { + 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 { + [ + 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) -> Result> { + 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) -> Result { + 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(), + } + } + } +} -- cgit v1.2.3