fixed missing attribute

This commit is contained in:
2026-03-25 16:52:57 +01:00
parent 34d7b42180
commit 6ba897d27f

View File

@@ -1,9 +1,9 @@
use axum::{ use axum::{
Router,
extract::{DefaultBodyLimit, Multipart, Path, State}, extract::{DefaultBodyLimit, Multipart, Path, State},
http::{HeaderMap, StatusCode}, http::{HeaderMap, StatusCode},
response::{IntoResponse, Json}, response::{IntoResponse, Json},
routing::{get, post, delete}, routing::{delete, get, post},
Router,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{env, fs, path::PathBuf, sync::Arc}; use std::{env, fs, path::PathBuf, sync::Arc};
@@ -45,6 +45,7 @@ impl Default for SiteConfig {
#[derive(Serialize)] #[derive(Serialize)]
struct PostInfo { struct PostInfo {
slug: String, slug: String,
excerpt: String,
} }
#[derive(Serialize)] #[derive(Serialize)]
@@ -125,13 +126,19 @@ async fn main() {
axum::serve(listener, app).await.unwrap(); axum::serve(listener, app).await.unwrap();
} }
fn check_auth(headers: &HeaderMap, admin_token: &str) -> Result<(), (StatusCode, Json<ErrorResponse>)> { fn check_auth(
headers: &HeaderMap,
admin_token: &str,
) -> Result<(), (StatusCode, Json<ErrorResponse>)> {
let auth_header = headers.get("Authorization").and_then(|h| h.to_str().ok()); let auth_header = headers.get("Authorization").and_then(|h| h.to_str().ok());
if auth_header != Some(&format!("Bearer {}", admin_token)) { if auth_header != Some(&format!("Bearer {}", admin_token)) {
warn!("Unauthorized access attempt detected"); warn!("Unauthorized access attempt detected");
return Err(( return Err((
StatusCode::UNAUTHORIZED, StatusCode::UNAUTHORIZED,
Json(ErrorResponse { error: "Unauthorized".to_string(), details: None }), Json(ErrorResponse {
error: "Unauthorized".to_string(),
details: None,
}),
)); ));
} }
Ok(()) Ok(())
@@ -157,12 +164,24 @@ async fn update_config(
let config_path = state.data_dir.join("config.json"); let config_path = state.data_dir.join("config.json");
let config_str = serde_json::to_string_pretty(&payload).map_err(|e| { let config_str = serde_json::to_string_pretty(&payload).map_err(|e| {
error!("Serialization error: {}", e); error!("Serialization error: {}", e);
(StatusCode::INTERNAL_SERVER_ERROR, Json(ErrorResponse { error: "Serialization error".to_string(), details: Some(e.to_string()) })) (
StatusCode::INTERNAL_SERVER_ERROR,
Json(ErrorResponse {
error: "Serialization error".to_string(),
details: Some(e.to_string()),
}),
)
})?; })?;
fs::write(&config_path, config_str).map_err(|e| { fs::write(&config_path, config_str).map_err(|e| {
error!("Write error for config: {}", e); error!("Write error for config: {}", e);
(StatusCode::INTERNAL_SERVER_ERROR, Json(ErrorResponse { error: "Write error".to_string(), details: Some(e.to_string()) })) (
StatusCode::INTERNAL_SERVER_ERROR,
Json(ErrorResponse {
error: "Write error".to_string(),
details: Some(e.to_string()),
}),
)
})?; })?;
Ok(Json(payload)) Ok(Json(payload))
@@ -178,15 +197,27 @@ async fn create_post(
if payload.slug.contains('/') || payload.slug.contains('\\') || payload.slug.contains("..") { if payload.slug.contains('/') || payload.slug.contains('\\') || payload.slug.contains("..") {
return Err(( return Err((
StatusCode::BAD_REQUEST, StatusCode::BAD_REQUEST,
Json(ErrorResponse { error: "Invalid slug".to_string(), details: None }), Json(ErrorResponse {
error: "Invalid slug".to_string(),
details: None,
}),
)); ));
} }
let file_path = state.data_dir.join("posts").join(format!("{}.md", payload.slug)); let file_path = state
.data_dir
.join("posts")
.join(format!("{}.md", payload.slug));
fs::write(&file_path, &payload.content).map_err(|e| { fs::write(&file_path, &payload.content).map_err(|e| {
error!("Write error for post {}: {}", payload.slug, e); error!("Write error for post {}: {}", payload.slug, e);
(StatusCode::INTERNAL_SERVER_ERROR, Json(ErrorResponse { error: "Write error".to_string(), details: Some(e.to_string()) })) (
StatusCode::INTERNAL_SERVER_ERROR,
Json(ErrorResponse {
error: "Write error".to_string(),
details: Some(e.to_string()),
}),
)
})?; })?;
info!("Post created/updated: {}", payload.slug); info!("Post created/updated: {}", payload.slug);
@@ -210,13 +241,22 @@ async fn delete_post(
warn!("Post not found for deletion: {}", slug); warn!("Post not found for deletion: {}", slug);
return Err(( return Err((
StatusCode::NOT_FOUND, StatusCode::NOT_FOUND,
Json(ErrorResponse { error: "Post not found".to_string(), details: None }), Json(ErrorResponse {
error: "Post not found".to_string(),
details: None,
}),
)); ));
} }
fs::remove_file(file_path).map_err(|e| { fs::remove_file(file_path).map_err(|e| {
error!("Delete error for post {}: {}", slug, e); error!("Delete error for post {}: {}", slug, e);
(StatusCode::INTERNAL_SERVER_ERROR, Json(ErrorResponse { error: "Delete error".to_string(), details: Some(e.to_string()) })) (
StatusCode::INTERNAL_SERVER_ERROR,
Json(ErrorResponse {
error: "Delete error".to_string(),
details: Some(e.to_string()),
}),
)
})?; })?;
info!("Post deleted: {}", slug); info!("Post deleted: {}", slug);
@@ -289,7 +329,10 @@ async fn get_post(
Ok(content) => Ok(Json(PostDetail { slug, content })), Ok(content) => Ok(Json(PostDetail { slug, content })),
Err(_) => Err(( Err(_) => Err((
StatusCode::NOT_FOUND, StatusCode::NOT_FOUND,
Json(ErrorResponse { error: "Post not found".to_string(), details: None }), Json(ErrorResponse {
error: "Post not found".to_string(),
details: None,
}),
)), )),
} }
} }
@@ -334,19 +377,36 @@ async fn upload_file(
file_path file_path
}; };
let final_name_str = final_path.file_name().unwrap().to_str().unwrap().to_string(); let final_name_str = final_path
.file_name()
.unwrap()
.to_str()
.unwrap()
.to_string();
let data = match field.bytes().await { let data = match field.bytes().await {
Ok(bytes) => bytes, Ok(bytes) => bytes,
Err(e) => { Err(e) => {
error!("Failed to read multipart bytes: {}", e); error!("Failed to read multipart bytes: {}", e);
return Err((StatusCode::BAD_REQUEST, Json(ErrorResponse { error: "Read error".to_string(), details: Some(e.to_string()) }))); return Err((
StatusCode::BAD_REQUEST,
Json(ErrorResponse {
error: "Read error".to_string(),
details: Some(e.to_string()),
}),
));
} }
}; };
if let Err(e) = fs::write(&final_path, &data) { if let Err(e) = fs::write(&final_path, &data) {
error!("Failed to write file to {:?}: {}", final_path, e); error!("Failed to write file to {:?}: {}", final_path, e);
return Err((StatusCode::INTERNAL_SERVER_ERROR, Json(ErrorResponse { error: "Write error".to_string(), details: Some(e.to_string()) }))); return Err((
StatusCode::INTERNAL_SERVER_ERROR,
Json(ErrorResponse {
error: "Write error".to_string(),
details: Some(e.to_string()),
}),
));
} }
info!("File uploaded successfully to {:?}", final_path); info!("File uploaded successfully to {:?}", final_path);
@@ -358,6 +418,9 @@ async fn upload_file(
warn!("Upload failed: no file found in multipart stream"); warn!("Upload failed: no file found in multipart stream");
Err(( Err((
StatusCode::BAD_REQUEST, StatusCode::BAD_REQUEST,
Json(ErrorResponse { error: "No file found".to_string(), details: None }), Json(ErrorResponse {
error: "No file found".to_string(),
details: None,
}),
)) ))
} }