Files
tianpu-ems/backend/app/api/v1/auth.py
Du Wenbo ef9b5d055f feat: add system settings, audit log, device detail, dark mode, i18n, email notifications
System Management:
- System Settings page with 8 configurable parameters (admin only)
- Audit Log page with filterable table (user, action, resource, date range)
- Audit logging wired into auth, devices, users, alarms, reports API handlers
- SystemSetting model + migration (002)

Device Detail:
- Dedicated /devices/:id page with 4 tabs (realtime, historical trends, alarm history, device info)
- ECharts historical charts with granularity/time range selectors
- Device name clickable in Devices and Monitoring tables → navigates to detail

Email & Scheduling:
- Email service with SMTP support (STARTTLS/SSL/plain)
- Alarm email notification with professional HTML template
- Report scheduler using APScheduler for cron-based auto-generation
- Scheduled report task seeded (daily at 8am)

UI Enhancements:
- Dark mode toggle (persisted to localStorage, Ant Design darkAlgorithm)
- Data comparison view in Analysis page (dual date range, side-by-side metrics)
- i18n framework (i18next) with zh/en translations for menu and common UI
- Language switcher in header (中文/English)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 19:42:22 +08:00

54 lines
2.1 KiB
Python

from datetime import datetime, timezone
from fastapi import APIRouter, Depends, HTTPException, Request, status
from fastapi.security import OAuth2PasswordRequestForm
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from pydantic import BaseModel
from app.core.database import get_db
from app.core.security import verify_password, create_access_token, hash_password
from app.core.deps import get_current_user
from app.models.user import User
from app.services.audit import log_audit
router = APIRouter(prefix="/auth", tags=["认证"])
class Token(BaseModel):
access_token: str
token_type: str = "bearer"
user: dict
class RegisterRequest(BaseModel):
username: str
password: str
full_name: str | None = None
email: str | None = None
phone: str | None = None
@router.post("/login", response_model=Token)
async def login(request: Request, form: OAuth2PasswordRequestForm = Depends(), db: AsyncSession = Depends(get_db)):
result = await db.execute(select(User).where(User.username == form.username))
user = result.scalar_one_or_none()
if not user or not verify_password(form.password, user.hashed_password):
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="用户名或密码错误")
if not user.is_active:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="账号已禁用")
user.last_login = datetime.now(timezone.utc)
token = create_access_token({"sub": str(user.id), "role": user.role})
client_ip = request.client.host if request.client else None
await log_audit(db, user.id, "login", "auth", detail=f"用户 {user.username} 登录", ip_address=client_ip)
return Token(
access_token=token,
user={"id": user.id, "username": user.username, "full_name": user.full_name, "role": user.role}
)
@router.get("/me")
async def get_me(user: User = Depends(get_current_user)):
return {
"id": user.id, "username": user.username, "full_name": user.full_name,
"email": user.email, "phone": user.phone, "role": user.role, "is_active": user.is_active,
}