1#![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::RateLimitSettings;
15
16#[cfg(feature = "server")]
31pub(crate) async fn seed_rate_limit_from_env(
32 client: &tokio_postgres::Client,
33) -> Result<(), AppError> {
34 type EnvField = (&'static str, &'static str, fn(u32) -> u32);
36
37 use crate::models::settings::RateLimitSettings as R;
38
39 let mut seeds: Vec<(&'static str, String)> = Vec::new();
40
41 let fields: &[EnvField] = &[
42 (
43 "RATE_LIMIT_STRICT_PER_SEC",
44 "ratelimit_strict_per_sec",
45 R::clamp_per_sec,
46 ),
47 (
48 "RATE_LIMIT_STRICT_BURST",
49 "ratelimit_strict_burst",
50 R::clamp_burst,
51 ),
52 (
53 "RATE_LIMIT_UPLOAD_PER_SEC",
54 "ratelimit_upload_per_sec",
55 R::clamp_per_sec,
56 ),
57 (
58 "RATE_LIMIT_UPLOAD_BURST",
59 "ratelimit_upload_burst",
60 R::clamp_burst,
61 ),
62 (
63 "RATE_LIMIT_IMAGE_PER_SEC",
64 "ratelimit_image_per_sec",
65 R::clamp_per_sec,
66 ),
67 (
68 "RATE_LIMIT_IMAGE_BURST",
69 "ratelimit_image_burst",
70 R::clamp_burst,
71 ),
72 (
73 "RATE_LIMIT_COMMENT_PER_SEC",
74 "ratelimit_comment_per_sec",
75 R::clamp_per_sec,
76 ),
77 (
78 "RATE_LIMIT_COMMENT_BURST",
79 "ratelimit_comment_burst",
80 R::clamp_burst,
81 ),
82 (
83 "RATE_LIMIT_COMMENT_UPLOAD_PER_SEC",
84 "ratelimit_comment_upload_per_sec",
85 R::clamp_per_sec,
86 ),
87 (
88 "RATE_LIMIT_COMMENT_UPLOAD_BURST",
89 "ratelimit_comment_upload_burst",
90 R::clamp_burst,
91 ),
92 (
93 "RATE_LIMIT_COMMENT_UPLOAD_DAILY",
94 "ratelimit_comment_upload_daily",
95 R::clamp_daily,
96 ),
97 (
98 "RATE_LIMIT_CODE_EXEC_PER_SEC",
99 "ratelimit_code_exec_per_sec",
100 R::clamp_per_sec,
101 ),
102 (
103 "RATE_LIMIT_CODE_EXEC_BURST",
104 "ratelimit_code_exec_burst",
105 R::clamp_burst,
106 ),
107 (
108 "RATE_LIMIT_CODE_EXEC_DAILY",
109 "ratelimit_code_exec_daily",
110 R::clamp_daily,
111 ),
112 (
113 "RATE_LIMIT_UNKNOWN_PER_SEC",
114 "ratelimit_unknown_per_sec",
115 R::clamp_per_sec,
116 ),
117 (
118 "RATE_LIMIT_UNKNOWN_BURST",
119 "ratelimit_unknown_burst",
120 R::clamp_burst,
121 ),
122 (
123 "RATE_LIMIT_MCP_PER_SEC",
124 "ratelimit_mcp_per_sec",
125 R::clamp_per_sec,
126 ),
127 (
128 "RATE_LIMIT_MCP_BURST",
129 "ratelimit_mcp_burst",
130 R::clamp_burst,
131 ),
132 (
133 "RATE_LIMIT_MCP_UPLOAD_PER_SEC",
134 "ratelimit_mcp_upload_per_sec",
135 R::clamp_per_sec,
136 ),
137 (
138 "RATE_LIMIT_MCP_UPLOAD_BURST",
139 "ratelimit_mcp_upload_burst",
140 R::clamp_burst,
141 ),
142 (
143 "RATE_LIMIT_GC_INTERVAL_SECS",
144 "ratelimit_gc_interval_secs",
145 R::clamp_gc_interval,
146 ),
147 ];
148
149 for &(env_key, db_key, clamp) in fields {
150 if let Ok(v) = std::env::var(env_key) {
151 match v.trim().parse::<u32>() {
152 Ok(n) => seeds.push((db_key, clamp(n).to_string())),
153 Err(_) => tracing::warn!("{env_key}={v:?} 非法(期望正整数),跳过"),
154 }
155 }
156 }
157
158 super::insert_env_seeds(client, seeds).await
159}
160
161#[cfg(feature = "server")]
165pub(crate) async fn load_rate_limit_settings(
166 client: &tokio_postgres::Client,
167) -> Result<RateLimitSettings, AppError> {
168 use crate::models::settings as m;
169 use RateLimitSettings as R;
170
171 let values = super::load_setting_values(
172 client,
173 &[
174 "ratelimit_strict_per_sec",
175 "ratelimit_strict_burst",
176 "ratelimit_upload_per_sec",
177 "ratelimit_upload_burst",
178 "ratelimit_image_per_sec",
179 "ratelimit_image_burst",
180 "ratelimit_comment_per_sec",
181 "ratelimit_comment_burst",
182 "ratelimit_comment_upload_per_sec",
183 "ratelimit_comment_upload_burst",
184 "ratelimit_comment_upload_daily",
185 "ratelimit_code_exec_per_sec",
186 "ratelimit_code_exec_burst",
187 "ratelimit_code_exec_daily",
188 "ratelimit_unknown_per_sec",
189 "ratelimit_unknown_burst",
190 "ratelimit_mcp_per_sec",
191 "ratelimit_mcp_burst",
192 "ratelimit_mcp_upload_per_sec",
193 "ratelimit_mcp_upload_burst",
194 "ratelimit_gc_interval_secs",
195 ],
196 )
197 .await?;
198 let read_clamped = |key: &str, default: u32, clamp: fn(u32) -> u32| {
199 values
200 .get(key)
201 .and_then(|v| v.parse().ok())
202 .map(clamp)
203 .unwrap_or(default)
204 };
205
206 Ok(RateLimitSettings {
207 strict_per_sec: read_clamped(
208 "ratelimit_strict_per_sec",
209 m::DEFAULT_RATE_LIMIT_STRICT_PER_SEC,
210 R::clamp_per_sec,
211 ),
212 strict_burst: read_clamped(
213 "ratelimit_strict_burst",
214 m::DEFAULT_RATE_LIMIT_STRICT_BURST,
215 R::clamp_burst,
216 ),
217 upload_per_sec: read_clamped(
218 "ratelimit_upload_per_sec",
219 m::DEFAULT_RATE_LIMIT_UPLOAD_PER_SEC,
220 R::clamp_per_sec,
221 ),
222 upload_burst: read_clamped(
223 "ratelimit_upload_burst",
224 m::DEFAULT_RATE_LIMIT_UPLOAD_BURST,
225 R::clamp_burst,
226 ),
227 image_per_sec: read_clamped(
228 "ratelimit_image_per_sec",
229 m::DEFAULT_RATE_LIMIT_IMAGE_PER_SEC,
230 R::clamp_per_sec,
231 ),
232 image_burst: read_clamped(
233 "ratelimit_image_burst",
234 m::DEFAULT_RATE_LIMIT_IMAGE_BURST,
235 R::clamp_burst,
236 ),
237 comment_per_sec: read_clamped(
238 "ratelimit_comment_per_sec",
239 m::DEFAULT_RATE_LIMIT_COMMENT_PER_SEC,
240 R::clamp_per_sec,
241 ),
242 comment_burst: read_clamped(
243 "ratelimit_comment_burst",
244 m::DEFAULT_RATE_LIMIT_COMMENT_BURST,
245 R::clamp_burst,
246 ),
247 comment_upload_per_sec: read_clamped(
248 "ratelimit_comment_upload_per_sec",
249 m::DEFAULT_RATE_LIMIT_COMMENT_UPLOAD_PER_SEC,
250 R::clamp_per_sec,
251 ),
252 comment_upload_burst: read_clamped(
253 "ratelimit_comment_upload_burst",
254 m::DEFAULT_RATE_LIMIT_COMMENT_UPLOAD_BURST,
255 R::clamp_burst,
256 ),
257 comment_upload_daily: read_clamped(
258 "ratelimit_comment_upload_daily",
259 m::DEFAULT_RATE_LIMIT_COMMENT_UPLOAD_DAILY,
260 R::clamp_daily,
261 ),
262 code_exec_per_sec: read_clamped(
263 "ratelimit_code_exec_per_sec",
264 m::DEFAULT_RATE_LIMIT_CODE_EXEC_PER_SEC,
265 R::clamp_per_sec,
266 ),
267 code_exec_burst: read_clamped(
268 "ratelimit_code_exec_burst",
269 m::DEFAULT_RATE_LIMIT_CODE_EXEC_BURST,
270 R::clamp_burst,
271 ),
272 code_exec_daily: read_clamped(
273 "ratelimit_code_exec_daily",
274 m::DEFAULT_RATE_LIMIT_CODE_EXEC_DAILY,
275 R::clamp_daily,
276 ),
277 unknown_per_sec: read_clamped(
278 "ratelimit_unknown_per_sec",
279 m::DEFAULT_RATE_LIMIT_UNKNOWN_PER_SEC,
280 R::clamp_per_sec,
281 ),
282 unknown_burst: read_clamped(
283 "ratelimit_unknown_burst",
284 m::DEFAULT_RATE_LIMIT_UNKNOWN_BURST,
285 R::clamp_burst,
286 ),
287 mcp_per_sec: read_clamped(
288 "ratelimit_mcp_per_sec",
289 m::DEFAULT_RATE_LIMIT_MCP_PER_SEC,
290 R::clamp_per_sec,
291 ),
292 mcp_burst: read_clamped(
293 "ratelimit_mcp_burst",
294 m::DEFAULT_RATE_LIMIT_MCP_BURST,
295 R::clamp_burst,
296 ),
297 mcp_upload_per_sec: read_clamped(
298 "ratelimit_mcp_upload_per_sec",
299 m::DEFAULT_RATE_LIMIT_MCP_UPLOAD_PER_SEC,
300 R::clamp_per_sec,
301 ),
302 mcp_upload_burst: read_clamped(
303 "ratelimit_mcp_upload_burst",
304 m::DEFAULT_RATE_LIMIT_MCP_UPLOAD_BURST,
305 R::clamp_burst,
306 ),
307 gc_interval_secs: read_clamped(
308 "ratelimit_gc_interval_secs",
309 m::DEFAULT_RATE_LIMIT_GC_INTERVAL_SECS,
310 R::clamp_gc_interval,
311 ),
312 })
313}
314
315#[server(GetRateLimitSettings, endpoint = "get_rate_limit_settings", input = leptos::server_fn::codec::Json)]
317pub async fn get_rate_limit_settings() -> Result<RateLimitSettings, ApiError> {
318 let _user = get_current_admin_user().await?;
319
320 #[cfg(feature = "server")]
321 {
322 let client = get_conn().await.map_err(AppError::db_conn)?;
323 let s = load_rate_limit_settings(&client)
324 .await
325 .map_err(<ApiError>::from)?;
326 Ok(s)
327 }
328
329 #[cfg(not(feature = "server"))]
330 {
331 Ok(RateLimitSettings::default())
332 }
333}
334
335#[server(UpdateRateLimitSettings, endpoint = "update_rate_limit_settings", input = leptos::server_fn::codec::Json)]
340pub async fn update_rate_limit_settings(
341 strict_per_sec: u32,
342 strict_burst: u32,
343 upload_per_sec: u32,
344 upload_burst: u32,
345 image_per_sec: u32,
346 image_burst: u32,
347 comment_per_sec: u32,
348 comment_burst: u32,
349 comment_upload_per_sec: u32,
350 comment_upload_burst: u32,
351 comment_upload_daily: u32,
352 code_exec_per_sec: u32,
353 code_exec_burst: u32,
354 code_exec_daily: u32,
355 unknown_per_sec: u32,
356 unknown_burst: u32,
357 mcp_per_sec: u32,
358 mcp_burst: u32,
359 mcp_upload_per_sec: u32,
360 mcp_upload_burst: u32,
361 gc_interval_secs: u32,
362) -> Result<RateLimitSettings, ApiError> {
363 let _user = get_current_admin_user().await?;
364
365 let strict_per_sec = RateLimitSettings::clamp_per_sec(strict_per_sec);
366 let strict_burst = RateLimitSettings::clamp_burst(strict_burst);
367 let upload_per_sec = RateLimitSettings::clamp_per_sec(upload_per_sec);
368 let upload_burst = RateLimitSettings::clamp_burst(upload_burst);
369 let image_per_sec = RateLimitSettings::clamp_per_sec(image_per_sec);
370 let image_burst = RateLimitSettings::clamp_burst(image_burst);
371 let comment_per_sec = RateLimitSettings::clamp_per_sec(comment_per_sec);
372 let comment_burst = RateLimitSettings::clamp_burst(comment_burst);
373 let comment_upload_per_sec = RateLimitSettings::clamp_per_sec(comment_upload_per_sec);
374 let comment_upload_burst = RateLimitSettings::clamp_burst(comment_upload_burst);
375 let comment_upload_daily = RateLimitSettings::clamp_daily(comment_upload_daily);
376 let code_exec_per_sec = RateLimitSettings::clamp_per_sec(code_exec_per_sec);
377 let code_exec_burst = RateLimitSettings::clamp_burst(code_exec_burst);
378 let code_exec_daily = RateLimitSettings::clamp_daily(code_exec_daily);
379 let unknown_per_sec = RateLimitSettings::clamp_per_sec(unknown_per_sec);
380 let unknown_burst = RateLimitSettings::clamp_burst(unknown_burst);
381 let mcp_per_sec = RateLimitSettings::clamp_per_sec(mcp_per_sec);
382 let mcp_burst = RateLimitSettings::clamp_burst(mcp_burst);
383 let mcp_upload_per_sec = RateLimitSettings::clamp_per_sec(mcp_upload_per_sec);
384 let mcp_upload_burst = RateLimitSettings::clamp_burst(mcp_upload_burst);
385 let gc_interval_secs = RateLimitSettings::clamp_gc_interval(gc_interval_secs);
386
387 #[cfg(feature = "server")]
388 {
389 let client = get_conn().await.map_err(AppError::db_conn)?;
390
391 super::upsert_settings(
392 &**client,
393 &[
394 ("ratelimit_strict_per_sec", strict_per_sec.to_string()),
395 ("ratelimit_strict_burst", strict_burst.to_string()),
396 ("ratelimit_upload_per_sec", upload_per_sec.to_string()),
397 ("ratelimit_upload_burst", upload_burst.to_string()),
398 ("ratelimit_image_per_sec", image_per_sec.to_string()),
399 ("ratelimit_image_burst", image_burst.to_string()),
400 ("ratelimit_comment_per_sec", comment_per_sec.to_string()),
401 ("ratelimit_comment_burst", comment_burst.to_string()),
402 (
403 "ratelimit_comment_upload_per_sec",
404 comment_upload_per_sec.to_string(),
405 ),
406 (
407 "ratelimit_comment_upload_burst",
408 comment_upload_burst.to_string(),
409 ),
410 (
411 "ratelimit_comment_upload_daily",
412 comment_upload_daily.to_string(),
413 ),
414 ("ratelimit_code_exec_per_sec", code_exec_per_sec.to_string()),
415 ("ratelimit_code_exec_burst", code_exec_burst.to_string()),
416 ("ratelimit_code_exec_daily", code_exec_daily.to_string()),
417 ("ratelimit_unknown_per_sec", unknown_per_sec.to_string()),
418 ("ratelimit_unknown_burst", unknown_burst.to_string()),
419 ("ratelimit_mcp_per_sec", mcp_per_sec.to_string()),
420 ("ratelimit_mcp_burst", mcp_burst.to_string()),
421 (
422 "ratelimit_mcp_upload_per_sec",
423 mcp_upload_per_sec.to_string(),
424 ),
425 ("ratelimit_mcp_upload_burst", mcp_upload_burst.to_string()),
426 ("ratelimit_gc_interval_secs", gc_interval_secs.to_string()),
427 ],
428 )
429 .await?;
430
431 tracing::info!(
432 "Rate limit settings updated (需重启生效): strict={}/{}, upload={}/{}, \
433 image={}/{}, comment={}/{}, comment_upload={}/{}/{}, code_exec={}/{}/{}, unknown={}/{}, \
434 mcp={}/{}, mcp_upload={}/{}, gc={}s",
435 strict_per_sec,
436 strict_burst,
437 upload_per_sec,
438 upload_burst,
439 image_per_sec,
440 image_burst,
441 comment_per_sec,
442 comment_burst,
443 comment_upload_per_sec,
444 comment_upload_burst,
445 comment_upload_daily,
446 code_exec_per_sec,
447 code_exec_burst,
448 code_exec_daily,
449 unknown_per_sec,
450 unknown_burst,
451 mcp_per_sec,
452 mcp_burst,
453 mcp_upload_per_sec,
454 mcp_upload_burst,
455 gc_interval_secs
456 );
457 }
458
459 Ok(RateLimitSettings {
460 strict_per_sec,
461 strict_burst,
462 upload_per_sec,
463 upload_burst,
464 image_per_sec,
465 image_burst,
466 comment_per_sec,
467 comment_burst,
468 comment_upload_per_sec,
469 comment_upload_burst,
470 comment_upload_daily,
471 code_exec_per_sec,
472 code_exec_burst,
473 code_exec_daily,
474 unknown_per_sec,
475 unknown_burst,
476 mcp_per_sec,
477 mcp_burst,
478 mcp_upload_per_sec,
479 mcp_upload_burst,
480 gc_interval_secs,
481 })
482}