chore: code cleanup - formatting, EOF newlines, model helper, and UI tweaks

This commit is contained in:
xfy 2026-06-02 17:25:58 +08:00
parent 1950646bef
commit 9c5b09a278
10 changed files with 85 additions and 70 deletions

View File

@ -42,6 +42,8 @@ RUST_LOG=info
Run migrations before first dev server start:
```bash
./migrate.sh # preferred: auto-creates DB, runs all migrations in order
# or manually:
psql $DATABASE_URL -f migrations/001_init.sql
```

View File

@ -38,19 +38,16 @@ fn validate_password(password: &str) -> Result<(), String> {
#[cfg(feature = "server")]
fn parse_session_token(cookie_header: &str) -> Option<&str> {
cookie_header
.split(';')
.map(|s| s.trim())
.find_map(|pair| {
let mut parts = pair.splitn(2, '=');
let name = parts.next()?.trim();
let value = parts.next()?.trim();
if name == "session" {
Some(value)
} else {
None
}
})
cookie_header.split(';').map(|s| s.trim()).find_map(|pair| {
let mut parts = pair.splitn(2, '=');
let name = parts.next()?.trim();
let value = parts.next()?.trim();
if name == "session" {
Some(value)
} else {
None
}
})
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
@ -88,13 +85,10 @@ pub async fn register(
});
}
let client = DB_POOL
.get()
.await
.map_err(|e| {
tracing::error!("Register DB connection failed: {:?}", e);
ServerFnError::new(format!("数据库连接失败: {}", e))
})?;
let client = DB_POOL.get().await.map_err(|e| {
tracing::error!("Register DB connection failed: {:?}", e);
ServerFnError::new(format!("数据库连接失败: {}", e))
})?;
let admin_count: i64 = client
.query_one("SELECT COUNT(*) FROM users WHERE role = 'admin'", &[])
@ -113,11 +107,10 @@ pub async fn register(
});
}
let password_hash = password::hash_password(&password)
.map_err(|e| {
tracing::error!("Register password hash failed: {:?}", e);
ServerFnError::new(format!("密码哈希失败: {}", e))
})?;
let password_hash = password::hash_password(&password).map_err(|e| {
tracing::error!("Register password hash failed: {:?}", e);
ServerFnError::new(format!("密码哈希失败: {}", e))
})?;
let result = client
.query_one(
@ -148,17 +141,11 @@ pub async fn register(
}
#[server(Login, "/api")]
pub async fn login(
username: String,
password: String,
) -> Result<AuthResponse, ServerFnError> {
let client = DB_POOL
.get()
.await
.map_err(|e| {
tracing::error!("Login DB connection failed: {:?}", e);
ServerFnError::new(format!("数据库连接失败: {}", e))
})?;
pub async fn login(username: String, password: String) -> Result<AuthResponse, ServerFnError> {
let client = DB_POOL.get().await.map_err(|e| {
tracing::error!("Login DB connection failed: {:?}", e);
ServerFnError::new(format!("数据库连接失败: {}", e))
})?;
let row = match client
.query_opt(
@ -182,11 +169,10 @@ pub async fn login(
};
let password_hash: String = row.get("password_hash");
let valid = password::verify_password(&password, &password_hash)
.map_err(|e| {
tracing::error!("Login password verify failed: {:?}", e);
ServerFnError::new(format!("密码验证失败: {}", e))
})?;
let valid = password::verify_password(&password, &password_hash).map_err(|e| {
tracing::error!("Login password verify failed: {:?}", e);
ServerFnError::new(format!("密码验证失败: {}", e))
})?;
if !valid {
return Ok(AuthResponse {
@ -242,21 +228,16 @@ pub async fn logout() -> Result<AuthResponse, ServerFnError> {
None
};
let client = DB_POOL
.get()
.await
.map_err(|e| {
tracing::error!("Logout DB connection failed: {:?}", e);
ServerFnError::new(format!("数据库连接失败: {}", e))
})?;
let client = DB_POOL.get().await.map_err(|e| {
tracing::error!("Logout DB connection failed: {:?}", e);
ServerFnError::new(format!("数据库连接失败: {}", e))
})?;
// 清除 cookie
if let Some(ctx) = dioxus::fullstack::FullstackContext::current() {
ctx.add_response_header(
SET_COOKIE,
HeaderValue::from_static(
"session=; HttpOnly; Path=/; Max-Age=0; SameSite=Lax",
),
HeaderValue::from_static("session=; HttpOnly; Path=/; Max-Age=0; SameSite=Lax"),
);
}
@ -301,13 +282,10 @@ pub async fn get_current_user() -> Result<CurrentUserResponse, ServerFnError> {
return Ok(CurrentUserResponse { user: None });
};
let client = DB_POOL
.get()
.await
.map_err(|e| {
tracing::error!("GetCurrentUser DB connection failed: {:?}", e);
ServerFnError::new(format!("数据库连接失败: {}", e))
})?;
let client = DB_POOL.get().await.map_err(|e| {
tracing::error!("GetCurrentUser DB connection failed: {:?}", e);
ServerFnError::new(format!("数据库连接失败: {}", e))
})?;
let row = client
.query_opt(

View File

@ -1,2 +1,2 @@
pub mod auth;
pub mod posts;
pub mod posts;

View File

@ -40,9 +40,13 @@ fn main() {
.serve_dioxus_application(config, router::AppRouter)
.layer(
TraceLayer::new_for_http()
.make_span_with(tower_http::trace::DefaultMakeSpan::new().level(Level::INFO))
.make_span_with(
tower_http::trace::DefaultMakeSpan::new().level(Level::INFO),
)
.on_request(tower_http::trace::DefaultOnRequest::new().level(Level::INFO))
.on_response(tower_http::trace::DefaultOnResponse::new().level(Level::INFO)),
.on_response(
tower_http::trace::DefaultOnResponse::new().level(Level::INFO),
),
);
Ok(router)

View File

@ -1,2 +1,2 @@
pub mod post;
pub mod user;
pub mod user;

View File

@ -42,6 +42,14 @@ pub struct Post {
pub tags: Vec<String>,
}
impl Post {
pub fn formatted_date(&self) -> String {
self.published_at
.map(|d| d.format("%Y-%m-%d").to_string())
.unwrap_or_else(|| self.created_at.format("%Y-%m-%d").to_string())
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Tag {
pub id: i32,

View File

@ -4,4 +4,4 @@ pub mod write;
pub use dashboard::Admin;
pub use posts::Posts;
pub use write::Write;
pub use write::Write;

View File

@ -38,7 +38,7 @@ pub fn Posts() -> Element {
thead {
tr { class: "border-b border-gray-200 dark:border-[#333] text-left text-gray-500 dark:text-[#9b9c9d]",
th { class: "px-4 py-3 font-medium", "标题" }
th { class: "px-4 py-3 font-medium w-24", "状态" }
th { class: "px-4 py-3 font-medium w-24 text-center", "状态" }
th { class: "px-4 py-3 font-medium w-32", "日期" }
th { class: "px-4 py-3 font-medium w-24 text-right", "操作" }
}
@ -107,9 +107,15 @@ fn PostRow(post: Post, deleting: bool, on_delete: EventHandler<i32>) -> Element
.unwrap_or_else(|| post.created_at.format("%Y-%m-%d").to_string());
let (status_label, status_class) = if post.status == PostStatus::Published {
("已发布", "bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300")
(
"已发布",
"bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300",
)
} else {
("草稿", "bg-gray-100 dark:bg-[#333] text-gray-600 dark:text-[#9b9c9d]")
(
"草稿",
"bg-gray-100 dark:bg-[#333] text-gray-600 dark:text-[#9b9c9d]",
)
};
rsx! {
@ -125,7 +131,7 @@ fn PostRow(post: Post, deleting: bool, on_delete: EventHandler<i32>) -> Element
"{post.title}"
}
}
td { class: "px-4 py-3",
td { class: "px-4 py-3 text-center",
span { class: "inline-flex items-center px-2 py-0.5 rounded text-xs font-medium {status_class}",
"{status_label}"
}

View File

@ -120,7 +120,16 @@ pub fn Write() -> Element {
error.set(None);
spawn(async move {
match create_post(title().trim().to_string(), slug_opt, summary_opt, md, status(), tags_list).await {
match create_post(
title().trim().to_string(),
slug_opt,
summary_opt,
md,
status(),
tags_list,
)
.await
{
Ok(CreatePostResponse { success: true, .. }) => {
saving.set(false);
success.set(true);
@ -131,7 +140,11 @@ pub fn Write() -> Element {
}
let _ = dioxus::router::navigator().push("/admin");
}
Ok(CreatePostResponse { success: false, message, .. }) => {
Ok(CreatePostResponse {
success: false,
message,
..
}) => {
saving.set(false);
error.set(Some(message));
}

View File

@ -33,7 +33,11 @@ pub fn Register() -> Element {
Ok(AuthResponse { success: true, .. }) => {
success.set(true);
}
Ok(AuthResponse { success: false, message, .. }) => {
Ok(AuthResponse {
success: false,
message,
..
}) => {
error.set(Some(message));
}
Err(e) => {