Skip to main content

yggdrasil/api/
settings.rs

1//! 回收站配置接口:读取与更新自动清理设置。
2//!
3//! 所有接口需要 admin 权限。配置持久化到 settings 键值表。
4//! Dioxus server function,注册在 `/api` 路径下。
5
6// 与 posts 模块一致:Dioxus `#[server]` 宏触发 deprecated/unit 提示,按项目惯例放行。
7#![allow(clippy::unused_unit, deprecated)]
8
9use dioxus::prelude::*;
10
11#[cfg(feature = "server")]
12use crate::api::auth::get_current_admin_user;
13#[cfg(feature = "server")]
14use crate::api::error::AppError;
15#[cfg(feature = "server")]
16use crate::db::pool::get_conn;
17use crate::models::settings::TrashSettings;
18
19/// 读取回收站配置。
20///
21/// settings 表缺失键时回退到默认值,保证向后兼容。
22#[server(GetTrashSettings, "/api")]
23pub async fn get_trash_settings() -> Result<TrashSettings, ServerFnError> {
24    let _user = get_current_admin_user().await?;
25
26    #[cfg(feature = "server")]
27    {
28        let client = get_conn().await.map_err(AppError::db_conn)?;
29
30        let enabled: bool = client
31            .query_opt(
32                "SELECT value FROM settings WHERE key = 'trash_auto_purge_enabled'",
33                &[],
34            )
35            .await
36            .map_err(AppError::query)?
37            .and_then(|r| r.get::<_, String>("value").parse().ok())
38            .unwrap_or(crate::models::settings::DEFAULT_AUTO_PURGE_ENABLED);
39
40        let days: i32 = client
41            .query_opt(
42                "SELECT value FROM settings WHERE key = 'trash_retention_days'",
43                &[],
44            )
45            .await
46            .map_err(AppError::query)?
47            .and_then(|r| r.get::<_, String>("value").parse().ok())
48            .unwrap_or(crate::models::settings::DEFAULT_RETENTION_DAYS);
49
50        Ok(TrashSettings {
51            auto_purge_enabled: enabled,
52            retention_days: TrashSettings::clamp_retention(days),
53        })
54    }
55
56    #[cfg(not(feature = "server"))]
57    {
58        Ok(TrashSettings::default())
59    }
60}
61
62/// 更新回收站配置。
63///
64/// retention_days 会被 clamp 到合法范围后写入。
65#[server(UpdateTrashSettings, "/api")]
66pub async fn update_trash_settings(
67    auto_purge_enabled: bool,
68    retention_days: i32,
69) -> Result<TrashSettings, ServerFnError> {
70    let _user = get_current_admin_user().await?;
71
72    let retention_days = TrashSettings::clamp_retention(retention_days);
73
74    #[cfg(feature = "server")]
75    {
76        let client = get_conn().await.map_err(AppError::db_conn)?;
77
78        // UPSERT 两个键。
79        client
80            .execute(
81                "INSERT INTO settings (key, value, updated_at) VALUES ('trash_auto_purge_enabled', $1, NOW())
82                 ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = NOW()",
83                &[&auto_purge_enabled.to_string()],
84            )
85            .await
86            .map_err(AppError::query)?;
87
88        client
89            .execute(
90                "INSERT INTO settings (key, value, updated_at) VALUES ('trash_retention_days', $1, NOW())
91                 ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = NOW()",
92                &[&retention_days.to_string()],
93            )
94            .await
95            .map_err(AppError::query)?;
96
97        tracing::info!(
98            "Trash settings updated: auto_purge={}, retention_days={}",
99            auto_purge_enabled,
100            retention_days
101        );
102    }
103
104    Ok(TrashSettings {
105        auto_purge_enabled,
106        retention_days,
107    })
108}