diff --git a/public/css/home.css b/public/css/home.css
index 60dc3a5..f467aba 100644
--- a/public/css/home.css
+++ b/public/css/home.css
@@ -80,7 +80,7 @@ body {
}
.icons {
- background-image: url('/images/icons.png');
+ background-image: url('/image/icons.png');
display: inline-block;
width: 18px;
height: 18px;
diff --git a/public/js/main.js b/public/js/main.js
index 5737173..ffbc1f3 100644
--- a/public/js/main.js
+++ b/public/js/main.js
@@ -33,11 +33,11 @@ function remove(id) {
}
function pfp(id) {
- return ``
+ return ``
}
function banner(id) {
- return ``
+ return ``
}
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
diff --git a/src/api/admin.rs b/src/api/admin.rs
index 7de5fc0..a23d20f 100644
--- a/src/api/admin.rs
+++ b/src/api/admin.rs
@@ -5,7 +5,8 @@ use serde::Deserialize;
use tower_cookies::{Cookie, Cookies};
use crate::{
- admin, database,
+ database,
+ public::admin,
types::{
extract::{AdminUser, Check, CheckResult, Json},
http::ResponseCode,
diff --git a/src/api/image.rs b/src/api/image.rs
deleted file mode 100644
index 84eccc7..0000000
--- a/src/api/image.rs
+++ /dev/null
@@ -1,54 +0,0 @@
-use axum::{extract::Query, response::Response, routing::get, Router, http::StatusCode};
-use serde::Deserialize;
-
-use crate::types::http::ResponseCode;
-
-
-
-#[derive(Deserialize)]
-struct AvatarRequest {
- user_id: u64,
-}
-
-async fn avatar(params: Option>) -> Response {
-
- let Some(params) = params else {
- return ResponseCode::BadRequest.text("Missing query paramaters");
- };
-
- let custom = format!("/image/custom/avatar/{}.png", params.user_id);
- let default = format!("/image/default/{}.png", params.user_id % 25);
-
- let file = ResponseCode::Success.file(&custom).await;
- if file.status() != StatusCode::OK {
- return ResponseCode::Success.file(&default).await
- }
- file
-}
-
-#[derive(Deserialize)]
-struct BannerRequest {
- user_id: u64,
-}
-
-async fn banner(params: Option>) -> Response {
-
- let Some(params) = params else {
- return ResponseCode::BadRequest.text("Missing query paramaters");
- };
-
- let custom = format!("/image/custom/banner/{}.png", params.user_id);
-
- let file = ResponseCode::Success.file(&custom).await;
- if file.status() != StatusCode::OK {
- return ResponseCode::NotFound.text("User does not have a custom banner")
- }
- file
-}
-
-
-pub fn router() -> Router {
- Router::new()
- .route("/avatar", get(avatar))
- .route("/banner", get(banner))
-}
diff --git a/src/api/mod.rs b/src/api/mod.rs
index adc19d7..9efcefc 100644
--- a/src/api/mod.rs
+++ b/src/api/mod.rs
@@ -1,14 +1,17 @@
use crate::types::extract::RouterURI;
-use axum::{Extension, Router, BoxError, error_handling::HandleErrorLayer};
+use axum::{error_handling::HandleErrorLayer, BoxError, Extension, Router};
use tower::ServiceBuilder;
-use tower_governor::{governor::GovernorConfigBuilder, GovernorLayer, errors::display_error, key_extractor::SmartIpKeyExtractor};
+use tower_governor::{
+ errors::display_error, governor::GovernorConfigBuilder, key_extractor::SmartIpKeyExtractor,
+ GovernorLayer,
+};
-pub mod admin;
-pub mod auth;
-pub mod pages;
-pub mod posts;
-pub mod users;
-pub mod image;
+mod admin;
+mod auth;
+mod posts;
+mod users;
+
+pub use auth::RegistrationRequet;
pub fn router() -> Router {
let governor_conf = Box::new(
diff --git a/src/api/users.rs b/src/api/users.rs
index 83a0d4e..e3c992b 100644
--- a/src/api/users.rs
+++ b/src/api/users.rs
@@ -3,7 +3,11 @@ use crate::types::{
http::ResponseCode,
user::User,
};
-use axum::{response::Response, routing::{post, put}, Router};
+use axum::{
+ response::Response,
+ routing::{post, put},
+ Router,
+};
use serde::Deserialize;
#[derive(Deserialize)]
@@ -64,7 +68,6 @@ async fn load_self(AuthorizedUser(user): AuthorizedUser) -> Response {
}
async fn avatar(AuthorizedUser(user): AuthorizedUser, Png(img): Png) -> Response {
-
let path = format!("./public/image/custom/avatar/{}.png", user.user_id);
if img.save(path).is_err() {
@@ -75,7 +78,6 @@ async fn avatar(AuthorizedUser(user): AuthorizedUser, Png(img): Png) -> Response
}
async fn banner(AuthorizedUser(user): AuthorizedUser, Png(img): Png) -> Response {
-
let path = format!("./public/image/custom/banner/{}.png", user.user_id);
if img.save(path).is_err() {
diff --git a/src/database/users.rs b/src/database/users.rs
index 27c3b7f..8045bc4 100644
--- a/src/database/users.rs
+++ b/src/database/users.rs
@@ -2,7 +2,7 @@ use rusqlite::{OptionalExtension, Row};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use tracing::instrument;
-use crate::{api::auth::RegistrationRequet, database, types::user::User};
+use crate::{api::RegistrationRequet, database, types::user::User};
pub fn init() -> Result<(), rusqlite::Error> {
let sql = "
diff --git a/src/main.rs b/src/main.rs
index e8f17b0..8a8e9d8 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,38 +1,25 @@
use axum::{
body::HttpBody,
- http::{Request, StatusCode},
+ extract::DefaultBodyLimit,
+ http::Request,
middleware::{self, Next},
response::Response,
- RequestExt, Router, extract::DefaultBodyLimit,
+ RequestExt, Router,
};
-use std::{net::SocketAddr, process::exit, fs};
+use public::console;
+use std::{fs, net::SocketAddr, process::exit};
use tower_cookies::CookieManagerLayer;
use tracing::{error, info, metadata::LevelFilter};
use tracing_subscriber::{
filter::filter_fn, prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt, Layer,
};
-use types::{http::ResponseCode, extract::RequestIp};
+use types::extract::RequestIp;
-use crate::api::{pages, image};
-
-mod admin;
mod api;
-mod console;
mod database;
+mod public;
mod types;
-async fn serve(req: Request, next: Next) -> Response
-where
- B: Send + Sync + 'static + HttpBody,
-{
- let uri = req.uri();
- let file = ResponseCode::Success.file(&uri.to_string()).await;
- if file.status() != StatusCode::OK {
- return next.run(req).await;
- }
- file
-}
-
async fn log(mut req: Request, next: Next) -> Response
where
B: Send + Sync + 'static + HttpBody,
@@ -47,7 +34,7 @@ where
}
async fn not_found() -> Response {
- ResponseCode::NotFound.file("/404.html").await
+ public::serve("/404.html").await
}
#[tokio::main]
@@ -69,16 +56,16 @@ async fn main() {
};
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");
- fs::create_dir_all("./public/image/custom/banner").expect("Coudn't make custom banner directory");
+ fs::create_dir_all("./public/image/custom/avatar")
+ .expect("Coudn't make custom avatar directory");
+ fs::create_dir_all("./public/image/custom/banner")
+ .expect("Coudn't make custom banner directory");
let app = Router::new()
.fallback(not_found)
.layer(middleware::from_fn(log))
- .layer(middleware::from_fn(serve))
- .nest("/", pages::router())
+ .nest("/", public::router())
.nest("/api", api::router())
- .nest("/cdn", image::router())
.layer(CookieManagerLayer::new())
.layer(DefaultBodyLimit::max(512_000));
diff --git a/src/admin.rs b/src/public/admin.rs
similarity index 100%
rename from src/admin.rs
rename to src/public/admin.rs
diff --git a/src/console.rs b/src/public/console.rs
similarity index 100%
rename from src/console.rs
rename to src/public/console.rs
diff --git a/src/public/file.rs b/src/public/file.rs
new file mode 100644
index 0000000..b54ef25
--- /dev/null
+++ b/src/public/file.rs
@@ -0,0 +1,69 @@
+use axum::{
+ extract::{Path, Query},
+ http::StatusCode,
+ response::Response,
+};
+use serde::Deserialize;
+
+use crate::types::http::ResponseCode;
+
+use super::console;
+
+pub async fn js(Path(path): Path) -> Response {
+ let path = format!("/js/{}", path);
+ super::serve(&path).await
+}
+
+pub async fn css(Path(path): Path) -> Response {
+ let path = format!("/css/{}", path);
+ super::serve(&path).await
+}
+
+pub async fn fonts(Path(path): Path) -> Response {
+ let path = format!("/fonts/{}", path);
+ super::serve(&path).await
+}
+
+pub async fn image(Path(path): Path) -> Response {
+ let path = format!("/image/{}", path);
+ super::serve(&path).await
+}
+
+#[derive(Deserialize)]
+pub struct AvatarRequest {
+ user_id: u64,
+}
+
+pub async fn avatar(params: Option>) -> Response {
+ let Some(params) = params else {
+ return ResponseCode::BadRequest.text("Missing query paramaters");
+ };
+
+ let custom = format!("/image/custom/avatar/{}.png", params.user_id);
+ let default = format!("/image/default/{}.png", params.user_id % 25);
+
+ let file = super::serve(&custom).await;
+ if file.status() != StatusCode::OK {
+ return super::serve(&default).await;
+ }
+ file
+}
+
+#[derive(Deserialize)]
+pub struct BannerRequest {
+ user_id: u64,
+}
+
+pub async fn banner(params: Option>) -> Response {
+ let Some(params) = params else {
+ return ResponseCode::BadRequest.text("Missing query paramaters");
+ };
+
+ let custom = format!("/image/custom/banner/{}.png", params.user_id);
+
+ let file = super::serve(&custom).await;
+ if file.status() != StatusCode::OK {
+ return ResponseCode::NotFound.text("User does not have a custom banner");
+ }
+ file
+}
diff --git a/src/public/mod.rs b/src/public/mod.rs
new file mode 100644
index 0000000..cf8156d
--- /dev/null
+++ b/src/public/mod.rs
@@ -0,0 +1,47 @@
+use axum::{
+ body::Body,
+ http::{Request, StatusCode},
+ response::{IntoResponse, Response},
+ routing::get,
+ Router,
+};
+use tower::ServiceExt;
+use tower_http::services::ServeFile;
+
+use crate::types::http::ResponseCode;
+
+pub mod admin;
+pub mod console;
+pub mod file;
+pub mod pages;
+
+pub fn router() -> Router {
+ Router::new()
+ .nest("/", pages::router())
+ .route("/js/*path", get(file::js))
+ .route("/css/*path", get(file::css))
+ .route("/fonts/*path", get(file::fonts))
+ .route("/image/*path", get(file::image))
+ .route("/image/avatar", get(file::avatar))
+ .route("/image/banner", get(file::banner))
+}
+
+pub async fn serve(path: &str) -> Response {
+ if !path.chars().any(|c| c == '.') {
+ return ResponseCode::BadRequest.text("Invalid file path");
+ }
+
+ let path = format!("public{path}");
+ let file = ServeFile::new(path);
+
+ let Ok(res) = file.oneshot(Request::new(Body::empty())).await else {
+ tracing::error!("Error while fetching file");
+ return ResponseCode::InternalServerError.text("Error while fetching file");
+ };
+
+ if res.status() != StatusCode::OK {
+ return ResponseCode::NotFound.text("File not found");
+ }
+
+ res.into_response()
+}
diff --git a/src/api/pages.rs b/src/public/pages.rs
similarity index 59%
rename from src/api/pages.rs
rename to src/public/pages.rs
index 4ed2e49..1614d81 100644
--- a/src/api/pages.rs
+++ b/src/public/pages.rs
@@ -5,7 +5,7 @@ use axum::{
};
use crate::{
- console,
+ public::console,
types::{
extract::{AuthorizedUser, Log},
http::ResponseCode,
@@ -24,32 +24,20 @@ async fn login(user: Option, _: Log) -> Response {
if user.is_some() {
Redirect::to("/home").into_response()
} else {
- ResponseCode::Success.file("/login.html").await
+ super::serve("/login.html").await
}
}
-async fn home(user: Option, _: Log) -> Response {
- if user.is_none() {
- Redirect::to("/login").into_response()
- } else {
- ResponseCode::Success.file("/home.html").await
- }
+async fn home(_: Log) -> Response {
+ super::serve("/home.html").await
}
-async fn people(user: Option, _: Log) -> Response {
- if user.is_none() {
- Redirect::to("/login").into_response()
- } else {
- ResponseCode::Success.file("/people.html").await
- }
+async fn people(_: Log) -> Response {
+ super::serve("/people.html").await
}
-async fn profile(user: Option, _: Log) -> Response {
- if user.is_none() {
- Redirect::to("/login").into_response()
- } else {
- ResponseCode::Success.file("/profile.html").await
- }
+async fn profile(_: Log) -> Response {
+ super::serve("/profile.html").await
}
async fn console() -> Response {
@@ -57,7 +45,7 @@ async fn console() -> Response {
}
async fn admin() -> Response {
- ResponseCode::Success.file("/admin.html").await
+ super::serve("/admin.html").await
}
async fn wordpress(_: Log) -> Response {
diff --git a/src/types/extract.rs b/src/types/extract.rs
index 4d7ac51..1258ef9 100644
--- a/src/types/extract.rs
+++ b/src/types/extract.rs
@@ -1,20 +1,24 @@
-use std::{io::{Read, Cursor}, net::{IpAddr, SocketAddr}};
+use std::{
+ io::{Cursor, Read},
+ net::{IpAddr, SocketAddr},
+};
use axum::{
async_trait,
body::HttpBody,
- extract::{FromRequest, FromRequestParts, ConnectInfo},
+ extract::{ConnectInfo, FromRequest, FromRequestParts},
http::{request::Parts, Request},
response::Response,
BoxError, RequestExt,
};
use bytes::Bytes;
-use image::{io::Reader, ImageFormat, DynamicImage};
+use image::{io::Reader, DynamicImage, ImageFormat};
use serde::de::DeserializeOwned;
use tower_cookies::Cookies;
use crate::{
- admin, console,
+ public::admin,
+ public::console,
types::{
http::{ResponseCode, Result},
session::Session,
@@ -32,41 +36,43 @@ where
type Rejection = Response;
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result {
-
let headers = &parts.headers;
- let forwardedfor = headers.get("x-forwarded-for")
- .and_then(|h| h.to_str().ok())
- .and_then(|h|
- h.split(',')
- .rev()
- .find_map(|s| s.trim().parse::().ok())
- );
+ let forwardedfor = headers
+ .get("x-forwarded-for")
+ .and_then(|h| h.to_str().ok())
+ .and_then(|h| {
+ h.split(',')
+ .rev()
+ .find_map(|s| s.trim().parse::().ok())
+ });
if let Some(forwardedfor) = forwardedfor {
- return Ok(RequestIp(forwardedfor))
+ return Ok(RequestIp(forwardedfor));
}
- let realip = headers.get("x-real-ip")
- .and_then(|hv| hv.to_str().ok())
- .and_then(|s| s.parse::().ok());
+ let realip = headers
+ .get("x-real-ip")
+ .and_then(|hv| hv.to_str().ok())
+ .and_then(|s| s.parse::().ok());
if let Some(realip) = realip {
- return Ok(RequestIp(realip))
+ return Ok(RequestIp(realip));
}
- let realip = headers.get("x-real-ip")
- .and_then(|hv| hv.to_str().ok())
- .and_then(|s| s.parse::().ok());
+ let realip = headers
+ .get("x-real-ip")
+ .and_then(|hv| hv.to_str().ok())
+ .and_then(|s| s.parse::().ok());
if let Some(realip) = realip {
- return Ok(RequestIp(realip))
+ return Ok(RequestIp(realip));
}
let info = parts.extensions.get::>();
if let Some(info) = info {
- return Ok(RequestIp(info.0.ip()))
+ return Ok(RequestIp(info.0.ip()));
}
Err(ResponseCode::Forbidden.text("You have no ip"))
@@ -163,7 +169,6 @@ where
type Rejection = Response;
async fn from_request(req: Request, state: &S) -> Result {
-
let bytes = match read_body(req, state).await {
Ok(body) => body,
Err(err) => return Err(err),
@@ -171,7 +176,7 @@ where
let mut reader = Reader::new(Cursor::new(bytes));
reader.set_format(ImageFormat::Png);
-
+
let Ok(img) = reader.decode() else {
return Err(ResponseCode::BadRequest.text("Failed to decode png image"))
};
@@ -238,7 +243,6 @@ where
B::Error: Into,
S: Send + Sync,
{
-
let Ok(RequestIp(ip)) = req.extract_parts::().await else {
tracing::error!("Failed to read client ip");
return Err(ResponseCode::InternalServerError.text("Failed to read client ip"));
@@ -255,14 +259,7 @@ where
return Err(ResponseCode::BadRequest.text("Request can be at most 512kb"));
};
- console::log(
- ip,
- method,
- uri,
- Some(path.to_string()),
- None,
- )
- .await;
+ console::log(ip, method, uri, Some(path.to_string()), None).await;
Ok(bytes.bytes().flatten().collect())
}
diff --git a/src/types/http.rs b/src/types/http.rs
index 8524b15..06674ca 100644
--- a/src/types/http.rs
+++ b/src/types/http.rs
@@ -1,11 +1,8 @@
use axum::{
- body::Body,
headers::HeaderName,
- http::{HeaderValue, Request, StatusCode},
+ http::{HeaderValue, StatusCode},
response::{IntoResponse, Response},
};
-use tower::ServiceExt;
-use tower_http::services::ServeFile;
use tracing::instrument;
#[derive(Debug)]
@@ -58,24 +55,6 @@ impl ResponseCode {
);
res
}
-
- #[instrument()]
- pub async fn file(self, path: &str) -> Response {
- if !path.chars().any(|c| c == '.') {
- return Self::BadRequest.text("Folders cannot be served");
- }
- let path = format!("public{path}");
- let svc = ServeFile::new(path);
- let Ok(mut res) = svc.oneshot(Request::new(Body::empty())).await else {
- tracing::error!("Error while fetching file");
- return Self::InternalServerError.text("Error while fetching file");
- };
- if res.status() != StatusCode::OK {
- return Self::NotFound.text("File not found");
- }
- *res.status_mut() = self.code();
- res.into_response()
- }
}
pub type Result = std::result::Result;
diff --git a/src/types/user.rs b/src/types/user.rs
index 2bffa52..835b675 100644
--- a/src/types/user.rs
+++ b/src/types/user.rs
@@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize};
use tracing::instrument;
-use crate::api::auth::RegistrationRequet;
+use crate::api::RegistrationRequet;
use crate::database;
use crate::types::http::{ResponseCode, Result};