diff options
Diffstat (limited to 'src/public/docs.rs')
-rw-r--r-- | src/public/docs.rs | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/src/public/docs.rs b/src/public/docs.rs new file mode 100644 index 0000000..1f1448b --- /dev/null +++ b/src/public/docs.rs @@ -0,0 +1,181 @@ +use axum::response::Response; +use lazy_static::lazy_static; +use tokio::sync::Mutex; + +use crate::{api::{admin, users, posts, auth}, types::http::ResponseCode}; + +use super::console::beautify; + +pub enum EndpointMethod { + Post, + Put, + Patch, +} + +impl ToString for EndpointMethod { + fn to_string(&self) -> String { + match self { + Self::Post => "POST".to_owned(), + Self::Put => "PUT".to_owned(), + Self::Patch => "PATCH".to_owned(), + } + } +} + +pub struct EndpointDocumentation<'a> { + pub uri: &'static str, + pub method: EndpointMethod, + pub description: &'static str, + pub body: Option<&'static str>, + pub responses: &'a [(u16, &'static str)], + pub cookie: Option<&'static str>, +} + +lazy_static! { + static ref ENDPOINTS: Mutex<Vec<String>> = Mutex::new(Vec::new()); +} + +fn generate_body(body: Option<&'static str>) -> String { + let Some(body) = body else { + return String::new() + }; + let html = r#" + <h2>Body</h2> + <div class="body"> + $body + </div> + "# + .to_string(); + let body = body.trim(); + if body.starts_with('{') { + return html.replace( + "$body", + &beautify(body) + .replace("<span class='key'", "<br><span class='key'") + .replace('}', "<br>}"), + ); + } + html.replace("$body", body) +} + +fn generate_responses(responses: &[(u16, &'static str)]) -> String { + let mut html = r#" + <h2>Responses</h2> + $responses + "# + .to_string(); + + for response in responses { + let res = format!( + r#" + <div> + <span class="ptype">{}</span> + <span class="pdesc">{}</span> + </div> + $responses + "#, + response.0, response.1 + ); + html = html.replace("$responses", &res); + } + + html.replace("$responses", "") +} + +fn generate_cookie(cookie: Option<&'static str>) -> String { + let Some(cookie) = cookie else { + return String::new() + }; + format!( + r#"<span class="auth"><span>{cookie}</span> cookie is required for authentication</span>"# + ) +} + +fn generate_endpoint(doc: &EndpointDocumentation) -> String { + let html = r#" + <div> + <div class="endpoint"> + <span class="method $method_class">$method</span> + <span class="uri">$uri</span> + <span class="desc">$description</span> + $cookie + </div> + <div class="info"> + $body + $responses + </div> + </div> + "#; + + html.replace("$method_class", &doc.method.to_string().to_lowercase()) + .replace("$method", &doc.method.to_string()) + .replace("$uri", doc.uri) + .replace("$description", doc.description) + .replace("$cookie", &generate_cookie(doc.cookie)) + .replace("$body", &generate_body(doc.body)) + .replace("$responses", &generate_responses(doc.responses)) +} + +pub async fn init() { + let docs = vec![ + auth::AUTH_REGISTER, + auth::AUTH_LOGIN, + auth::AUTH_LOGOUT, + posts::POSTS_CREATE, + posts::POSTS_PAGE, + posts::POSTS_USER, + posts::POSTS_COMMENT, + posts::POSTS_LIKE, + users::USERS_LOAD, + users::USERS_PAGE, + users::USERS_SELF, + users::USERS_AVATAR, + users::USERS_BANNER, + admin::ADMIN_AUTH, + admin::ADMIN_QUERY, + admin::ADMIN_POSTS, + admin::ADMIN_USERS, + admin::ADMIN_SESSIONS, + ]; + let mut endpoints = ENDPOINTS.lock().await; + for doc in docs { + endpoints.push(generate_endpoint(&doc)); + } +} + +pub async fn generate() -> Response { + let mut data = String::new(); + { + let endpoints = ENDPOINTS.lock().await; + endpoints.iter().for_each(|endpoint| { + data.push_str(endpoint); + }); + } + + let html = format!( + r#" + <!DOCTYPE html> + <html lang="en"> + <head> + <meta charset="UTF-8"> + <link rel="stylesheet" href="/css/main.css"> + <link rel="stylesheet" href="/css/header.css"> + <link rel="stylesheet" href="/css/console.css"> + <link rel="stylesheet" href="/css/api.css"> + <title>XSSBook - API Documentation</title> + </head> + <body> + <div id="header"> + <span class="logo"><a href="/">xssbook</a></span> + <span class="gtext desc" style="margin-left: 6em; font-size: 2em; color: #606770">API Documentation</span> + </div> + <div id="docs"> + {data} + </div> + </body> + </html> + "# + ); + + ResponseCode::Success.html(&html) +} |