from datetime import datetime from typing import Optional from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel, EmailStr, Field from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.core.security import get_current_user from app.db.session import get_db from app.models.datasource import DataSource from app.models.system_setting import SystemSetting from app.models.user import User from app.services.scheduler import sync_datasource_job router = APIRouter() DEFAULT_SETTINGS = { "system": { "system_name": "智能星球", "refresh_interval": 60, "auto_refresh": True, "data_retention_days": 30, "max_concurrent_tasks": 5, }, "notifications": { "email_enabled": False, "email_address": "", "critical_alerts": True, "warning_alerts": True, "daily_summary": False, }, "security": { "session_timeout": 60, "max_login_attempts": 5, "password_policy": "medium", }, } class SystemSettingsUpdate(BaseModel): system_name: str = "智能星球" refresh_interval: int = Field(default=60, ge=10, le=3600) auto_refresh: bool = True data_retention_days: int = Field(default=30, ge=1, le=3650) max_concurrent_tasks: int = Field(default=5, ge=1, le=50) class NotificationSettingsUpdate(BaseModel): email_enabled: bool = False email_address: Optional[EmailStr] = None critical_alerts: bool = True warning_alerts: bool = True daily_summary: bool = False class SecuritySettingsUpdate(BaseModel): session_timeout: int = Field(default=60, ge=5, le=1440) max_login_attempts: int = Field(default=5, ge=1, le=20) password_policy: str = Field(default="medium") class CollectorSettingsUpdate(BaseModel): is_active: bool priority: str = Field(default="P1") frequency_minutes: int = Field(default=60, ge=1, le=10080) def merge_with_defaults(category: str, payload: Optional[dict]) -> dict: merged = DEFAULT_SETTINGS[category].copy() if payload: merged.update(payload) return merged async def get_setting_record(db: AsyncSession, category: str) -> Optional[SystemSetting]: result = await db.execute(select(SystemSetting).where(SystemSetting.category == category)) return result.scalar_one_or_none() async def get_setting_payload(db: AsyncSession, category: str) -> dict: record = await get_setting_record(db, category) return merge_with_defaults(category, record.payload if record else None) async def save_setting_payload(db: AsyncSession, category: str, payload: dict) -> dict: record = await get_setting_record(db, category) if record is None: record = SystemSetting(category=category, payload=payload) db.add(record) else: record.payload = payload await db.commit() await db.refresh(record) return merge_with_defaults(category, record.payload) def format_frequency_label(minutes: int) -> str: if minutes % 1440 == 0: return f"{minutes // 1440}d" if minutes % 60 == 0: return f"{minutes // 60}h" return f"{minutes}m" def serialize_collector(datasource: DataSource) -> dict: return { "id": datasource.id, "name": datasource.name, "source": datasource.source, "module": datasource.module, "priority": datasource.priority, "frequency_minutes": datasource.frequency_minutes, "frequency": format_frequency_label(datasource.frequency_minutes), "is_active": datasource.is_active, "last_run_at": datasource.last_run_at.isoformat() if datasource.last_run_at else None, "last_status": datasource.last_status, "next_run_at": datasource.next_run_at.isoformat() if datasource.next_run_at else None, } @router.get("/system") async def get_system_settings( current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): return {"system": await get_setting_payload(db, "system")} @router.put("/system") async def update_system_settings( settings: SystemSettingsUpdate, current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): payload = await save_setting_payload(db, "system", settings.model_dump()) return {"status": "updated", "system": payload} @router.get("/notifications") async def get_notification_settings( current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): return {"notifications": await get_setting_payload(db, "notifications")} @router.put("/notifications") async def update_notification_settings( settings: NotificationSettingsUpdate, current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): payload = await save_setting_payload(db, "notifications", settings.model_dump()) return {"status": "updated", "notifications": payload} @router.get("/security") async def get_security_settings( current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): return {"security": await get_setting_payload(db, "security")} @router.put("/security") async def update_security_settings( settings: SecuritySettingsUpdate, current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): payload = await save_setting_payload(db, "security", settings.model_dump()) return {"status": "updated", "security": payload} @router.get("/collectors") async def get_collector_settings( current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): result = await db.execute(select(DataSource).order_by(DataSource.module, DataSource.id)) datasources = result.scalars().all() return {"collectors": [serialize_collector(datasource) for datasource in datasources]} @router.put("/collectors/{datasource_id}") async def update_collector_settings( datasource_id: int, settings: CollectorSettingsUpdate, current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): datasource = await db.get(DataSource, datasource_id) if not datasource: raise HTTPException(status_code=404, detail="Data source not found") datasource.is_active = settings.is_active datasource.priority = settings.priority datasource.frequency_minutes = settings.frequency_minutes await db.commit() await db.refresh(datasource) await sync_datasource_job(datasource.id) return {"status": "updated", "collector": serialize_collector(datasource)} @router.get("") async def get_all_settings( current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): result = await db.execute(select(DataSource).order_by(DataSource.module, DataSource.id)) datasources = result.scalars().all() return { "system": await get_setting_payload(db, "system"), "notifications": await get_setting_payload(db, "notifications"), "security": await get_setting_payload(db, "security"), "collectors": [serialize_collector(datasource) for datasource in datasources], "generated_at": datetime.utcnow().isoformat() + "Z", }