diff options
Diffstat (limited to 'src/ui.rs')
| -rw-r--r-- | src/ui.rs | 134 |
1 files changed, 0 insertions, 134 deletions
diff --git a/src/ui.rs b/src/ui.rs deleted file mode 100644 index 91d0eb8..0000000 --- a/src/ui.rs +++ /dev/null @@ -1,134 +0,0 @@ -use axum::{ - extract::{Path, Request, State}, - http::{header, StatusCode}, - middleware::{self, Next}, - response::{IntoResponse, Redirect, Response}, - routing::get, - Router, -}; -use mime_guess::Mime; -use rust_embed::EmbeddedFile; - -use crate::{app::App, channel, error::Internal, invite, login::Login}; - -#[derive(rust_embed::Embed)] -#[folder = "target/ui"] -struct Assets; - -impl Assets { - fn load(path: impl AsRef<str>) -> Result<Asset, NotFound<String>> { - let path = path.as_ref(); - let mime = mime_guess::from_path(path).first_or_octet_stream(); - - Self::get(path) - .map(|file| Asset(mime, file)) - .ok_or(NotFound(format!("not found: {path}"))) - } - - fn index() -> Result<Asset, Internal> { - // "not found" in this case really is an internal error, as it should - // never happen. `index.html` is a known-valid path. - Ok(Self::load("index.html")?) - } -} - -pub fn router(app: &App) -> Router<App> { - [ - Router::new() - .route("/*path", get(asset)) - .route("/setup", get(setup)), - Router::new() - .route("/", get(root)) - .route("/login", get(login)) - .route("/ch/:channel", get(channel)) - .route("/invite/:invite", get(invite)) - .route_layer(middleware::from_fn_with_state(app.clone(), setup_required)), - ] - .into_iter() - .fold(Router::default(), Router::merge) -} - -async fn asset(Path(path): Path<String>) -> Result<Asset, NotFound<String>> { - Assets::load(path) -} - -async fn root(login: Option<Login>) -> Result<impl IntoResponse, Internal> { - if login.is_none() { - Ok(Redirect::temporary("/login").into_response()) - } else { - Ok(Assets::index()?.into_response()) - } -} - -async fn login() -> Result<impl IntoResponse, Internal> { - Assets::index() -} - -async fn setup(State(app): State<App>) -> Result<impl IntoResponse, Internal> { - if app.setup().completed().await? { - Ok(Redirect::to("/login").into_response()) - } else { - Ok(Assets::index().into_response()) - } -} - -async fn channel( - State(app): State<App>, - login: Option<Login>, - Path(channel): Path<channel::Id>, -) -> Result<impl IntoResponse, Internal> { - if login.is_none() { - Ok(Redirect::temporary("/").into_response()) - } else if app.channels().get(&channel).await?.is_none() { - Ok(NotFound(Assets::index()?).into_response()) - } else { - Ok(Assets::index()?.into_response()) - } -} - -async fn invite( - State(app): State<App>, - Path(invite): Path<invite::Id>, -) -> Result<impl IntoResponse, Internal> { - match app.invites().get(&invite).await { - Ok(_) => Ok(Assets::index()?.into_response()), - Err(invite::app::Error::NotFound(_)) => Ok(NotFound(Assets::index()?).into_response()), - Err(other) => Err(Internal::from(other)), - } -} - -struct Asset(Mime, EmbeddedFile); - -impl IntoResponse for Asset { - fn into_response(self) -> Response { - let Self(mime, file) = self; - ( - StatusCode::OK, - [(header::CONTENT_TYPE, mime.as_ref())], - file.data, - ) - .into_response() - } -} - -#[derive(Debug, thiserror::Error)] -#[error("{0}")] -struct NotFound<E>(pub E); - -impl<E> IntoResponse for NotFound<E> -where - E: IntoResponse, -{ - fn into_response(self) -> Response { - let Self(response) = self; - (StatusCode::NOT_FOUND, response).into_response() - } -} - -pub async fn setup_required(State(app): State<App>, request: Request, next: Next) -> Response { - match app.setup().completed().await { - Ok(true) => next.run(request).await, - Ok(false) => Redirect::to("/setup").into_response(), - Err(error) => Internal::from(error).into_response(), - } -} |
