yggdrasil/api/settings/
mod.rs1mod asset_purge;
22mod backup;
23mod image_cache;
24mod image_limit;
25mod rate_limit;
26mod runner;
27mod security;
28mod site;
29mod system;
30mod trash;
31mod upload;
32mod webp;
33
34#[cfg(feature = "server")]
35use crate::api::error::AppError;
36#[cfg(feature = "server")]
37use std::collections::HashMap;
38
39#[cfg(feature = "server")]
44pub(crate) async fn insert_env_seeds(
45 client: &tokio_postgres::Client,
46 seeds: Vec<(&'static str, String)>,
47) -> Result<(), AppError> {
48 if seeds.is_empty() {
49 return Ok(());
50 }
51
52 let keys: Vec<&str> = seeds.iter().map(|(key, _)| *key).collect();
53 let values: Vec<&str> = seeds.iter().map(|(_, value)| value.as_str()).collect();
54 client
55 .execute(
56 "INSERT INTO settings (key, value)
57 SELECT * FROM UNNEST($1::text[], $2::text[])
58 ON CONFLICT (key) DO NOTHING",
59 &[&keys, &values],
60 )
61 .await
62 .map_err(AppError::query)?;
63
64 for (key, value) in seeds {
65 tracing::info!("配置已从环境变量播种: {key}={value}(仅键缺失时生效)");
66 }
67 Ok(())
68}
69
70#[cfg(feature = "server")]
72pub(crate) async fn load_setting_values(
73 client: &tokio_postgres::Client,
74 keys: &[&str],
75) -> Result<HashMap<String, String>, AppError> {
76 if keys.is_empty() {
77 return Ok(HashMap::new());
78 }
79
80 let keys: Vec<&str> = keys.to_vec();
81 let rows = client
82 .query(
83 "SELECT key, value FROM settings WHERE key = ANY($1::text[])",
84 &[&keys],
85 )
86 .await
87 .map_err(AppError::query)?;
88
89 let mut values = HashMap::with_capacity(rows.len());
90 for row in rows {
91 values.insert(row.get::<_, String>("key"), row.get::<_, String>("value"));
92 }
93 Ok(values)
94}
95
96#[cfg(feature = "server")]
102pub(crate) async fn bootstrap_startup_settings(
103 client: &tokio_postgres::Client,
104) -> Result<(), AppError> {
105 let (backup, upload, security, image_cache, asset_purge, rate_limit, webp, image_limit, runner) = tokio::join!(
106 seed_backup_settings_from_env(client),
107 seed_upload_settings_from_env(client),
108 seed_security_settings_from_env(client),
109 seed_image_cache_settings_from_env(client),
110 seed_asset_purge_settings_from_env(client),
111 seed_rate_limit_from_env(client),
112 seed_webp_settings_from_env(client),
113 seed_image_limit_settings_from_env(client),
114 seed_runner_settings_from_env(client),
115 );
116
117 for (name, result) in [
118 ("backup", backup),
119 ("upload", upload),
120 ("security", security),
121 ("image_cache", image_cache),
122 ("asset_purge", asset_purge),
123 ("rate_limit", rate_limit),
124 ("webp", webp),
125 ("image_limit", image_limit),
126 ("runner", runner),
127 ] {
128 if let Err(error) = result {
129 tracing::warn!(setting_group = name, error = ?error, "启动设置环境变量播种失败");
130 }
131 }
132
133 let (rate_limit, webp, image_limit, runner) = tokio::join!(
134 load_rate_limit_settings(client),
135 load_webp_settings(client),
136 load_image_limit_settings(client),
137 load_runner_settings(client),
138 );
139
140 let rate_limit = rate_limit.map_err(|error| {
141 tracing::error!(error = ?error, "限流启动配置加载失败,拒绝使用默认值");
142 error
143 })?;
144 let webp = webp.map_err(|error| {
145 tracing::error!(error = ?error, "WebP 启动配置加载失败,拒绝使用默认值");
146 error
147 })?;
148 let image_limit = image_limit.map_err(|error| {
149 tracing::error!(error = ?error, "图片限制启动配置加载失败,拒绝使用默认值");
150 error
151 })?;
152 let runner = runner.map_err(|error| {
153 tracing::error!(error = ?error, "代码运行器启动配置加载失败,拒绝使用默认值");
154 error
155 })?;
156
157 crate::config::set_rate_limit(rate_limit);
158 crate::config::set_webp(webp);
159 crate::config::set_image_limit(image_limit);
160 crate::config::set_runner(runner);
161 Ok(())
162}
163
164pub use asset_purge::*;
165pub use backup::*;
166pub use image_cache::*;
167pub use image_limit::*;
168pub use rate_limit::*;
169pub use runner::*;
170pub use security::*;
171pub use site::*;
172#[allow(unused_imports)]
175pub use system::*;
176pub use trash::*;
177pub use upload::*;
178pub use webp::*;