refactor: integrate tags module into posts.rs and mod.rs

This commit is contained in:
xfy 2026-06-08 16:45:20 +08:00
parent 441060e7c2
commit 32131377c3
3 changed files with 132 additions and 86 deletions

129
Cargo.lock generated
View File

@ -1834,6 +1834,16 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "forwarded-header-value"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8835f84f38484cc86f110a805655697908257fb9a7af005234060891557198e9"
dependencies = [
"nonempty",
"thiserror 1.0.69",
]
[[package]]
name = "futf"
version = "0.1.5"
@ -1915,6 +1925,12 @@ version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
[[package]]
name = "futures-timer"
version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af43fadb8a98512d547e37b4e92e0ced13e205c061b87b4623eff01d918d6968"
[[package]]
name = "futures-util"
version = "0.3.32"
@ -2058,6 +2074,29 @@ dependencies = [
"web-sys",
]
[[package]]
name = "governor"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be93b4ec2e4710b04d9264c0c7350cdd62a8c20e5e4ac732552ebb8f0debe8eb"
dependencies = [
"cfg-if",
"dashmap",
"futures-sink",
"futures-timer",
"futures-util",
"getrandom 0.3.4",
"no-std-compat",
"nonzero_ext",
"parking_lot",
"portable-atomic",
"quanta",
"rand 0.9.4",
"smallvec",
"spinning_top",
"web-time",
]
[[package]]
name = "h2"
version = "0.4.14"
@ -3028,6 +3067,12 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
[[package]]
name = "no-std-compat"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c"
[[package]]
name = "no_std_io2"
version = "0.9.4"
@ -3046,6 +3091,18 @@ dependencies = [
"memchr",
]
[[package]]
name = "nonempty"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7"
[[package]]
name = "nonzero_ext"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21"
[[package]]
name = "noop_proc_macro"
version = "0.3.0"
@ -3544,6 +3601,21 @@ dependencies = [
"bytemuck",
]
[[package]]
name = "quanta"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7"
dependencies = [
"crossbeam-utils",
"libc",
"once_cell",
"raw-cpuid",
"wasi 0.11.1+wasi-snapshot-preview1",
"web-sys",
"winapi",
]
[[package]]
name = "quick-error"
version = "2.0.1"
@ -3761,6 +3833,15 @@ dependencies = [
"rgb",
]
[[package]]
name = "raw-cpuid"
version = "11.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186"
dependencies = [
"bitflags",
]
[[package]]
name = "raw-window-handle"
version = "0.6.2"
@ -4245,6 +4326,15 @@ version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
name = "spinning_top"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300"
dependencies = [
"lock_api",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.1"
@ -4731,6 +4821,22 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
[[package]]
name = "tower_governor"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84e6672c7510df74859726427edea641674dad1aeeb30057b87335b1ba23b843"
dependencies = [
"axum",
"forwarded-header-value",
"governor",
"http",
"pin-project",
"thiserror 2.0.18",
"tower",
"tracing",
]
[[package]]
name = "tracing"
version = "0.1.44"
@ -5236,6 +5342,22 @@ dependencies = [
"web-sys",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.11"
@ -5245,6 +5367,12 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
version = "0.62.2"
@ -5701,6 +5829,7 @@ dependencies = [
"tokio",
"tokio-postgres",
"tower-http",
"tower_governor",
"tracing",
"tracing-subscriber",
"uuid",

View File

@ -1,5 +1,6 @@
pub mod auth;
pub mod image;
pub mod posts;
pub mod tags;
pub mod upload;
pub mod utils;

View File

@ -2,6 +2,8 @@
use dioxus::prelude::*;
#[cfg(feature = "server")]
use crate::api::tags::get_post_tags;
#[cfg(feature = "server")]
use crate::api::utils::{db_conn_error, query_error, tx_error};
#[cfg(feature = "server")]
@ -386,92 +388,6 @@ fn slugify_heading(text: &str) -> String {
slug
}
// ============================================================================
// Tag helpers
// ============================================================================
#[cfg(feature = "server")]
#[allow(dead_code)]
async fn set_post_tags(
client: &tokio_postgres::Client,
post_id: i32,
tags: &[String],
) -> Result<(), ServerFnError> {
// Remove existing tags
client
.execute("DELETE FROM post_tags WHERE post_id = $1", &[&post_id])
.await
.map_err(|e| {
tracing::error!("delete tag links failed: {:?}", e);
ServerFnError::new(format!("删除标签关联失败: {}", e))
})?;
for tag_name in tags {
let tag_name = tag_name.trim();
if tag_name.is_empty() {
continue;
}
// Insert or get tag
let tag_id: i32 = {
let row = client
.query_opt(
"INSERT INTO tags (name) VALUES ($1) ON CONFLICT (name) DO NOTHING RETURNING id",
&[&tag_name],
)
.await
.map_err(|e| {
tracing::error!("create tag failed: {:?}", e);
ServerFnError::new(format!("创建标签失败: {}", e))
})?;
match row {
Some(r) => r.get(0),
None => {
// Tag already exists, fetch its id
let row = client
.query_opt("SELECT id FROM tags WHERE name = $1", &[&tag_name])
.await
.map_err(|e| {
tracing::error!("query tag failed: {:?}", e);
ServerFnError::new(format!("查询标签失败: {}", e))
})?;
row.map(|r| r.get(0))
.ok_or_else(|| ServerFnError::new(format!("标签不存在: {}", tag_name)))?
}
}
};
client
.execute(
"INSERT INTO post_tags (post_id, tag_id) VALUES ($1, $2)",
&[&post_id, &tag_id],
)
.await
.map_err(|e| {
tracing::error!("link tag failed: {:?}", e);
ServerFnError::new(format!("关联标签失败: {}", e))
})?;
}
Ok(())
}
#[cfg(feature = "server")]
async fn get_post_tags(client: &tokio_postgres::Client, post_id: i32) -> Vec<String> {
let rows = client
.query(
"SELECT t.name FROM tags t JOIN post_tags pt ON t.id = pt.tag_id WHERE pt.post_id = $1 ORDER BY t.name",
&[&post_id],
)
.await;
match rows {
Ok(rows) => rows.iter().map(|r| r.get(0)).collect(),
Err(_) => vec![],
}
}
// ============================================================================
// Row to Post conversion
// ============================================================================