use std::env; use axum::{response::Response, routing::post, Router}; use serde::Deserialize; use tower_cookies::{Cookie, Cookies}; use crate::{ public::{ admin, docs::{EndpointDocumentation, EndpointMethod}, }, types::{ extract::{AdminUser, Check, CheckResult, Database, 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, } impl Check for AdminAuthRequest { fn check(&self) -> CheckResult { Ok(()) } } async fn auth(cookies: Cookies, Json(body): Json) -> Response { let check = env::var("SECRET").unwrap_or_else(|_| "admin".to_string()); if check != body.secret { return ResponseCode::BadRequest.text("Invalid admin secret"); } let mut cookie = Cookie::new("admin", admin::regen_secret().await); cookie.set_secure(true); cookie.set_http_only(true); cookie.set_path("/"); cookies.add(cookie); 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, } impl Check for QueryRequest { fn check(&self) -> CheckResult { Ok(()) } } async fn query(_: AdminUser, Database(db): Database, Json(body): Json) -> Response { match db.query(body.query) { Ok(changes) => ResponseCode::Success.text(&format!( "Query executed successfully. {changes} lines changed." )), Err(err) => ResponseCode::InternalServerError.text(&format!("{err}")), } } 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, Database(db): Database) -> Response { admin::generate_posts(&db) } 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, Database(db): Database) -> Response { admin::generate_users(&db) } 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, Database(db): Database) -> Response { admin::generate_sessions(&db) } pub const ADMIN_COMMENTS: EndpointDocumentation = EndpointDocumentation { uri: "/api/admin/comments", method: EndpointMethod::Post, description: "Returns the entire comments table", body: None, responses: &[ (200, "Returns sql table in text/html"), (401, "Unauthorized"), (500, "Failed to fetch data"), ], cookie: Some("admin"), }; async fn comments(_: AdminUser, Database(db): Database) -> Response { admin::generate_comments(&db) } pub const ADMIN_LIKES: EndpointDocumentation = EndpointDocumentation { uri: "/api/admin/likes", method: EndpointMethod::Post, description: "Returns the entire likes table", body: None, responses: &[ (200, "Returns sql table in text/html"), (401, "Unauthorized"), (500, "Failed to fetch data"), ], cookie: Some("admin"), }; async fn likes(_: AdminUser, Database(db): Database) -> Response { admin::generate_likes(&db) } async fn check(check: Option) -> Response { if check.is_none() { ResponseCode::Success.text("false") } else { ResponseCode::Success.text("true") } } pub fn router() -> Router { Router::new() .route("/auth", post(auth)) .route("/query", post(query)) .route("/posts", post(posts)) .route("/users", post(users)) .route("/sessions", post(sessions)) .route("/comments", post(comments)) .route("/likes", post(likes)) .route("/check", post(check)) }