chore: code cleanup - formatting, EOF newlines, model helper, and UI tweaks
This commit is contained in:
parent
1950646bef
commit
9c5b09a278
@ -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
|
||||
```
|
||||
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
pub mod auth;
|
||||
pub mod posts;
|
||||
pub mod posts;
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
pub mod post;
|
||||
pub mod user;
|
||||
pub mod user;
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -4,4 +4,4 @@ pub mod write;
|
||||
|
||||
pub use dashboard::Admin;
|
||||
pub use posts::Posts;
|
||||
pub use write::Write;
|
||||
pub use write::Write;
|
||||
|
||||
@ -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}"
|
||||
}
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
@ -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) => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user