use poem::{ error::ResponseError, handler, http::{header, StatusCode}, web::{Data, Redirect}, IntoResponse, Request, Response, Result, }; use crate::{storage::StorageError, util, Talon}; #[derive(thiserror::Error, Debug)] pub enum PageError { #[error("invalid subdomain")] InvalidSubdomain, #[error("website does not have any version")] NoVersion, } impl ResponseError for PageError { fn status(&self) -> StatusCode { StatusCode::NOT_FOUND } } #[handler] pub async fn page(request: &Request, talon: Data<&Talon>) -> Result { let host = request .header(header::HOST) .ok_or(PageError::InvalidSubdomain)?; let (subdomain, vid) = util::parse_host(host, &talon.cfg.server.root_domain).ok_or(PageError::InvalidSubdomain)?; let vid = match vid { Some(vid) => vid, None => { let ws = talon.db.get_website(subdomain)?; ws.latest_version.ok_or(PageError::NoVersion)? } }; let (file, ok) = match talon .storage .get_file(subdomain, vid, request.uri().path(), request.headers()) { Ok(file) => (file, true), Err(StorageError::NotFound(f)) => { let version = talon.db.get_version(subdomain, vid)?; if let Some(fallback) = &version.fallback { ( talon .storage .get_file(subdomain, vid, fallback, request.headers())?, version.spa, ) } else if version.spa { ( talon .storage .get_file(subdomain, vid, "", request.headers())?, true, ) } else { return Err(StorageError::NotFound(f).into()); } } Err(e) => return Err(e.into()), }; Ok(match file.rd_path { Some(rd_path) => Redirect::moved_permanent(rd_path).into_response(), None => talon .storage .file_to_response(file, request.headers(), ok) .await? .into_response(), }) }