yggdrasil/api/settings/
image_limit.rs1#![allow(clippy::unused_unit, deprecated, clippy::too_many_arguments)]
4
5use dioxus::prelude::*;
6
7#[cfg(feature = "server")]
8use crate::api::auth::get_current_admin_user;
9#[cfg(feature = "server")]
10use crate::api::error::AppError;
11#[cfg(feature = "server")]
12use crate::db::pool::get_conn;
13use crate::models::settings::ImageLimitSettings;
14
15#[cfg(feature = "server")]
22pub(crate) async fn seed_image_limit_settings_from_env(
23 client: &tokio_postgres::Client,
24) -> Result<(), AppError> {
25 use crate::models::settings as m;
26
27 let mut seeds: Vec<(&'static str, String)> = Vec::new();
28
29 if let Ok(v) = std::env::var("MAX_IMAGE_DIMENSION") {
30 match v.trim().parse::<u32>() {
31 Ok(n) => seeds.push((
32 "image_max_dimension",
33 m::ImageLimitSettings::clamp_max_dimension(n).to_string(),
34 )),
35 Err(_) => tracing::warn!("MAX_IMAGE_DIMENSION={v:?} 非法(期望正整数),跳过"),
36 }
37 }
38 if let Ok(v) = std::env::var("MAX_IMAGE_PIXELS") {
39 match v.trim().parse::<u64>() {
40 Ok(n) => seeds.push((
41 "image_max_pixels",
42 m::ImageLimitSettings::clamp_max_pixels(n).to_string(),
43 )),
44 Err(_) => tracing::warn!("MAX_IMAGE_PIXELS={v:?} 非法(期望正整数),跳过"),
45 }
46 }
47 if let Ok(v) = std::env::var("IMAGE_DIMENSIONS_CACHE_TTL_SECS") {
48 match v.trim().parse::<u64>() {
49 Ok(n) => seeds.push((
50 "image_dimensions_cache_ttl_secs",
51 m::ImageLimitSettings::clamp_dimensions_cache_ttl_secs(n).to_string(),
52 )),
53 Err(_) => {
54 tracing::warn!("IMAGE_DIMENSIONS_CACHE_TTL_SECS={v:?} 非法(期望正整数),跳过")
55 }
56 }
57 }
58
59 super::insert_env_seeds(client, seeds).await
60}
61
62#[cfg(feature = "server")]
64pub(crate) async fn load_image_limit_settings(
65 client: &tokio_postgres::Client,
66) -> Result<ImageLimitSettings, AppError> {
67 use crate::models::settings as m;
68
69 let values = super::load_setting_values(
70 client,
71 &[
72 "image_max_dimension",
73 "image_max_pixels",
74 "image_dimensions_cache_ttl_secs",
75 ],
76 )
77 .await?;
78 let max_dimension = values
79 .get("image_max_dimension")
80 .and_then(|v| v.parse().ok())
81 .map(m::ImageLimitSettings::clamp_max_dimension)
82 .unwrap_or(m::DEFAULT_IMAGE_MAX_DIMENSION);
83 let max_pixels = values
84 .get("image_max_pixels")
85 .and_then(|v| v.parse().ok())
86 .map(m::ImageLimitSettings::clamp_max_pixels)
87 .unwrap_or(m::DEFAULT_IMAGE_MAX_PIXELS);
88 let dimensions_cache_ttl_secs = values
89 .get("image_dimensions_cache_ttl_secs")
90 .and_then(|v| v.parse().ok())
91 .map(m::ImageLimitSettings::clamp_dimensions_cache_ttl_secs)
92 .unwrap_or(m::DEFAULT_IMAGE_DIMENSIONS_CACHE_TTL_SECS);
93
94 Ok(ImageLimitSettings {
95 max_dimension,
96 max_pixels,
97 dimensions_cache_ttl_secs,
98 })
99}
100
101#[server(GetImageLimitSettings, "/api")]
103pub async fn get_image_limit_settings() -> Result<ImageLimitSettings, ServerFnError> {
104 let _user = get_current_admin_user().await?;
105
106 #[cfg(feature = "server")]
107 {
108 let client = get_conn().await.map_err(AppError::db_conn)?;
109 let s = load_image_limit_settings(&client)
110 .await
111 .map_err(ServerFnError::from)?;
112 Ok(s)
113 }
114
115 #[cfg(not(feature = "server"))]
116 {
117 Ok(ImageLimitSettings::default())
118 }
119}
120
121#[server(UpdateImageLimitSettings, "/api")]
125pub async fn update_image_limit_settings(
126 max_dimension: u32,
127 max_pixels: u64,
128 dimensions_cache_ttl_secs: u64,
129) -> Result<ImageLimitSettings, ServerFnError> {
130 let _user = get_current_admin_user().await?;
131
132 let max_dimension = ImageLimitSettings::clamp_max_dimension(max_dimension);
133 let max_pixels = ImageLimitSettings::clamp_max_pixels(max_pixels);
134 let dimensions_cache_ttl_secs =
135 ImageLimitSettings::clamp_dimensions_cache_ttl_secs(dimensions_cache_ttl_secs);
136
137 #[cfg(feature = "server")]
138 {
139 let client = get_conn().await.map_err(AppError::db_conn)?;
140
141 for (key, value) in [
142 ("image_max_dimension", max_dimension.to_string()),
143 ("image_max_pixels", max_pixels.to_string()),
144 (
145 "image_dimensions_cache_ttl_secs",
146 dimensions_cache_ttl_secs.to_string(),
147 ),
148 ] {
149 client
150 .execute(
151 "INSERT INTO settings (key, value, updated_at) VALUES ($1, $2, NOW())
152 ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = NOW()",
153 &[&key, &value],
154 )
155 .await
156 .map_err(AppError::query)?;
157 }
158
159 tracing::info!(
160 "Image limit settings updated (需重启生效): max_dimension={}, max_pixels={}, ttl={}s",
161 max_dimension,
162 max_pixels,
163 dimensions_cache_ttl_secs
164 );
165 }
166
167 Ok(ImageLimitSettings {
168 max_dimension,
169 max_pixels,
170 dimensions_cache_ttl_secs,
171 })
172}