feat: add configuration management

- Add Settings class using pydantic-settings
- Load config from environment variables with GM_ prefix
- Support encrypt_key and api_token (required, no defaults for security)
- Provide defaults for data_dir, host, port
- Add computed properties for db_path, ssh_keys_dir, repos_dir
- Add tests for config defaults and environment variable overrides
- Add Base class to app.models to unblock conftest.py imports

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
panw
2026-03-30 15:07:55 +08:00
parent afe385d48b
commit cd6bf9bb13
3 changed files with 86 additions and 0 deletions

43
backend/app/config.py Normal file
View File

@@ -0,0 +1,43 @@
import os
from pathlib import Path
from typing import Literal
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
"""应用配置,从环境变量加载."""
# 安全配置
encrypt_key: str # AES-256 密钥 (base64)
api_token: str # API 认证 Token
# 路径配置
data_dir: Path = Path('./data')
# 服务器配置
host: str = '0.0.0.0'
port: int = 8000
model_config = SettingsConfigDict(
env_prefix='GM_',
env_file='.env',
env_file_encoding='utf-8',
)
@property
def db_path(self) -> Path:
"""SQLite 数据库路径."""
return self.data_dir / 'git_manager.db'
@property
def ssh_keys_dir(self) -> Path:
"""SSH 密钥存储目录."""
return self.data_dir / 'ssh_keys'
@property
def repos_dir(self) -> Path:
"""仓库镜像存储目录."""
return self.data_dir / 'repos'
settings = Settings()

View File

@@ -0,0 +1,11 @@
"""ORM Models.
NOTE: This module is a placeholder until Task 2.1.
The Base class is needed by conftest.py for database fixtures.
"""
from sqlalchemy.orm import DeclarativeBase
class Base(DeclarativeBase):
"""Base class for all ORM models."""
pass

View File

@@ -0,0 +1,32 @@
import os
import pytest
import base64
from pathlib import Path
def test_config_defaults(test_env_vars, monkeypatch):
"""测试配置默认值."""
# Clear GM_DATA_DIR to test default value
monkeypatch.delenv("GM_DATA_DIR", raising=False)
# Reload config to pick up the change
from app.config import Settings
settings = Settings()
assert settings.data_dir == Path('./data')
assert settings.host == '0.0.0.0'
assert settings.port == 8000
def test_config_from_env(monkeypatch):
"""测试从环境变量读取配置."""
# Set required security fields
monkeypatch.setenv("GM_ENCRYPT_KEY", base64.b64encode(b'test-key-32-bytes-long-1234567890').decode())
monkeypatch.setenv("GM_API_TOKEN", "test-token")
monkeypatch.setenv("GM_DATA_DIR", "/custom/data")
monkeypatch.setenv("GM_PORT", "9000")
# 重新加载配置
from app.config import Settings
settings = Settings()
assert settings.data_dir == Path('/custom/data')
assert settings.port == 9000