Skip to main content

yggdrasil/api/settings/
image_cache.rs

1// 与 posts 模块一致:Leptos `#[server]` 宏触发 deprecated/unit/too_many_arguments
2// 提示,按项目惯例放行(限流/运行器等配置项天然参数多)。
3#![allow(clippy::unused_unit, deprecated, clippy::too_many_arguments)]
4
5use crate::api::error::ApiError;
6use leptos::prelude::*;
7
8#[cfg(feature = "server")]
9use crate::api::auth::get_current_admin_user;
10#[cfg(feature = "server")]
11use crate::api::error::AppError;
12#[cfg(feature = "server")]
13use crate::db::pool::get_conn;
14use crate::models::settings::ImageCacheSettings;
15// 仅 server 构建的函数体引用(WASM 端 server fn 体被宏剥离)。
16#[cfg(feature = "server")]
17use crate::cache::invalidate_image_cache_settings;
18
19// ============================================================================
20// 图片磁盘缓存配置(即时生效)
21// ============================================================================
22
23/// 启动时用 `IMAGE_DISK_CACHE_MAX_MB` / `_MAX_AGE_HOURS` 环境变量播种。
24///
25/// 语义与 [`seed_backup_settings_from_env`][crate::api::settings::seed_backup_settings_from_env] 一致。
26#[cfg(feature = "server")]
27pub(crate) async fn seed_image_cache_settings_from_env(
28    client: &tokio_postgres::Client,
29) -> Result<(), AppError> {
30    let mut seeds: Vec<(&'static str, String)> = Vec::new();
31
32    if let Ok(v) = std::env::var("IMAGE_DISK_CACHE_MAX_MB") {
33        match v.trim().parse::<u32>() {
34            Ok(n) => seeds.push((
35                "image_disk_cache_max_mb",
36                crate::models::settings::ImageCacheSettings::clamp_max_mb(n).to_string(),
37            )),
38            Err(_) => tracing::warn!("IMAGE_DISK_CACHE_MAX_MB={v:?} 非法(期望正整数),跳过"),
39        }
40    }
41    if let Ok(v) = std::env::var("IMAGE_DISK_CACHE_MAX_AGE_HOURS") {
42        match v.trim().parse::<u32>() {
43            Ok(n) => seeds.push((
44                "image_disk_cache_max_age_hours",
45                crate::models::settings::ImageCacheSettings::clamp_max_age_hours(n).to_string(),
46            )),
47            Err(_) => {
48                tracing::warn!("IMAGE_DISK_CACHE_MAX_AGE_HOURS={v:?} 非法(期望正整数),跳过")
49            }
50        }
51    }
52
53    super::insert_env_seeds(client, seeds).await
54}
55
56/// 从 settings 表读取图片磁盘缓存配置(缺键回退默认值)。
57#[cfg(feature = "server")]
58pub(crate) async fn load_image_cache_settings(
59    client: &tokio_postgres::Client,
60) -> Result<crate::models::settings::ImageCacheSettings, AppError> {
61    let values = super::load_setting_values(
62        client,
63        &["image_disk_cache_max_mb", "image_disk_cache_max_age_hours"],
64    )
65    .await?;
66
67    let disk_cache_max_mb = values
68        .get("image_disk_cache_max_mb")
69        .and_then(|v| v.parse().ok())
70        .map(crate::models::settings::ImageCacheSettings::clamp_max_mb)
71        .unwrap_or(crate::models::settings::DEFAULT_IMAGE_DISK_CACHE_MAX_MB);
72    let disk_cache_max_age_hours = values
73        .get("image_disk_cache_max_age_hours")
74        .and_then(|v| v.parse().ok())
75        .map(crate::models::settings::ImageCacheSettings::clamp_max_age_hours)
76        .unwrap_or(crate::models::settings::DEFAULT_IMAGE_DISK_CACHE_MAX_AGE_HOURS);
77
78    Ok(crate::models::settings::ImageCacheSettings {
79        disk_cache_max_mb,
80        disk_cache_max_age_hours,
81    })
82}
83
84/// 运行时读取图片磁盘缓存配置:先查 moka 缓存,未命中查 DB 并回填。
85#[cfg(feature = "server")]
86pub(crate) async fn runtime_image_cache_settings() -> crate::models::settings::ImageCacheSettings {
87    if let Some(s) = crate::cache::get_image_cache_settings().await {
88        return s;
89    }
90    let fallback = crate::models::settings::ImageCacheSettings::default();
91    // 无 DATABASE_URL 时(如单元测试环境)直接回退默认值,
92    // 避免触发 DB_POOL LazyLock 的防御性 panic。
93    if std::env::var("DATABASE_URL").is_err() {
94        return fallback;
95    }
96    match get_conn().await {
97        Ok(client) => match load_image_cache_settings(&client).await {
98            Ok(s) => {
99                crate::cache::set_image_cache_settings(s.clone()).await;
100                s
101            }
102            Err(e) => {
103                tracing::warn!("读取图片缓存配置失败,回退默认值:{e:?}");
104                fallback
105            }
106        },
107        Err(e) => {
108            tracing::warn!("获取连接读取图片缓存配置失败,回退默认值:{e:?}");
109            fallback
110        }
111    }
112}
113
114/// 读取图片磁盘缓存配置(面板用)。
115#[server(GetImageCacheSettings, endpoint = "get_image_cache_settings", input = leptos::server_fn::codec::Json)]
116pub async fn get_image_cache_settings() -> Result<ImageCacheSettings, ApiError> {
117    let _user = get_current_admin_user().await?;
118
119    #[cfg(feature = "server")]
120    {
121        let client = get_conn().await.map_err(AppError::db_conn)?;
122        let s = load_image_cache_settings(&client)
123            .await
124            .map_err(<ApiError>::from)?;
125        crate::cache::set_image_cache_settings(s.clone()).await;
126        Ok(s)
127    }
128
129    #[cfg(not(feature = "server"))]
130    {
131        Ok(ImageCacheSettings::default())
132    }
133}
134
135/// 更新图片磁盘缓存配置。
136#[server(UpdateImageCacheSettings, endpoint = "update_image_cache_settings", input = leptos::server_fn::codec::Json)]
137pub async fn update_image_cache_settings(
138    disk_cache_max_mb: u32,
139    disk_cache_max_age_hours: u32,
140) -> Result<ImageCacheSettings, ApiError> {
141    let _user = get_current_admin_user().await?;
142
143    let disk_cache_max_mb = ImageCacheSettings::clamp_max_mb(disk_cache_max_mb);
144    let disk_cache_max_age_hours =
145        ImageCacheSettings::clamp_max_age_hours(disk_cache_max_age_hours);
146
147    #[cfg(feature = "server")]
148    {
149        let client = get_conn().await.map_err(AppError::db_conn)?;
150
151        super::upsert_settings(
152            &**client,
153            &[
154                ("image_disk_cache_max_mb", disk_cache_max_mb.to_string()),
155                (
156                    "image_disk_cache_max_age_hours",
157                    disk_cache_max_age_hours.to_string(),
158                ),
159            ],
160        )
161        .await?;
162
163        invalidate_image_cache_settings();
164        tracing::info!(
165            "Image cache settings updated: max_mb={}, max_age_hours={}",
166            disk_cache_max_mb,
167            disk_cache_max_age_hours
168        );
169    }
170
171    Ok(ImageCacheSettings {
172        disk_cache_max_mb,
173        disk_cache_max_age_hours,
174    })
175}