Skip to main content

yggdrasil/api/settings/
image_limit.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::ImageLimitSettings;
15
16// ============================================================================
17// 图片尺寸限制配置(需重启生效)
18// ============================================================================
19
20/// 启动时用 `MAX_IMAGE_DIMENSION` / `MAX_IMAGE_PIXELS` /
21/// `IMAGE_DIMENSIONS_CACHE_TTL_SECS` 环境变量播种图片尺寸限制配置。
22#[cfg(feature = "server")]
23pub(crate) async fn seed_image_limit_settings_from_env(
24    client: &tokio_postgres::Client,
25) -> Result<(), AppError> {
26    use crate::models::settings as m;
27
28    let mut seeds: Vec<(&'static str, String)> = Vec::new();
29
30    if let Ok(v) = std::env::var("MAX_IMAGE_DIMENSION") {
31        match v.trim().parse::<u32>() {
32            Ok(n) => seeds.push((
33                "image_max_dimension",
34                m::ImageLimitSettings::clamp_max_dimension(n).to_string(),
35            )),
36            Err(_) => tracing::warn!("MAX_IMAGE_DIMENSION={v:?} 非法(期望正整数),跳过"),
37        }
38    }
39    if let Ok(v) = std::env::var("MAX_IMAGE_PIXELS") {
40        match v.trim().parse::<u64>() {
41            Ok(n) => seeds.push((
42                "image_max_pixels",
43                m::ImageLimitSettings::clamp_max_pixels(n).to_string(),
44            )),
45            Err(_) => tracing::warn!("MAX_IMAGE_PIXELS={v:?} 非法(期望正整数),跳过"),
46        }
47    }
48    if let Ok(v) = std::env::var("IMAGE_DIMENSIONS_CACHE_TTL_SECS") {
49        match v.trim().parse::<u64>() {
50            Ok(n) => seeds.push((
51                "image_dimensions_cache_ttl_secs",
52                m::ImageLimitSettings::clamp_dimensions_cache_ttl_secs(n).to_string(),
53            )),
54            Err(_) => {
55                tracing::warn!("IMAGE_DIMENSIONS_CACHE_TTL_SECS={v:?} 非法(期望正整数),跳过")
56            }
57        }
58    }
59
60    super::insert_env_seeds(client, seeds).await
61}
62
63/// 从 settings 表读取图片尺寸限制配置(缺键回退默认值)。
64#[cfg(feature = "server")]
65pub(crate) async fn load_image_limit_settings(
66    client: &tokio_postgres::Client,
67) -> Result<ImageLimitSettings, AppError> {
68    use crate::models::settings as m;
69
70    let values = super::load_setting_values(
71        client,
72        &[
73            "image_max_dimension",
74            "image_max_pixels",
75            "image_dimensions_cache_ttl_secs",
76        ],
77    )
78    .await?;
79    let max_dimension = values
80        .get("image_max_dimension")
81        .and_then(|v| v.parse().ok())
82        .map(m::ImageLimitSettings::clamp_max_dimension)
83        .unwrap_or(m::DEFAULT_IMAGE_MAX_DIMENSION);
84    let max_pixels = values
85        .get("image_max_pixels")
86        .and_then(|v| v.parse().ok())
87        .map(m::ImageLimitSettings::clamp_max_pixels)
88        .unwrap_or(m::DEFAULT_IMAGE_MAX_PIXELS);
89    let dimensions_cache_ttl_secs = values
90        .get("image_dimensions_cache_ttl_secs")
91        .and_then(|v| v.parse().ok())
92        .map(m::ImageLimitSettings::clamp_dimensions_cache_ttl_secs)
93        .unwrap_or(m::DEFAULT_IMAGE_DIMENSIONS_CACHE_TTL_SECS);
94
95    Ok(ImageLimitSettings {
96        max_dimension,
97        max_pixels,
98        dimensions_cache_ttl_secs,
99    })
100}
101
102/// 读取图片尺寸限制配置(面板用)。
103#[server(GetImageLimitSettings, endpoint = "get_image_limit_settings", input = leptos::server_fn::codec::Json)]
104pub async fn get_image_limit_settings() -> Result<ImageLimitSettings, ApiError> {
105    let _user = get_current_admin_user().await?;
106
107    #[cfg(feature = "server")]
108    {
109        let client = get_conn().await.map_err(AppError::db_conn)?;
110        let s = load_image_limit_settings(&client)
111            .await
112            .map_err(<ApiError>::from)?;
113        Ok(s)
114    }
115
116    #[cfg(not(feature = "server"))]
117    {
118        Ok(ImageLimitSettings::default())
119    }
120}
121
122/// 更新图片尺寸限制配置。
123///
124/// 字段会被 clamp 后写入 DB。配置烘焙进 LazyLock,修改后需**重启进程**生效。
125#[server(UpdateImageLimitSettings, endpoint = "update_image_limit_settings", input = leptos::server_fn::codec::Json)]
126pub async fn update_image_limit_settings(
127    max_dimension: u32,
128    max_pixels: u64,
129    dimensions_cache_ttl_secs: u64,
130) -> Result<ImageLimitSettings, ApiError> {
131    let _user = get_current_admin_user().await?;
132
133    let max_dimension = ImageLimitSettings::clamp_max_dimension(max_dimension);
134    let max_pixels = ImageLimitSettings::clamp_max_pixels(max_pixels);
135    let dimensions_cache_ttl_secs =
136        ImageLimitSettings::clamp_dimensions_cache_ttl_secs(dimensions_cache_ttl_secs);
137
138    #[cfg(feature = "server")]
139    {
140        let client = get_conn().await.map_err(AppError::db_conn)?;
141
142        super::upsert_settings(
143            &**client,
144            &[
145                ("image_max_dimension", max_dimension.to_string()),
146                ("image_max_pixels", max_pixels.to_string()),
147                (
148                    "image_dimensions_cache_ttl_secs",
149                    dimensions_cache_ttl_secs.to_string(),
150                ),
151            ],
152        )
153        .await?;
154
155        tracing::info!(
156            "Image limit settings updated (需重启生效): max_dimension={}, max_pixels={}, ttl={}s",
157            max_dimension,
158            max_pixels,
159            dimensions_cache_ttl_secs
160        );
161    }
162
163    Ok(ImageLimitSettings {
164        max_dimension,
165        max_pixels,
166        dimensions_cache_ttl_secs,
167    })
168}