Refactor project structure to use pnpm monorepo; update Dockerfile and related configurations for frontend build process. Adjust .dockerignore and .gitignore to reflect new paths. Modify .env.example for cron job timing. Update CONTRIBUTING.md and README.md for new development instructions.
Build, Test, and Push CFDM Docker Image / test (push) Failing after 3h13m2s
Build, Test, and Push CFDM Docker Image / create-release (push) Has been cancelled
Build, Test, and Push CFDM Docker Image / build-and-push (push) Has been cancelled
Build, Test, and Push CFDM Docker Image / update-wiki (push) Has been cancelled

This commit is contained in:
Denozordec
2026-06-15 15:37:36 +07:00
parent 87789fa82d
commit b1467575c5
803 changed files with 16646 additions and 8251 deletions
+60
View File
@@ -1,3 +1,63 @@
use crate::error::{AppError, AppResult};
use regex::Regex;
use std::sync::LazyLock;
static NAME_RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^(@|\*|[a-zA-Z0-9_]([a-zA-Z0-9_-]*[a-zA-Z0-9_])?(\.[a-zA-Z0-9_]([a-zA-Z0-9_-]*[a-zA-Z0-9_])?)*)$").unwrap());
static IPV4_RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$").unwrap());
static IPV6_RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$").unwrap());
const ALLOWED_TYPES: &[&str] = &["A", "AAAA", "CNAME", "TXT", "MX", "NS", "SRV", "CAA"];
pub fn validate_dns_record(
record_type: &str,
name: &str,
content: &str,
ttl: i64,
proxied: bool,
) -> AppResult<()> {
let rt = record_type.to_uppercase();
if !ALLOWED_TYPES.contains(&rt.as_str()) {
return Err(AppError::Validation(format!("unsupported record type: {record_type}")));
}
if !NAME_RE.is_match(name) {
return Err(AppError::Validation(format!("invalid record name: {name}")));
}
if ttl != 1 && !(60..=86400).contains(&ttl) {
return Err(AppError::Validation("ttl must be 1 (auto) or 60-86400".into()));
}
if proxied && !matches!(rt.as_str(), "A" | "AAAA" | "CNAME") {
return Err(AppError::Validation("proxied only allowed for A, AAAA, CNAME".into()));
}
match rt.as_str() {
"A" if !IPV4_RE.is_match(content) => {
return Err(AppError::Validation("A record requires valid IPv4".into()));
}
"AAAA" if !IPV6_RE.is_match(content) => {
return Err(AppError::Validation("AAAA record requires valid IPv6".into()));
}
"CNAME" | "NS" if content.is_empty() || content.contains(' ') => {
return Err(AppError::Validation("CNAME/NS requires valid hostname".into()));
}
"TXT" if content.is_empty() || content.len() > 2048 => {
return Err(AppError::Validation("TXT content length 1-2048".into()));
}
_ => {}
}
Ok(())
}
pub fn cert_status_from_expiry(days_left: i64) -> &'static str {
if days_left < 0 {
crate::domain::CERT_EXPIRED
} else if days_left <= 30 {
crate::domain::CERT_WARNING
} else {
crate::domain::CERT_OK
}
}
#[cfg(test)]
mod tests {
use super::*;