添加tracing日志并重构服务器启动逻辑

This commit is contained in:
李林军 2026-05-27 11:39:09 +08:00
parent 9c834ba1df
commit 4093178fc3
16 changed files with 124 additions and 66 deletions

View File

@ -1 +1,2 @@
DATABASE_URL=postgres://postgres:postgres@localhost:5432/yggdrasil DATABASE_URL=postgres://postgres:postgres@localhost:5432/yggdrasil
RUST_LOG=info

34
Cargo.lock generated
View File

@ -2436,6 +2436,15 @@ dependencies = [
"jni-sys 0.3.1", "jni-sys 0.3.1",
] ]
[[package]]
name = "nu-ansi-term"
version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
"windows-sys 0.60.2",
]
[[package]] [[package]]
name = "num-conv" name = "num-conv"
version = "0.2.2" version = "0.2.2"
@ -3724,6 +3733,7 @@ dependencies = [
"tower", "tower",
"tower-layer", "tower-layer",
"tower-service", "tower-service",
"tracing",
"url", "url",
] ]
@ -3769,6 +3779,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
dependencies = [ dependencies = [
"once_cell", "once_cell",
"valuable",
] ]
[[package]] [[package]]
@ -3781,6 +3792,17 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "tracing-log"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
dependencies = [
"log",
"once_cell",
"tracing-core",
]
[[package]] [[package]]
name = "tracing-subscriber" name = "tracing-subscriber"
version = "0.3.23" version = "0.3.23"
@ -3788,12 +3810,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319"
dependencies = [ dependencies = [
"matchers", "matchers",
"nu-ansi-term",
"once_cell", "once_cell",
"regex-automata", "regex-automata",
"sharded-slab", "sharded-slab",
"smallvec",
"thread_local", "thread_local",
"tracing", "tracing",
"tracing-core", "tracing-core",
"tracing-log",
] ]
[[package]] [[package]]
@ -3961,6 +3986,12 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "valuable"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.5" version = "0.9.5"
@ -4637,6 +4668,9 @@ dependencies = [
"serde", "serde",
"tokio", "tokio",
"tokio-postgres", "tokio-postgres",
"tower-http",
"tracing",
"tracing-subscriber",
"uuid", "uuid",
"wasm-bindgen", "wasm-bindgen",
"wasm-bindgen-futures", "wasm-bindgen-futures",

View File

@ -15,6 +15,9 @@ chrono = { version = "0.4", features = ["serde"] }
regex = "1.12" regex = "1.12"
pulldown-cmark = "0.13" pulldown-cmark = "0.13"
dotenvy = { version = "0.15", optional = true } dotenvy = { version = "0.15", optional = true }
tracing = { version = "0.1", optional = true }
tracing-subscriber = { version = "0.3", optional = true }
tower-http = { version = "0.6", features = ["trace"], optional = true }
rand = { version = "0.8", features = ["getrandom"] } rand = { version = "0.8", features = ["getrandom"] }
getrandom = { version = "0.2", features = ["js"] } getrandom = { version = "0.2", features = ["js"] }
http = "1" http = "1"
@ -42,4 +45,7 @@ server = [
"dep:tokio-postgres", "dep:tokio-postgres",
"dep:deadpool-postgres", "dep:deadpool-postgres",
"dep:dotenvy", "dep:dotenvy",
"dep:tracing",
"dep:tracing-subscriber",
"dep:tower-http",
] ]

View File

@ -1,9 +1,9 @@
use dioxus::prelude::*; use dioxus::prelude::*;
use crate::api::auth::{get_current_user, logout}; use crate::api::auth::{get_current_user, logout};
use crate::components::header::{Header, NavItemConfig}; use crate::components::admin_skeleton::AdminDashboardSkeleton;
use crate::components::footer::Footer; use crate::components::footer::Footer;
use crate::components::admin_skeleton::{AdminSkeleton, AdminDashboardSkeleton}; use crate::components::header::{Header, NavItemConfig};
use crate::components::write_skeleton::WriteSkeleton; use crate::components::write_skeleton::WriteSkeleton;
use crate::context::UserContext; use crate::context::UserContext;
use crate::router::Route; use crate::router::Route;
@ -39,12 +39,12 @@ pub fn AdminLayout() -> Element {
NavItemConfig { NavItemConfig {
href: "/admin", href: "/admin",
label: "仪表盘", label: "仪表盘",
is_active: matches!(route, Route::AdminPage {}), is_active: matches!(route, Route::Admin {}),
}, },
NavItemConfig { NavItemConfig {
href: "/admin/write", href: "/admin/write",
label: "写文章", label: "写文章",
is_active: matches!(route, Route::WritePage {}), is_active: matches!(route, Route::Write {}),
}, },
NavItemConfig { NavItemConfig {
href: "/", href: "/",
@ -53,15 +53,13 @@ pub fn AdminLayout() -> Element {
}, },
]; ];
let nav = navigator.clone();
let logout_button = rsx! { let logout_button = rsx! {
button { button {
class: "text-sm text-gray-600 dark:text-[#9b9c9d] hover:text-gray-900 dark:hover:text-[#dadadb] transition-colors", class: "text-sm text-gray-600 dark:text-[#9b9c9d] hover:text-gray-900 dark:hover:text-[#dadadb] transition-colors",
onclick: move |_| { onclick: move |_| {
let nav = nav.clone();
spawn(async move { spawn(async move {
let _ = logout().await; let _ = logout().await;
let _ = nav.push("/login"); let _ = navigator.push("/login");
}); });
}, },
"登出" "登出"
@ -94,7 +92,7 @@ pub fn AdminLayout() -> Element {
Header { nav_items: admin_nav_items, right_content: logout_button } Header { nav_items: admin_nav_items, right_content: logout_button }
main { class: "flex-1 w-full max-w-5xl mx-auto px-6 py-8", main { class: "flex-1 w-full max-w-5xl mx-auto px-6 py-8",
{match route { {match route {
Route::WritePage {} => rsx! { WriteSkeleton {} }, Route::Write {} => rsx! { WriteSkeleton {} },
_ => rsx! { AdminDashboardSkeleton {} }, _ => rsx! { AdminDashboardSkeleton {} },
}} }}
} }

View File

@ -2,7 +2,7 @@ use dioxus::prelude::*;
#[component] #[component]
pub fn Footer() -> Element { pub fn Footer() -> Element {
let mut visible = use_signal(|| false); let visible = use_signal(|| false);
use_effect(move || { use_effect(move || {
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]

View File

@ -2,5 +2,4 @@ pub mod header;
pub mod footer; pub mod footer;
pub mod admin_layout; pub mod admin_layout;
pub mod admin_skeleton; pub mod admin_skeleton;
pub use admin_skeleton::{AdminSkeleton, AdminDashboardSkeleton};
pub mod write_skeleton; pub mod write_skeleton;

View File

@ -9,17 +9,37 @@ mod router;
mod tasks; mod tasks;
mod theme; mod theme;
use router::AppRouter;
fn main() { fn main() {
#[cfg(feature = "server")] #[cfg(feature = "server")]
{ {
dotenvy::dotenv().ok(); dotenvy::dotenv().ok();
std::thread::spawn(|| { tracing_subscriber::fmt()
let rt = tokio::runtime::Runtime::new().expect("Failed to create Tokio runtime"); .with_env_filter(
rt.block_on(tasks::session_cleanup::run_cleanup()); tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")),
)
.init();
dioxus::server::serve(|| async move {
use dioxus::server::{axum, DioxusRouterExt, ServeConfig};
use tower_http::trace::TraceLayer;
tokio::spawn(async {
tasks::session_cleanup::run_cleanup().await;
});
let config = ServeConfig::new();
let router = axum::Router::new()
.layer(TraceLayer::new_for_http())
.serve_dioxus_application(config, router::AppRouter);
Ok(router)
}); });
} }
dioxus::launch(AppRouter); #[cfg(not(feature = "server"))]
{
use router::AppRouter;
dioxus::launch(AppRouter);
}
} }

View File

@ -3,7 +3,7 @@ use dioxus::prelude::*;
use crate::pages::home::{Post, POSTS}; use crate::pages::home::{Post, POSTS};
#[component] #[component]
pub fn AdminPage() -> Element { pub fn Admin() -> Element {
rsx! { rsx! {
div { class: "space-y-8", div { class: "space-y-8",
// 统计卡片 // 统计卡片

View File

@ -1,5 +1,5 @@
pub mod dashboard; pub mod dashboard;
pub mod write; pub mod write;
pub use dashboard::AdminPage; pub use dashboard::Admin;
pub use write::WritePage; pub use write::Write;

View File

@ -6,9 +6,9 @@ use wasm_bindgen::JsCast;
use crate::components::write_skeleton::WriteSkeleton; use crate::components::write_skeleton::WriteSkeleton;
#[component] #[component]
pub fn WritePage() -> Element { pub fn Write() -> Element {
let mut title = use_signal(|| "".to_string()); let mut title = use_signal(|| "".to_string());
let mut content = use_signal(|| "".to_string()); let content = use_signal(|| "".to_string());
let mut loading = use_signal(|| true); let mut loading = use_signal(|| true);
// 初始化 Tiptap 编辑器 // 初始化 Tiptap 编辑器

View File

@ -114,14 +114,14 @@ fn group_posts(posts: &[Post]) -> Vec<YearGroup> {
} }
#[component] #[component]
pub fn ArchivesPage() -> Element { pub fn Archives() -> Element {
let route = use_route::<Route>(); let route = use_route::<Route>();
let nav_items = vec![ let nav_items = vec![
NavItemConfig { href: "/", label: "首页", is_active: matches!(route, Route::HomePage {}) }, NavItemConfig { href: "/", label: "首页", is_active: matches!(route, Route::Home {}) },
NavItemConfig { href: "/archives", label: "归档", is_active: matches!(route, Route::ArchivesPage {}) }, NavItemConfig { href: "/archives", label: "归档", is_active: matches!(route, Route::Archives {}) },
NavItemConfig { href: "/tags", label: "标签", is_active: matches!(route, Route::TagsPage {}) || matches!(route, Route::TagDetailPage { .. }) }, NavItemConfig { href: "/tags", label: "标签", is_active: matches!(route, Route::Tags {}) || matches!(route, Route::TagDetail { .. }) },
NavItemConfig { href: "/search", label: "搜索", is_active: matches!(route, Route::SearchPage {}) }, NavItemConfig { href: "/search", label: "搜索", is_active: matches!(route, Route::Search {}) },
NavItemConfig { href: "/about", label: "关于", is_active: matches!(route, Route::AboutPage {}) }, NavItemConfig { href: "/about", label: "关于", is_active: matches!(route, Route::About {}) },
]; ];
let grouped = group_posts(POSTS); let grouped = group_posts(POSTS);

View File

@ -60,14 +60,14 @@ pub const POSTS: &[Post] = &[
]; ];
#[component] #[component]
pub fn HomePage() -> Element { pub fn Home() -> Element {
let route = use_route::<Route>(); let route = use_route::<Route>();
let nav_items = vec![ let nav_items = vec![
NavItemConfig { href: "/", label: "首页", is_active: matches!(route, Route::HomePage {}) }, NavItemConfig { href: "/", label: "首页", is_active: matches!(route, Route::Home {}) },
NavItemConfig { href: "/archives", label: "归档", is_active: matches!(route, Route::ArchivesPage {}) }, NavItemConfig { href: "/archives", label: "归档", is_active: matches!(route, Route::Archives {}) },
NavItemConfig { href: "/tags", label: "标签", is_active: matches!(route, Route::TagsPage {}) || matches!(route, Route::TagDetailPage { .. }) }, NavItemConfig { href: "/tags", label: "标签", is_active: matches!(route, Route::Tags {}) || matches!(route, Route::TagDetail { .. }) },
NavItemConfig { href: "/search", label: "搜索", is_active: matches!(route, Route::SearchPage {}) }, NavItemConfig { href: "/search", label: "搜索", is_active: matches!(route, Route::Search {}) },
NavItemConfig { href: "/about", label: "关于", is_active: matches!(route, Route::AboutPage {}) }, NavItemConfig { href: "/about", label: "关于", is_active: matches!(route, Route::About {}) },
]; ];
rsx! { rsx! {

View File

@ -3,7 +3,7 @@ use dioxus::prelude::*;
use crate::api::auth::{login, AuthResponse}; use crate::api::auth::{login, AuthResponse};
#[component] #[component]
pub fn LoginPage() -> Element { pub fn Login() -> Element {
let mut username = use_signal(|| "".to_string()); let mut username = use_signal(|| "".to_string());
let mut password = use_signal(|| "".to_string()); let mut password = use_signal(|| "".to_string());
let mut error = use_signal(|| None::<String>); let mut error = use_signal(|| None::<String>);

View File

@ -3,7 +3,7 @@ use dioxus::prelude::*;
use crate::api::auth::{register, AuthResponse}; use crate::api::auth::{register, AuthResponse};
#[component] #[component]
pub fn RegisterPage() -> Element { pub fn Register() -> Element {
let mut username = use_signal(|| "".to_string()); let mut username = use_signal(|| "".to_string());
let mut email = use_signal(|| "".to_string()); let mut email = use_signal(|| "".to_string());
let mut password = use_signal(|| "".to_string()); let mut password = use_signal(|| "".to_string());

View File

@ -40,14 +40,14 @@ fn posts_for_tag(tag: &str) -> Vec<Post> {
} }
#[component] #[component]
pub fn TagsPage() -> Element { pub fn Tags() -> Element {
let route = use_route::<Route>(); let route = use_route::<Route>();
let nav_items = vec![ let nav_items = vec![
NavItemConfig { href: "/", label: "首页", is_active: matches!(route, Route::HomePage {}) }, NavItemConfig { href: "/", label: "首页", is_active: matches!(route, Route::Home {}) },
NavItemConfig { href: "/archives", label: "归档", is_active: matches!(route, Route::ArchivesPage {}) }, NavItemConfig { href: "/archives", label: "归档", is_active: matches!(route, Route::Archives {}) },
NavItemConfig { href: "/tags", label: "标签", is_active: matches!(route, Route::TagsPage {}) || matches!(route, Route::TagDetailPage { .. }) }, NavItemConfig { href: "/tags", label: "标签", is_active: matches!(route, Route::Tags {}) || matches!(route, Route::TagDetail { .. }) },
NavItemConfig { href: "/search", label: "搜索", is_active: matches!(route, Route::SearchPage {}) }, NavItemConfig { href: "/search", label: "搜索", is_active: matches!(route, Route::Search {}) },
NavItemConfig { href: "/about", label: "关于", is_active: matches!(route, Route::AboutPage {}) }, NavItemConfig { href: "/about", label: "关于", is_active: matches!(route, Route::About {}) },
]; ];
let tags = collect_tags(); let tags = collect_tags();
@ -92,14 +92,14 @@ pub fn TagsPage() -> Element {
} }
#[component] #[component]
pub fn TagDetailPage(tag: String) -> Element { pub fn TagDetail(tag: String) -> Element {
let route = use_route::<Route>(); let route = use_route::<Route>();
let nav_items = vec![ let nav_items = vec![
NavItemConfig { href: "/", label: "首页", is_active: matches!(route, Route::HomePage {}) }, NavItemConfig { href: "/", label: "首页", is_active: matches!(route, Route::Home {}) },
NavItemConfig { href: "/archives", label: "归档", is_active: matches!(route, Route::ArchivesPage {}) }, NavItemConfig { href: "/archives", label: "归档", is_active: matches!(route, Route::Archives {}) },
NavItemConfig { href: "/tags", label: "标签", is_active: matches!(route, Route::TagsPage {}) || matches!(route, Route::TagDetailPage { .. }) }, NavItemConfig { href: "/tags", label: "标签", is_active: matches!(route, Route::Tags {}) || matches!(route, Route::TagDetail { .. }) },
NavItemConfig { href: "/search", label: "搜索", is_active: matches!(route, Route::SearchPage {}) }, NavItemConfig { href: "/search", label: "搜索", is_active: matches!(route, Route::Search {}) },
NavItemConfig { href: "/about", label: "关于", is_active: matches!(route, Route::AboutPage {}) }, NavItemConfig { href: "/about", label: "关于", is_active: matches!(route, Route::About {}) },
]; ];
let posts = posts_for_tag(&tag); let posts = posts_for_tag(&tag);

View File

@ -3,43 +3,43 @@ use std::sync::Arc;
use crate::components::admin_layout::AdminLayout; use crate::components::admin_layout::AdminLayout;
use crate::context::UserContext; use crate::context::UserContext;
use crate::pages::admin::{AdminPage, WritePage}; use crate::pages::admin::{Admin, Write};
use crate::pages::archives::ArchivesPage; use crate::pages::archives::Archives;
use crate::pages::home::HomePage; use crate::pages::home::Home;
use crate::pages::login::LoginPage; use crate::pages::login::Login;
use crate::pages::register::RegisterPage; use crate::pages::register::Register;
use crate::pages::tags::{TagsPage, TagDetailPage}; use crate::pages::tags::{TagDetail, Tags};
use crate::theme::{Theme, ThemePreload, use_theme_provider}; use crate::theme::{use_theme_provider, Theme, ThemePreload};
#[derive(Clone, Routable, Debug, PartialEq)] #[derive(Clone, Routable, Debug, PartialEq)]
#[rustfmt::skip] #[rustfmt::skip]
pub enum Route { pub enum Route {
#[route("/")] #[route("/")]
HomePage {}, Home {},
#[route("/login")] #[route("/login")]
LoginPage {}, Login {},
#[route("/register")] #[route("/register")]
RegisterPage {}, Register {},
#[nest("/admin")] #[nest("/admin")]
#[layout(AdminLayout)] #[layout(AdminLayout)]
#[route("/")] #[route("/")]
AdminPage {}, Admin {},
#[route("/write")] #[route("/write")]
WritePage {}, Write {},
#[end_layout] #[end_layout]
#[end_nest] #[end_nest]
#[route("/archives")] #[route("/archives")]
ArchivesPage {}, Archives {},
#[route("/tags")] #[route("/tags")]
TagsPage {}, Tags {},
#[route("/tags/:tag")] #[route("/tags/:tag")]
TagDetailPage { tag: String }, TagDetail { tag: String },
#[route("/search")] #[route("/search")]
SearchPage {}, Search {},
#[route("/about")] #[route("/about")]
AboutPage {}, About {},
} }
#[component] #[component]
@ -64,11 +64,11 @@ pub fn AppRouter() -> Element {
} }
#[component] #[component]
pub fn SearchPage() -> Element { pub fn Search() -> Element {
rsx! { "Search" } rsx! { "Search" }
} }
#[component] #[component]
pub fn AboutPage() -> Element { pub fn About() -> Element {
rsx! { "About" } rsx! { "About" }
} }