feat: 数据备份管理系统 v0.1.0

带 Web 操作界面的备份系统,支持:
- 数据源:MySQL 数据库(mysqldump)、服务器目录(tar.gz)
- 存储目标:本地目录、S3 兼容对象存储(MinIO/Ceph/AWS S3)
- 触发方式:手动 + Cron 定时调度(APScheduler)
- 备份还原:MySQL 库、目录
- JWT 登录认证 + Fernet 字段加密
- 保留策略:按数量 + 按天数双重清理

技术栈:FastAPI + SQLAlchemy 2 + APScheduler + aioboto3;
前端 Vite + React 18 + TypeScript + Ant Design 5 + Zustand。
Docker Compose 一键起。

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
publ
2026-06-22 18:27:41 +08:00
commit 4e3a4b4602
92 changed files with 5290 additions and 0 deletions
+44
View File
@@ -0,0 +1,44 @@
"""应用配置:从环境变量加载,启动时校验必填项。"""
from functools import lru_cache
from typing import List
from pydantic import Field, field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
case_sensitive=True,
extra="ignore",
)
# ===== 必填 =====
SECRET_KEY: str = Field(..., description="JWT 签名密钥(>= 32 字符)")
FERNET_KEY: str = Field(..., description="Fernet 字段加密密钥")
INITIAL_ADMIN_PASSWORD: str = Field(..., min_length=8)
# ===== 可选 =====
JWT_EXPIRE_MINUTES: int = 1440
DATABASE_URL: str = "sqlite:///./data/backup.db"
STORAGE_LOCAL_ROOT: str = "./data/backups"
LOG_LEVEL: str = "INFO"
CORS_ORIGINS: str = "http://localhost:5173,http://localhost:8000"
TZ: str = "UTC"
@field_validator("SECRET_KEY")
@classmethod
def _check_secret_key(cls, v: str) -> str:
if len(v) < 32:
raise ValueError("SECRET_KEY 长度必须 >= 32 字符(建议 openssl rand -hex 32")
return v
@property
def cors_origins_list(self) -> List[str]:
return [o.strip() for o in self.CORS_ORIGINS.split(",") if o.strip()]
@lru_cache
def get_settings() -> Settings:
return Settings() # type: ignore[call-arg]