yggdrasil/utils/server.rs
1//! 服务端共享工具(仅 `feature = "server"` 编译)。
2//!
3//! 集中跨模块重复的服务端常量与工具函数(issue #7 重复常量去重)。
4
5#![cfg(feature = "server")]
6
7use sha2::{Digest, Sha256};
8
9/// 明文 token / 任意字符串 → SHA-256 hex。
10///
11/// 此前 `auth/session.rs` 与 `mcp/auth.rs` 各有一份逐字相同的实现。
12pub fn sha256_hex(input: &str) -> String {
13 let mut hasher = Sha256::new();
14 hasher.update(input.as_bytes());
15 hex::encode(hasher.finalize())
16}
17
18/// 邮箱格式正则(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)。
19///
20/// 此前 `api/auth.rs` 与 `api/comments/helpers.rs` 各有一份逐字相同的 LazyLock。
21pub static EMAIL_REGEX: std::sync::LazyLock<regex::Regex> = std::sync::LazyLock::new(|| {
22 regex::Regex::new(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")
23 .expect("EMAIL_REGEX 正则模式应在编译期通过校验")
24});
25
26/// 上传文件大小上限(5 MiB)。
27///
28/// 此前 `api/upload.rs` 与 `mcp/tools/media.rs` 各定义一份相同的常量。
29pub const MAX_FILE_SIZE: usize = 5 * 1024 * 1024;
30
31/// 启动期数据库迁移超时窗口(秒),由 `MIGRATE_STARTUP_TIMEOUT_SECS` 控制,默认 30。
32///
33/// 此前 `main.rs` 与 `db/pool.rs`(`get_conn_for_startup`、`ensure_database_exists` 两处)
34/// 各有一份逐字相同的 `.ok().and_then(parse).unwrap_or(30)` 解析链。
35pub fn parse_migrate_startup_timeout() -> u64 {
36 std::env::var("MIGRATE_STARTUP_TIMEOUT_SECS")
37 .ok()
38 .and_then(|s| s.parse::<u64>().ok())
39 .unwrap_or(30)
40}
41
42/// 转义 SQL `LIKE` 模式串中的特殊字符(`\`、`%`、`_`),配合 `ESCAPE '\\'` 使用。
43///
44/// 此前 `api/posts/list.rs`(逐字符循环)与 `api/posts/search.rs`、`mcp/tools/read.rs`
45///(replace 链)各有一份实现;统一为等价且更简洁的 replace 链风格。
46pub fn escape_like_pattern(s: &str) -> String {
47 s.replace('\\', "\\\\")
48 .replace('%', "\\%")
49 .replace('_', "\\_")
50}