Files
Task_Reporter/app/modules/auth/router.py
egg 3927441103 feat: Add AI report generation with DIFY integration
- Add Users table for display name resolution from AD authentication
- Integrate DIFY AI service for report content generation
- Create docx assembly service with image embedding from MinIO
- Add REST API endpoints for report generation and download
- Add WebSocket notifications for generation progress
- Add frontend UI with progress modal and download functionality
- Add integration tests for report generation flow

Report sections (Traditional Chinese):
- 事件摘要 (Summary)
- 時間軸 (Timeline)
- 參與人員 (Participants)
- 處理過程 (Resolution Process)
- 目前狀態 (Current Status)
- 最終處置結果 (Final Resolution)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 18:32:40 +08:00

107 lines
3.6 KiB
Python

"""Authentication API endpoints
提供:
- POST /api/auth/login - 使用者登入
- POST /api/auth/logout - 使用者登出
"""
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from app.core.database import get_db
from app.modules.auth.schemas import LoginRequest, LoginResponse, LogoutResponse, ErrorResponse
from app.modules.auth.services.ad_client import ad_auth_service
from app.modules.auth.services.encryption import encryption_service
from app.modules.auth.services.session_service import session_service
from app.modules.auth.services.user_service import upsert_user
from fastapi import Header
from typing import Optional
router = APIRouter(prefix="/api/auth", tags=["Authentication"])
@router.post(
"/login",
response_model=LoginResponse,
responses={
401: {"model": ErrorResponse, "description": "Invalid credentials"},
503: {"model": ErrorResponse, "description": "Authentication service unavailable"},
},
)
async def login(request: LoginRequest, db: Session = Depends(get_db)):
"""使用者登入
流程:
1. 呼叫 AD API 驗證憑證
2. 儲存/更新使用者資訊到 users 表(用於報告姓名解析)
3. 加密密碼(用於自動刷新)
4. 生成 internal token (UUID)
5. 儲存 session 到資料庫
6. 回傳 internal token 和 display_name
"""
try:
# Step 1: Authenticate with AD API
ad_result = await ad_auth_service.authenticate(request.username, request.password)
except ValueError as e:
# Invalid credentials
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials"
)
except ConnectionError as e:
# AD API unavailable
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail="Authentication service unavailable",
)
# Step 2: Upsert user info for report generation (permanent storage)
upsert_user(
db=db,
user_id=ad_result["email"],
display_name=ad_result["username"],
office_location=ad_result.get("office_location"),
job_title=ad_result.get("job_title"),
)
# Step 3: Encrypt password for future auto-refresh
encrypted_password = encryption_service.encrypt_password(request.password)
# Step 4 & 5: Generate internal token and create session
user_session = session_service.create_session(
db=db,
username=request.username,
display_name=ad_result["username"],
ad_token=ad_result["token"],
encrypted_password=encrypted_password,
ad_token_expires_at=ad_result["expires_at"],
)
# Step 5: Return internal token to client
return LoginResponse(token=user_session.internal_token, display_name=user_session.display_name)
@router.post(
"/logout",
response_model=LogoutResponse,
responses={401: {"model": ErrorResponse, "description": "No authentication token provided"}},
)
async def logout(authorization: Optional[str] = Header(None), db: Session = Depends(get_db)):
"""使用者登出
刪除 session 記錄,使 token 失效
"""
if not authorization or not authorization.startswith("Bearer "):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail="No authentication token provided"
)
# Extract token
internal_token = authorization.replace("Bearer ", "")
# Find and delete session
user_session = session_service.get_session_by_token(db, internal_token)
if user_session:
session_service.delete_session(db, user_session.id)
return LogoutResponse(message="Logout successful")