summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.env1
-rw-r--r--.gitignore1
-rw-r--r--src/error.rs23
-rw-r--r--src/handlers/admin.rs39
-rw-r--r--src/handlers/frontend.rs37
-rw-r--r--src/handlers/mod.rs21
-rw-r--r--src/main.rs105
7 files changed, 124 insertions, 103 deletions
diff --git a/.env b/.env
deleted file mode 100644
index 839a708..0000000
--- a/.env
+++ /dev/null
@@ -1 +0,0 @@
-TOKEN=TOKEN
diff --git a/.gitignore b/.gitignore
index 840f776..a52003f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
# Added by cargo
/target
*.db*
+.env
diff --git a/src/error.rs b/src/error.rs
new file mode 100644
index 0000000..72378ae
--- /dev/null
+++ b/src/error.rs
@@ -0,0 +1,23 @@
+use axum::http::StatusCode;
+use axum::response::IntoResponse;
+
+pub struct AppError(anyhow::Error);
+
+impl IntoResponse for AppError {
+ fn into_response(self) -> axum::response::Response {
+ (
+ StatusCode::INTERNAL_SERVER_ERROR,
+ format!("Something went wrong: {}", self.0),
+ )
+ .into_response()
+ }
+}
+
+impl<E> From<E> for AppError
+where
+ E: Into<anyhow::Error>,
+{
+ fn from(err: E) -> Self {
+ Self(err.into())
+ }
+}
diff --git a/src/handlers/admin.rs b/src/handlers/admin.rs
new file mode 100644
index 0000000..349275b
--- /dev/null
+++ b/src/handlers/admin.rs
@@ -0,0 +1,39 @@
+use std::env;
+
+use axum::extract::Request;
+use axum::http::{HeaderMap, StatusCode};
+use axum::middleware::Next;
+use axum::response::Response;
+use subtle::ConstantTimeEq;
+
+pub async fn admin_middleware(
+ headers: HeaderMap,
+ request: Request,
+ next: Next,
+) -> Result<Response, StatusCode> {
+ let expected_token = env::var("TOKEN").map_err(|_| {
+ eprintln!("Error: TOKEN environment variable is not set");
+ StatusCode::INTERNAL_SERVER_ERROR
+ })?;
+
+ let token = headers
+ .get("X-Admin-Token")
+ .and_then(|header| header.to_str().ok())
+ .ok_or(StatusCode::UNAUTHORIZED)?;
+
+ if !expected_token
+ .as_bytes()
+ .ct_eq(token.as_bytes())
+ .unwrap_u8()
+ == 1
+ {
+ eprintln!("Warning: Unauthorized access attempt with invalid token");
+ return Err(StatusCode::UNAUTHORIZED);
+ }
+
+ Ok(next.run(request).await)
+}
+
+pub async fn admin_handler() -> &'static str {
+ "Hello World"
+}
diff --git a/src/handlers/frontend.rs b/src/handlers/frontend.rs
new file mode 100644
index 0000000..f4b7c6c
--- /dev/null
+++ b/src/handlers/frontend.rs
@@ -0,0 +1,37 @@
+use axum::response::IntoResponse;
+use maud::{html, Markup, DOCTYPE};
+
+use crate::error::AppError;
+
+pub async fn home() -> Result<impl IntoResponse, AppError> {
+ Ok(html!({
+ (head("Home"))
+ body class="bg-[#eae3d2] text-[#333] dark:bg-[#333] dark:text-[#eae3d2] font-mono text-base tracking-wide" {
+ (header_with_navbar())
+ }
+ }))
+}
+
+fn header_with_navbar() -> Markup {
+ html!({
+ header {
+ navbar class="p-3 text-sm flex flex-row flex-wrap content-center justify-around decoration-purple-500 shadow" {
+ a class="hover:underline" href="/" { "Home" }
+ a class="hover:underline" href="/" { "Stories" }
+ a class="hover:underline" href="/" { "Tools" }
+ a class="hover:underline" href="/" { "Games" }
+ }
+ }
+ })
+}
+
+fn head(page_title: &str) -> Markup {
+ html! {
+ head {
+ (DOCTYPE)
+ meta charset="utf-8";
+ link rel="stylesheet" type="text/css" href="/static/styles.css";
+ title { (page_title) }
+ }
+ }
+}
diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs
new file mode 100644
index 0000000..d69f590
--- /dev/null
+++ b/src/handlers/mod.rs
@@ -0,0 +1,21 @@
+use axum::extract::DefaultBodyLimit;
+use axum::middleware::{self};
+use axum::routing::get;
+use axum::Router;
+use sqlx::SqlitePool;
+use tower_http::services::ServeDir;
+
+use admin::{admin_handler, admin_middleware};
+use frontend::home;
+
+mod admin;
+mod frontend;
+
+pub async fn router(pool: SqlitePool) -> Router {
+ Router::new()
+ .route("/admin", get(admin_handler))
+ .layer(middleware::from_fn(admin_middleware))
+ .route("/", get(home))
+ .nest_service("/static", ServeDir::new("src/static"))
+ .layer(DefaultBodyLimit::max(10000))
+}
diff --git a/src/main.rs b/src/main.rs
index feaa6a7..b4d7567 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,39 +1,11 @@
use std::env;
-use axum::extract::{DefaultBodyLimit, Request};
-use axum::http::{HeaderMap, StatusCode};
-use axum::middleware::Next;
-use axum::response::{IntoResponse, Response};
-use axum::routing::get;
-use axum::{middleware, Router};
use dotenvy::dotenv;
-use maud::{html, Markup, DOCTYPE};
use sqlx::sqlite::SqlitePoolOptions;
-use subtle::ConstantTimeEq;
-use tower_http::services::ServeDir;
mod db;
-
-struct AppError(anyhow::Error);
-
-impl IntoResponse for AppError {
- fn into_response(self) -> axum::response::Response {
- (
- StatusCode::INTERNAL_SERVER_ERROR,
- format!("Something went wrong: {}", self.0),
- )
- .into_response()
- }
-}
-
-impl<E> From<E> for AppError
-where
- E: Into<anyhow::Error>,
-{
- fn from(err: E) -> Self {
- Self(err.into())
- }
-}
+mod error;
+mod handlers;
#[tokio::main]
async fn main() {
@@ -50,79 +22,8 @@ async fn main() {
.await
.expect("Failed to run migrations");
- let app = Router::new()
- .route("/admin", get(admin_handler))
- .layer(middleware::from_fn(admin_middleware))
- .route("/", get(home))
- .nest_service("/static", ServeDir::new("src/static"))
- .layer(DefaultBodyLimit::max(10000));
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
- axum::serve(listener, app.into_make_service())
+ axum::serve(listener, handlers::router(pool).await.into_make_service())
.await
.unwrap();
}
-
-async fn admin_middleware(
- headers: HeaderMap,
- request: Request,
- next: Next,
-) -> Result<Response, StatusCode> {
- let expected_token = env::var("TOKEN").map_err(|_| {
- eprintln!("Error: TOKEN environment variable is not set");
- StatusCode::INTERNAL_SERVER_ERROR
- })?;
-
- let token = headers
- .get("X-Admin-Token")
- .and_then(|header| header.to_str().ok())
- .ok_or(StatusCode::UNAUTHORIZED)?;
-
- if !expected_token
- .as_bytes()
- .ct_eq(token.as_bytes())
- .unwrap_u8()
- == 1
- {
- eprintln!("Warning: Unauthorized access attempt with invalid token");
- return Err(StatusCode::UNAUTHORIZED);
- }
-
- Ok(next.run(request).await)
-}
-
-async fn admin_handler() -> &'static str {
- "Hello World"
-}
-
-async fn home() -> Result<impl IntoResponse, AppError> {
- Ok(html!({
- (head("Home"))
- body class="bg-[#eae3d2] text-[#333] dark:bg-[#333] dark:text-[#eae3d2] font-mono text-base tracking-wide" {
- (header_with_navbar())
- }
- }))
-}
-
-fn header_with_navbar() -> Markup {
- html!({
- header {
- navbar class="p-3 text-sm flex flex-row flex-wrap content-center justify-around decoration-purple-500 shadow" {
- a class="hover:underline" href="/" { "Home" }
- a class="hover:underline" href="/" { "Stories" }
- a class="hover:underline" href="/" { "Tools" }
- a class="hover:underline" href="/" { "Games" }
- }
- }
- })
-}
-
-fn head(page_title: &str) -> Markup {
- html! {
- head {
- (DOCTYPE)
- meta charset="utf-8";
- link rel="stylesheet" type="text/css" href="/static/styles.css";
- title { (page_title) }
- }
- }
-}