use std::{str::FromStr, sync::Arc}; use axum::{Extension, body::Body, extract::Multipart, http::StatusCode, response::Response}; use somethings::auth::Session; use wikarden::Wikarden; use super::PageFrontmatter; use crate::{ Query, RuntimeError, config::Settings, extractor::Wrap, handler::{quark::open_template_path, see_other}, }; /// POST /quark/api/create pub async fn create( Extension(wikarden): Extension>, Extension(settings): Extension, Wrap(_session): Wrap, mut multipart: Multipart, ) -> Result { 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::Create => { let CreateForm { name, content }: CreateForm = data.try_into()?; let redirect = api_redirect(&settings, &name, format!("../{name}")); let wikres = wikarden .create(&name, content, format!("Create page '{name}'")) .await; match wikres { Err(e) => api_error(&settings, e).await, Ok(_) => redirect.await, } } _ => Err(RuntimeError::bad_request("usupported operation")), } } /// POST /quark/api/create/section pub async fn create_section( Extension(wikarden): Extension>, Extension(settings): Extension, Wrap(_session): Wrap, body: String, ) -> Result { let query: Query = body.parse()?; match query.get_first_value("name") { None => Err(RuntimeError::incomplete_form("name")), Some(name) => match wikarden.create_section(name, None).await { Err(e) => return api_error(&settings, e).await, Ok(_) => super::markup::preview(&format!("created section {name}"), &settings).await, }, } } /// POST /quark/api/edit pub async fn edit( Extension(wikarden): Extension>, Extension(settings): Extension, Wrap(_session): Wrap, mut multipart: Multipart, ) -> Result { 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, frontmatter, }: EditForm = data.try_into()?; let redirect = api_redirect(&settings, &name, format!("../{name}")); let commit = match commit { Some(commit) => commit, None => { let time = timeparse::format::human_with_time(&timeparse::whichever_now()); format!("edited '{name}' {time}") } }; tracing::trace!("editing with commit='{commit}'"); let source = format!("{frontmatter}\n{content}"); match wikarden.modify(&name, source, commit).await { Err(e) => api_error(&settings, e).await, Ok(_) => redirect.await, } } _ => Err(RuntimeError::bad_request("usupported operation")), } } /// POST /quark/api/edit/section pub async fn edit_section( Extension(wikarden): Extension>, Extension(settings): Extension, Wrap(_session): Wrap, body: String, ) -> Result { let query: Query = body.parse()?; let name = query .get_first_value("name") .ok_or(RuntimeError::incomplete_form("name"))?; match query.get_first_value("home") { None => Err(RuntimeError::incomplete_form("home")), Some(home) => { if home.is_empty() { return Err(RuntimeError::incomplete_form("home")); } let full_path = format!("{name}/{home}"); match wikarden.page_exists(&full_path).await { Ok(true) => (), _ => { return api_error(&settings, format!("page '{full_path}' does not exist")) .await; } } let res = wikarden .modify_section_data(name, None, |sd| sd.set_home(Some(&full_path))) .await; match res { Err(e) => api_error(&settings, e).await, Ok(_) => api_success(&settings, format!("set redirection to '{full_path}'")).await, } } } } /// POST /quark/api/move pub async fn move_page( Extension(wikarden): Extension>, Extension(settings): Extension, Wrap(_session): Wrap, body: String, ) -> Result { let query: Query = body.parse()?; let from = query .get_first_value("from") .ok_or(RuntimeError::incomplete_form("from"))?; let to = query .get_first_value("to") .ok_or(RuntimeError::incomplete_form("to"))?; match wikarden.move_page(from, to).await { Err(e) => api_error(&settings, e).await, Ok(()) => api_success(&settings, format!("moved '{from}' to '{to}'")).await, } } /// POST /quark/api/delete pub async fn delete( Extension(wikarden): Extension>, Extension(settings): Extension, Wrap(_session): Wrap, body: String, ) -> Result { let query = Query::from_str(&body)?; let page = query .get_first_value("name") .ok_or(RuntimeError::incomplete_form("name"))?; let page_path = format!("../{page}"); if query.has("rejection") || !query.has("confirmation") { return see_other(page_path).await; } if page == settings.wikarden_index() { //FIXME: Return an error here explaining that the index cannot be deleted return see_other(page_path).await; } match wikarden.delete(page).await { Err(e) => api_error(&settings, e).await, Ok(_) => see_other(format!("../")).await, } } /// redirect for API paths /// /// reads the `api_redirect.html` template and returns a constructed `Response` /// /// The `api_redirct.html` template has javascript within it that allows /// the `