diff options
| author | gennyble <gen@nyble.dev> | 2026-09-25 21:14:31 -0500 |
|---|---|---|
| committer | gennyble <gen@nyble.dev> | 2026-09-25 21:14:31 -0500 |
| commit | fdbca76c341711e8516252b52e981ff0ebba133f (patch) | |
| tree | 9a608806da2f0aa40016fbd79d758752fc06d5f7 /splorm/src/handler | |
| parent | 761f67912f9a49736fb0fe6990192942d2522ecb (diff) | |
wikarden page U
Diffstat (limited to 'splorm/src/handler')
| -rw-r--r-- | splorm/src/handler/quark/api.rs | 102 | ||||
| -rw-r--r-- | splorm/src/handler/quark/index.rs | 1 | ||||
| -rw-r--r-- | splorm/src/handler/quark/mod.rs | 74 |
3 files changed, 161 insertions, 16 deletions
diff --git a/splorm/src/handler/quark/api.rs b/splorm/src/handler/quark/api.rs index 5d6f9ef..9513cbd 100644 --- a/splorm/src/handler/quark/api.rs +++ b/splorm/src/handler/quark/api.rs @@ -4,7 +4,9 @@ use axum::{Extension, body::Body, extract::Multipart, http::StatusCode, response use somethings::auth::Session; use wikarden::Wikarden; -use crate::{RuntimeError, config::Settings, extractor::Wrap}; +use crate::{ + RuntimeError, config::Settings, extractor::Wrap, fs, handler::quark::open_template_path, +}; /// POST /quark/api/create pub async fn create( @@ -23,6 +25,7 @@ pub async fn create( }, Operation::Create => { let CreateForm { name, content }: CreateForm = data.try_into()?; + let redirect = api_redirect(&settings, &name, format!("../{name}")); wikarden .create(&name, content, false, format!("Create page '{name}'")) @@ -30,14 +33,65 @@ pub async fn create( .map_err(|_| RuntimeError::Unknown)?; Response::builder() - .status(StatusCode::TEMPORARY_REDIRECT) - .header("Location", format!("../{name}")) - .body(Body::from(format!("redirecting to {name}"))) + .status(StatusCode::OK) + .body(redirect.await?) .map_err(|_| RuntimeError::Unknown) } + _ => Err(RuntimeError::bad_request("usupported operation")), } } +/// POST /quark/api/edit +pub async fn edit( + Extension(wikarden): Extension<Arc<Wikarden>>, + Extension(settings): Extension<Settings>, + Wrap(_session): Wrap<Session>, + mut multipart: Multipart, +) -> Result<Response, RuntimeError> { + let data = MultipartFields::from_multipart(&mut multipart).await?; + let operation = data.operation()?; + + match operation { + Operation::Preview => match data.content { + None => Err(RuntimeError::incomplete_form("content")), + Some(content) => super::markup::preview(&content, &settings).await, + }, + Operation::Edit => { + let EditForm { + name, + content, + commit, + }: EditForm = data.try_into()?; + let redirect = api_redirect(&settings, &name, format!("../{name}")); + + wikarden + .modify(&name, false, content, commit) + .await + .map_err(|_| RuntimeError::Unknown)?; + + Response::builder() + .status(StatusCode::OK) + .body(redirect.await?) + .map_err(|_| RuntimeError::Unknown) + } + _ => Err(RuntimeError::bad_request("usupported operation")), + } +} + +async fn api_redirect( + settings: &Settings, + page_name: &str, + page_location: String, +) -> Result<Body, RuntimeError> { + let path = settings.quark_template_dir().join("api_redirect.html"); + let mut doc = open_template_path(path, settings).await?; + + doc.set("page-name", page_name); + doc.set("page-location", page_location); + + Ok(Body::from(doc.compile())) +} + #[derive(Debug, PartialEq)] struct CreateForm { name: String, @@ -45,16 +99,26 @@ struct CreateForm { } #[derive(Debug, PartialEq)] +struct EditForm { + name: String, + content: String, + commit: String, +} + +#[derive(Debug, PartialEq)] enum Operation { Create, + Edit, Preview, } struct MultipartFields { name: Option<String>, content: Option<String>, + commit: Option<String>, create: bool, + edit: bool, preview: bool, } @@ -62,8 +126,10 @@ impl MultipartFields { pub async fn from_multipart(multipart: &mut Multipart) -> Result<Self, RuntimeError> { let mut name = None; let mut content = None; + let mut commit = None; let mut create = false; + let mut edit = false; let mut preview = false; macro_rules! field_text { @@ -81,9 +147,11 @@ impl MultipartFields { { match field.name() { Some("create") => create = true, + Some("edit") => edit = true, Some("preview") => preview = true, Some("name") => name = field_text!(field "name"), Some("content") => content = field_text!(field "content"), + Some("commit") => commit = field_text!(field "commit"), _ => (), } } @@ -91,15 +159,18 @@ impl MultipartFields { Ok(MultipartFields { name, content, + commit, create, + edit, preview, }) } fn operation(&self) -> Result<Operation, RuntimeError> { - match (self.create, self.preview) { - (true, false) => Ok(Operation::Create), - (false, true) => Ok(Operation::Preview), + match (self.create, self.edit, self.preview) { + (true, false, false) => Ok(Operation::Create), + (false, true, false) => Ok(Operation::Edit), + (false, false, true) => Ok(Operation::Preview), _ => Err(RuntimeError::bad_request("too many operations")), } @@ -112,9 +183,22 @@ impl TryInto<CreateForm> for MultipartFields { fn try_into(self) -> Result<CreateForm, RuntimeError> { match (self.name, self.content) { (Some(name), Some(content)) => Ok(CreateForm { name, content }), - (None, Some(_)) => Err(RuntimeError::incomplete_form("name")), - (Some(_), None) => Err(RuntimeError::incomplete_form("content")), _ => Err(RuntimeError::bad_request("create request missing fields")), } } } + +impl TryInto<EditForm> for MultipartFields { + type Error = RuntimeError; + + fn try_into(self) -> Result<EditForm, RuntimeError> { + match (self.name, self.content, self.commit) { + (Some(name), Some(content), Some(commit)) => Ok(EditForm { + name, + content, + commit, + }), + _ => Err(RuntimeError::bad_request("edit request missing fields")), + } + } +} diff --git a/splorm/src/handler/quark/index.rs b/splorm/src/handler/quark/index.rs index 983f76c..391a61c 100644 --- a/splorm/src/handler/quark/index.rs +++ b/splorm/src/handler/quark/index.rs @@ -39,6 +39,7 @@ pub async fn index( let template_path = settings.quark_template_dir().join("page.html"); let mut template = super::open_template_path(template_path, &settings).await?; + template.set("title", "Quark"); template.set("main", "<h2>quark index</h2>"); Response::builder() diff --git a/splorm/src/handler/quark/mod.rs b/splorm/src/handler/quark/mod.rs index 1a9598c..7130d18 100644 --- a/splorm/src/handler/quark/mod.rs +++ b/splorm/src/handler/quark/mod.rs @@ -2,29 +2,33 @@ mod api; pub mod index; pub mod markup; -use std::sync::Arc; +use std::{str::FromStr, sync::Arc}; use axum::{ Extension, Router, body::Body, extract::{Path, RawQuery}, + http::{StatusCode, header}, response::Response, routing::{get, post}, }; use bempline::{Document, Options, options::IncludeMethod}; use camino::Utf8PathBuf; +use somethings::auth::Session; use wikarden::Wikarden; use crate::{ - RuntimeError, + Query, RuntimeError, config::Settings, - fs::{self}, + extractor::Wrap, + fs::{self, RequestPath, SafePath}, state::SplormState, }; pub fn routes() -> Router<SplormState> { Router::new() .route("/api/create", post(api::create)) + .route("/api/edit", post(api::edit)) .route("/", get(index::index)) .route("/{name}", get(page)) } @@ -33,12 +37,24 @@ async fn page( Extension(wikarden): Extension<Arc<Wikarden>>, Extension(settings): Extension<Settings>, Path(name): Path<String>, - RawQuery(_rquery): RawQuery, + maybe_session: Option<Wrap<Session>>, + RawQuery(rquery): RawQuery, ) -> Result<Response, RuntimeError> { - //TODO: parse rquery for "edit" + if let Some(rq) = rquery { + if let Ok(q) = Query::from_str(&rq) { + if maybe_session.is_none() { + return Err(RuntimeError::Generic403); + } + + if q.has("edit") { + return page_edit(name, &wikarden, settings).await; + } + } + } let template_path = settings.quark_template_dir().join("page.html"); let mut template = open_template_path(template_path, &settings).await?; + template.set("title", &name); match wikarden.source(name, false).await { Err(e) => template.set("main", format!("<h2>{e:?}</h2>")), @@ -48,8 +64,40 @@ async fn page( } Response::builder() - .status(200) - .header("Content-Type", "text/html") + .status(StatusCode::OK) + .header(header::CONTENT_TYPE, "text/html") + .body(Body::from(template.compile())) + .map_err(|_| RuntimeError::Unknown) +} + +async fn page_edit( + page: String, + wik: &Wikarden, + settings: Settings, +) -> Result<Response, RuntimeError> { + let template_path = settings.quark_template_dir().join("minimal.html"); + let mut template = open_template_path(template_path, &settings).await?; + + let rp = RequestPath::verify_safe(settings.root(), "quark/edit.html")?; + let edit = fs::read_file_to_string(rp.system_path()).await?; + let mut page_template = open_template(&edit, rp.system_path(), &settings).await?; + + let src = match wik.source(&page, false).await { + //FIXME: Make a variant for unfound wiki pages + Err(_e) => return Err(RuntimeError::Generic404), + Ok(src) => src, + }; + + page_template.set("page-editing", page); + page_template.set("page-content", src.data); + + template.set("mainClass", "main-create"); + template.set("quarkForm", "true"); + template.set("main", page_template.compile()); + + Response::builder() + .status(StatusCode::OK) + .header(header::CONTENT_TYPE, "text/html") .body(Body::from(template.compile())) .map_err(|_| RuntimeError::Unknown) } @@ -66,3 +114,15 @@ pub async fn open_template_path<P: Into<Utf8PathBuf>>( )); Document::from_str(str, options).map_err(|perr| RuntimeError::template_parse_error(perr, path)) } + +pub async fn open_template<'p>( + content: &str, + path: SafePath<'p>, + settings: &Settings, +) -> Result<Document, RuntimeError> { + let options = Options::new().include_path(IncludeMethod::Path( + settings.quark_template_dir().into_std_path_buf(), + )); + Document::from_str(content, options) + .map_err(|perr| RuntimeError::template_parse_error(perr, path.to_path_buf())) +} |
