From d85dd163e34ebdf963e1299b4ad3387ea713797f Mon Sep 17 00:00:00 2001 From: Tyler Murphy Date: Fri, 3 Feb 2023 15:03:16 -0500 Subject: [PATCH] docs is ssr'd --- public/api.html | 542 ------------------------------------------ src/api/admin.rs | 75 +++++- src/api/auth.rs | 57 ++++- src/api/mod.rs | 8 +- src/api/posts.rs | 97 +++++++- src/api/users.rs | 82 ++++++- src/database/posts.rs | 3 +- src/main.rs | 4 + src/public/docs.rs | 181 ++++++++++++++ src/public/mod.rs | 3 +- src/public/pages.rs | 4 +- 11 files changed, 500 insertions(+), 556 deletions(-) delete mode 100644 public/api.html create mode 100644 src/public/docs.rs diff --git a/public/api.html b/public/api.html deleted file mode 100644 index 6e6086a..0000000 --- a/public/api.html +++ /dev/null @@ -1,542 +0,0 @@ - - - - - - - - - XSSBook - API Documentation - - - -
-
-
- POST - /api/auth/register - Registeres a new account -
-
-

Body

-
- {
- "firstname" : "[Object"
- "lastname" : "object]"
- "email" : "object@object.object"
- "password" : "i love js"
- "gender" : "lettuce"
- "day" : 1
- "month" : 1
- "year" : 1970
- }
-
-

Responses

-
- 201 - Successfully created new user, auth cookie is returned -
-
- 400 - Body does not match paramaters -
-
-
-
-
- POST - /api/auth/login - Logs into an existing account -
-
-

Body

-
- {
- "email" : "object@object.object"
- "password" : "i love js"
- }
-
-

Responses

-
- 200 - Successfully logged in, auth cookie is returned -
-
- 400 - Body does not match paramaters, or email/password is already in use -
-
-
-
-
- POST - /api/auth/logout - Logs out of an logged in account - auth cookie is required for authentication -
-
-

Responses

-
- 200 - Successfully logged out -
-
- 401 - Unauthorized -
-
- 500 - Failed to log out user -
-
-
-
-
- POST - /api/posts/create - Creates a new post - auth cookie is required for authentication -
-
-

Body

-
- {
- "content" : "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
- }
-
-

Responses

-
- 201 - Successfully created post -
-
- 400 - Body does not match paramaters -
-
- 401 - Unauthorized -
-
- 500 - Failed to create post -
-
-
-
-
- POST - /api/posts/page - Load a section of posts from newest to oldest - auth cookie is required for authentication -
-
-

Body

-
- {
- "page" : 0
- }
-
-

Responses

-
- 200 - Returns posts in application/json -
-
- 400 - Body does not match paramaters -
-
- 401 - Unauthorized -
-
- 500 - Failed to fetch posts -
-
-
-
-
- POST - /api/posts/user - Load a section of posts from newest to oldest from a specific user - auth cookie is required for authentication -
-
-

Body

-
- {
- "user_id" : 3
- "page" : 0
- }
-
-

Responses

-
- 200 - Returns posts in application/json -
-
- 400 - Body does not match paramaters -
-
- 401 - Unauthorized -
-
- 500 - Failed to fetch posts -
-
-
-
-
- PATCH - /api/posts/comment - Adds a comment to a post - auth cookie is required for authentication -
-
-

Body

-
- {
- "content" : "This is a very good post"
- "post_id" : 0
- }
-
-

Responses

-
- 200 - Successfully added comment -
-
- 400 - Body does not match paramaters -
-
- 401 - Unauthorized -
-
- 500 - Failed to add comment -
-
-
-
-
- PATCH - /api/posts/like - Set like status on a post - auth cookie is required for authentication -
-
-

Body

-
- {
- "state" : true
- "post_id" : 0
- }
-
-

Responses

-
- 200 - Successfully set like status -
-
- 400 - Body does not match paramaters -
-
- 401 - Unauthorized -
-
- 500 - Failed to set like status -
-
-
-
-
- POST - /api/users/load - Load a requested set of users - auth cookie is required for authentication -
-
-

Body

-
- {
- "ids" : [0,3,7]
- }
-
-

Responses

-
- 200 - Returns users in application/json -
-
- 400 - Body does not match paramaters -
-
- 401 - Unauthorized -
-
- 500 - Failed to fetch users -
-
-
-
-
- POST - /api/users/page - Load a section of users from newest to oldest - auth cookie is required for authentication -
-
-

Body

-
- {
- "page" : 0
- }
-
-

Responses

-
- 200 - Returns users in application/json -
-
- 400 - Body does not match paramaters -
-
- 401 - Unauthorized -
-
- 500 - Failed to fetch users -
-
-
-
-
- POST - /api/users/self - Returns current authenticated user (whoami) - auth cookie is required for authentication -
-
-

Responses

-
- 200 - Returns authed user in application/json -
-
- 401 - Unauthorized -
-
- 500 - Failed to fetch user -
-
-
-
-
- PUT - /api/users/avatar - Set your current profile avatar - auth cookie is required for authentication -
-
-

Body

-
- PNG sent as a binary blob -
-

Responses

-
- 200 - Successfully updated avatar -
-
- 400 - Invalid PNG or disallowed size -
-
- 401 - Unauthorized -
-
- 500 - Failed to update avatar -
-
-
-
-
- PUT - /api/users/banner - Set your current profile banner - auth cookie is required for authentication -
-
-

Body

-
- PNG sent as a binary blob -
-

Responses

-
- 200 - Successfully updated banner -
-
- 400 - Invalid PNG or disallowed size -
-
- 401 - Unauthorized -
-
- 500 - Failed to update banner -
-
-
-
-
- POST - /api/admin/auth - Authenticates on the admin panel -
-
-

Body

-
- {
- "secret" : "admin"
- }
-
-

Responses

-
- 200 - Successfully authed, admin cookie returned -
-
- 400 - Body does match parameters, or invalid admin scret -
-
-
-
-
- POST - /api/admin/query - Run a SQL query on the database - admin cookie is required for authentication -
-
-

Body

-
- {
- "query" : "DROP TABLE users;"
- }
-
-

Responses

-
- 200 - Successfully ran SQL query -
-
- 400 - Body does match parameters -
-
- 401 - Unauthorized -
-
- 500 - SQL query error -
-
-
-
-
- POST - /api/admin/posts - Returns the entire posts table - admin cookie is required for authentication -
-
-

Responses

-
- 200 - Returns sql table in text/html -
-
- 401 - Unauthorized -
-
- 500 - Failed to fetch data -
-
-
-
-
- POST - /api/admin/users - Returns the entire users table - admin cookie is required for authentication -
-
-

Responses

-
- 200 - Returns sql table in text/html -
-
- 401 - Unauthorized -
-
- 500 - Failed to fetch data -
-
-
-
-
- POST - /api/admin/sessions - Returns the entire posts sessions - admin cookie is required for authentication -
-
-

Responses

-
- 200 - Returns sql table in text/html -
-
- 401 - Unauthorized -
-
- 500 - Failed to fetch data -
-
-
-
- \ No newline at end of file diff --git a/src/api/admin.rs b/src/api/admin.rs index a23d20f..8db3032 100644 --- a/src/api/admin.rs +++ b/src/api/admin.rs @@ -6,13 +6,29 @@ use tower_cookies::{Cookie, Cookies}; use crate::{ database, - public::admin, + public::{admin, docs::{EndpointDocumentation, EndpointMethod}}, types::{ extract::{AdminUser, Check, CheckResult, Json}, http::ResponseCode, }, }; +pub const ADMIN_AUTH: EndpointDocumentation = EndpointDocumentation { + uri: "/api/admin/auth", + method: EndpointMethod::Post, + description: "Authenticates on the admin panel", + body: Some(r#" + { + "secret" : "admin" + } + "#), + responses: &[ + (200, "Successfully executed SQL query"), + (400, " Successfully authed, admin cookie returned") + ], + cookie: None, +}; + #[derive(Deserialize)] struct AdminAuthRequest { secret: String, @@ -40,6 +56,24 @@ async fn auth(cookies: Cookies, Json(body): Json) -> Response ResponseCode::Success.text("Successfully logged in") } +pub const ADMIN_QUERY: EndpointDocumentation = EndpointDocumentation { + uri: "/api/admin/query", + method: EndpointMethod::Post, + description: "Run a SQL query on the database", + body: Some(r#" + { + "query" : "DROP TABLE users;" + } + "#), + responses: &[ + (200, "Successfully executed SQL query"), + (400, "Body does not match parameters"), + (401, "Unauthorized"), + (500, "SQL query ran into an error") + ], + cookie: Some("admin"), +}; + #[derive(Deserialize)] struct QueryRequest { query: String, @@ -60,14 +94,53 @@ async fn query(_: AdminUser, Json(body): Json) -> Response { } } +pub const ADMIN_POSTS: EndpointDocumentation = EndpointDocumentation { + uri: "/api/admin/posts", + method: EndpointMethod::Post, + description: "Returns the entire posts table", + body: None, + responses: &[ + (200, "Returns sql table in text/html"), + (401, "Unauthorized"), + (500, "Failed to fetch data") + ], + cookie: Some("admin"), +}; + async fn posts(_: AdminUser) -> Response { admin::generate_posts() } +pub const ADMIN_USERS: EndpointDocumentation = EndpointDocumentation { + uri: "/api/admin/users", + method: EndpointMethod::Post, + description: "Returns the entire users table", + body: None, + responses: &[ + (200, "Returns sql table in text/html"), + (401, "Unauthorized"), + (500, "Failed to fetch data") + ], + cookie: Some("admin"), +}; + async fn users(_: AdminUser) -> Response { admin::generate_users() } +pub const ADMIN_SESSIONS: EndpointDocumentation = EndpointDocumentation { + uri: "/api/admin/sessions", + method: EndpointMethod::Post, + description: "Returns the entire sessions table", + body: None, + responses: &[ + (200, "Returns sql table in text/html"), + (401, "Unauthorized"), + (500, "Failed to fetch data") + ], + cookie: Some("admin"), +}; + async fn sessions(_: AdminUser) -> Response { admin::generate_sessions() } diff --git a/src/api/auth.rs b/src/api/auth.rs index 7f7cf9e..0ff180e 100644 --- a/src/api/auth.rs +++ b/src/api/auth.rs @@ -3,11 +3,34 @@ use serde::Deserialize; use time::{Duration, OffsetDateTime}; use tower_cookies::{Cookie, Cookies}; -use crate::types::{ +use crate::{types::{ extract::{AuthorizedUser, Check, CheckResult, Json, Log}, http::ResponseCode, session::Session, user::User, +}, public::docs::{EndpointDocumentation, EndpointMethod}}; + +pub const AUTH_REGISTER: EndpointDocumentation = EndpointDocumentation { + uri: "/api/auth/register", + method: EndpointMethod::Post, + description: "Registeres a new account", + body: Some(r#" + { + "firstname": "[Object]", + "lastname": "object]", + "email": "object@object.object", + "password": "i love js", + "gender": "object", + "day": 1, + "month": 1, + "year": 1970 + } + "#), + responses: &[ + (201, "Successfully registered new user"), + (400, "Body does not match parameters"), + ], + cookie: None, }; #[derive(Deserialize, Debug)] @@ -93,9 +116,26 @@ async fn register(cookies: Cookies, Json(body): Json) -> Res cookies.add(cookie); - ResponseCode::Created.text("Successfully created new user") + ResponseCode::Created.text("Successfully created new user, auth cookie is returned") } +pub const AUTH_LOGIN: EndpointDocumentation = EndpointDocumentation { + uri: "/api/auth/login", + method: EndpointMethod::Post, + description: "Logs into an existing account", + body: Some(r#" + { + "email": "object@object.object", + "password": "i love js" + } + "#), + responses: &[ + (200, "Successfully logged in, auth cookie is returned"), + (400, "Body does not match parameters, or invalid email password combination"), + ], + cookie: None, +}; + #[derive(Deserialize)] struct LoginRequest { email: String, @@ -136,6 +176,19 @@ async fn login(cookies: Cookies, Json(body): Json) -> Response { ResponseCode::Success.text("Successfully logged in") } +pub const AUTH_LOGOUT: EndpointDocumentation = EndpointDocumentation { + uri: "/api/auth/logout", + method: EndpointMethod::Post, + description: "Logs out of a logged in account", + body: None, + responses: &[ + (200, "Successfully logged out"), + (401, "Unauthorized"), + (500, "Failed to log out user") + ], + cookie: None, +}; + async fn logout(cookies: Cookies, AuthorizedUser(user): AuthorizedUser, _: Log) -> Response { cookies.remove(Cookie::new("auth", "")); diff --git a/src/api/mod.rs b/src/api/mod.rs index 9efcefc..12563e3 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -6,10 +6,10 @@ use tower_governor::{ GovernorLayer, }; -mod admin; -mod auth; -mod posts; -mod users; +pub mod admin; +pub mod auth; +pub mod posts; +pub mod users; pub use auth::RegistrationRequet; diff --git a/src/api/posts.rs b/src/api/posts.rs index 6aa074f..f1cdab3 100644 --- a/src/api/posts.rs +++ b/src/api/posts.rs @@ -5,10 +5,28 @@ use axum::{ }; use serde::Deserialize; -use crate::types::{ +use crate::{types::{ extract::{AuthorizedUser, Check, CheckResult, Json}, http::ResponseCode, post::Post, +}, public::docs::{EndpointDocumentation, EndpointMethod}}; + +pub const POSTS_CREATE: EndpointDocumentation = EndpointDocumentation { + uri: "/api/posts/create", + method: EndpointMethod::Post, + description: "Creates a new post", + body: Some(r#" + { + "content" : "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." + } + "#), + responses: &[ + (201, "Successfully created post"), + (400, "Body does not match parameters"), + (401, "Unauthorized"), + (500, "Failed to create post") + ], + cookie: Some("auth"), }; #[derive(Deserialize)] @@ -43,6 +61,24 @@ async fn create( ResponseCode::Created.json(&json) } +pub const POSTS_PAGE: EndpointDocumentation = EndpointDocumentation { + uri: "/api/posts/page", + method: EndpointMethod::Post, + description: "Load a section of posts from newest to oldest", + body: Some(r#" + { + "page": 0 + } + "#), + responses: &[ + (200, "Returns posts in application/json"), + (400, "Body does not match parameters"), + (401, "Unauthorized"), + (500, "Failed to fetch posts") + ], + cookie: Some("auth"), +}; + #[derive(Deserialize)] struct PostPageRequest { page: u64, @@ -69,10 +105,29 @@ async fn page( ResponseCode::Success.json(&json) } +pub const POSTS_USER: EndpointDocumentation = EndpointDocumentation { + uri: "/api/posts/user", + method: EndpointMethod::Post, + description: "Load a section of posts from newest to oldest from a specific user", + body: Some(r#" + { + "user_id": 3, + "page": 0 + } + "#), + responses: &[ + (200, "Returns posts in application/json"), + (400, "Body does not match parameters"), + (401, "Unauthorized"), + (500, "Failed to fetch posts") + ], + cookie: Some("auth"), +}; + #[derive(Deserialize)] struct UsersPostsRequest { user_id: u64, - page: u64 + page: u64, } impl Check for UsersPostsRequest { @@ -96,6 +151,25 @@ async fn user( ResponseCode::Success.json(&json) } +pub const POSTS_COMMENT: EndpointDocumentation = EndpointDocumentation { + uri: "/api/posts/comment", + method: EndpointMethod::Patch, + description: "Add a comment to a post", + body: Some(r#" + { + "content": "This is a very cool comment", + "post_id": 0 + } + "#), + responses: &[ + (200, "Successfully added comment"), + (400, "Body does not match parameters"), + (401, "Unauthorized"), + (500, "Failed to add comment") + ], + cookie: Some("auth"), +}; + #[derive(Deserialize)] struct PostCommentRequest { content: String, @@ -129,6 +203,25 @@ async fn comment( ResponseCode::Success.text("Successfully commented on post") } +pub const POSTS_LIKE: EndpointDocumentation = EndpointDocumentation { + uri: "/api/posts/like", + method: EndpointMethod::Patch, + description: "Set like status on a post", + body: Some(r#" + { + "post_id" : 0, + "status" : true + } + "#), + responses: &[ + (200, "Successfully set like status"), + (400, "Body does not match parameters"), + (401, "Unauthorized"), + (500, "Failed to set like status") + ], + cookie: Some("auth"), +}; + #[derive(Deserialize)] struct PostLikeRequest { state: bool, diff --git a/src/api/users.rs b/src/api/users.rs index e3c992b..7d1f006 100644 --- a/src/api/users.rs +++ b/src/api/users.rs @@ -1,8 +1,8 @@ -use crate::types::{ +use crate::{types::{ extract::{AuthorizedUser, Check, CheckResult, Json, Png}, http::ResponseCode, user::User, -}; +}, public::docs::{EndpointDocumentation, EndpointMethod}}; use axum::{ response::Response, routing::{post, put}, @@ -10,6 +10,24 @@ use axum::{ }; use serde::Deserialize; +pub const USERS_LOAD: EndpointDocumentation = EndpointDocumentation { + uri: "/api/users/load", + method: EndpointMethod::Post, + description: "Loads a requested set of users", + body: Some(r#" + { + "ids": [0, 3, 7] + } + "#), + responses: &[ + (200, "Returns users in application/json"), + (400, "Body does not match parameters"), + (401, "Unauthorized"), + (500, "Failed to fetch users") + ], + cookie: Some("auth"), +}; + #[derive(Deserialize)] struct UserLoadRequest { ids: Vec, @@ -33,6 +51,25 @@ async fn load_batch( ResponseCode::Success.json(&json) } +pub const USERS_PAGE: EndpointDocumentation = EndpointDocumentation { + uri: "/api/users/page", + method: EndpointMethod::Post, + description: "Load a section of users from newest to oldest", + body: Some(r#" + { + "user_id": 3, + "page": 0 + } + "#), + responses: &[ + (200, "Returns users in application/json"), + (400, "Body does not match parameters"), + (401, "Unauthorized"), + (500, "Failed to fetch users") + ], + cookie: Some("auth"), +}; + #[derive(Deserialize)] struct UserPageReqiest { page: u64, @@ -59,6 +96,19 @@ async fn load_page( ResponseCode::Success.json(&json) } +pub const USERS_SELF: EndpointDocumentation = EndpointDocumentation { + uri: "/api/users/self", + method: EndpointMethod::Post, + description: "Returns current authenticated user (whoami)", + body: None, + responses: &[ + (200, "Successfully executed SQL query"), + (401, "Unauthorized"), + (500, "Failed to fetch user") + ], + cookie: Some("auth"), +}; + async fn load_self(AuthorizedUser(user): AuthorizedUser) -> Response { let Ok(json) = serde_json::to_string(&user) else { return ResponseCode::InternalServerError.text("Failed to fetch user") @@ -67,6 +117,20 @@ async fn load_self(AuthorizedUser(user): AuthorizedUser) -> Response { ResponseCode::Success.json(&json) } +pub const USERS_AVATAR: EndpointDocumentation = EndpointDocumentation { + uri: "/api/users/avatar", + method: EndpointMethod::Put, + description: "Set your current profile avatar", + body: Some("PNG sent as a binary blob"), + responses: &[ + (200, "Successfully updated avatar"), + (400, "Invalid PNG or disallowed size"), + (401, "Unauthorized"), + (500, "Failed to update avatar") + ], + cookie: Some("auth"), +}; + async fn avatar(AuthorizedUser(user): AuthorizedUser, Png(img): Png) -> Response { let path = format!("./public/image/custom/avatar/{}.png", user.user_id); @@ -77,6 +141,20 @@ async fn avatar(AuthorizedUser(user): AuthorizedUser, Png(img): Png) -> Response ResponseCode::Success.text("Successfully updated avatar") } +pub const USERS_BANNER: EndpointDocumentation = EndpointDocumentation { + uri: "/api/users/banner", + method: EndpointMethod::Put, + description: "Set your current profile banner", + body: Some("PNG sent as a binary blob"), + responses: &[ + (200, "Successfully updated banner"), + (400, "Invalid PNG or disallowed size"), + (401, "Unauthorized"), + (500, "Failed to update banner") + ], + cookie: Some("auth"), +}; + async fn banner(AuthorizedUser(user): AuthorizedUser, Png(img): Png) -> Response { let path = format!("./public/image/custom/banner/{}.png", user.user_id); diff --git a/src/database/posts.rs b/src/database/posts.rs index a81d6a7..8ca9b2d 100644 --- a/src/database/posts.rs +++ b/src/database/posts.rs @@ -94,7 +94,8 @@ pub fn get_users_post_page(user_id: u64, page: u64) -> Result, rusqlit tracing::trace!("Retrieving users posts"); let page_size = 10; let conn = database::connect()?; - let mut stmt = conn.prepare("SELECT * FROM posts WHERE user_id = ? ORDER BY post_id DESC LIMIT ? OFFSET ?")?; + let mut stmt = conn + .prepare("SELECT * FROM posts WHERE user_id = ? ORDER BY post_id DESC LIMIT ? OFFSET ?")?; let row = stmt.query_map([user_id, page_size, page_size * page], |row| { let row = post_from_row(row)?; Ok(row) diff --git a/src/main.rs b/src/main.rs index 8a8e9d8..ace9968 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,6 +15,8 @@ use tracing_subscriber::{ }; use types::extract::RequestIp; +use crate::public::docs; + mod api; mod database; mod public; @@ -55,6 +57,8 @@ async fn main() { exit(1) }; + docs::init().await; + fs::create_dir_all("./public/image/custom").expect("Coudn't make custom data directory"); fs::create_dir_all("./public/image/custom/avatar") .expect("Coudn't make custom avatar directory"); 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> = Mutex::new(Vec::new()); +} + +fn generate_body(body: Option<&'static str>) -> String { + let Some(body) = body else { + return String::new() + }; + let html = r#" +

Body

+
+ $body +
+ "# + .to_string(); + let body = body.trim(); + if body.starts_with('{') { + return html.replace( + "$body", + &beautify(body) + .replace("}"), + ); + } + html.replace("$body", body) +} + +fn generate_responses(responses: &[(u16, &'static str)]) -> String { + let mut html = r#" +

Responses

+ $responses + "# + .to_string(); + + for response in responses { + let res = format!( + r#" +
+ {} + {} +
+ $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#"{cookie} cookie is required for authentication"# + ) +} + +fn generate_endpoint(doc: &EndpointDocumentation) -> String { + let html = r#" +
+
+ $method + $uri + $description + $cookie +
+
+ $body + $responses +
+
+ "#; + + 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#" + + + + + + + + + XSSBook - API Documentation + + + +
+ {data} +
+ + + "# + ); + + ResponseCode::Success.html(&html) +} diff --git a/src/public/mod.rs b/src/public/mod.rs index ebd5fc5..82c994d 100644 --- a/src/public/mod.rs +++ b/src/public/mod.rs @@ -18,6 +18,7 @@ use crate::types::http::ResponseCode; pub mod admin; pub mod console; +pub mod docs; pub mod file; pub mod pages; @@ -33,7 +34,7 @@ pub fn router() -> Router { Router::new() .nest("/", pages::router()) - .route("/favicon.ico",get(file::favicon)) + .route("/favicon.ico", get(file::favicon)) .route("/js/*path", get(file::js)) .route("/css/*path", get(file::css)) .route("/fonts/*path", get(file::fonts)) diff --git a/src/public/pages.rs b/src/public/pages.rs index 196a441..32056b7 100644 --- a/src/public/pages.rs +++ b/src/public/pages.rs @@ -12,6 +12,8 @@ use crate::{ }, }; +use super::docs; + async fn root(user: Option, _: Log) -> Response { if user.is_some() { Redirect::to("/home").into_response() @@ -49,7 +51,7 @@ async fn admin() -> Response { } async fn api() -> Response { - super::serve("/api.html").await + docs::generate().await } async fn wordpress(_: Log) -> Response {