Files
hr-portal/backend/app/main.py
Porsche Chen 360533393f feat: HR Portal - Complete Multi-Tenant System with Redis Session Storage
Major Features:
-  Multi-tenant architecture (tenant isolation)
-  Employee CRUD with lifecycle management (onboarding/offboarding)
-  Department tree structure with email domain management
-  Company info management (single-record editing)
-  System functions CRUD (permission management)
-  Email account management (multi-account per employee)
-  Keycloak SSO integration (auth.lab.taipei)
-  Redis session storage (10.1.0.254:6379)
  - Solves Cookie 4KB limitation
  - Cross-system session sharing
  - Sliding expiration (8 hours)
  - Automatic token refresh

Technical Stack:
Backend:
- FastAPI + SQLAlchemy
- PostgreSQL 16 (10.1.0.20:5433)
- Keycloak Admin API integration
- Docker Mailserver integration (SSH)
- Alembic migrations

Frontend:
- Next.js 14 (App Router)
- NextAuth 4 with Keycloak Provider
- Redis session storage (ioredis)
- Tailwind CSS

Infrastructure:
- Redis 7 (10.1.0.254:6379) - Session + Cache
- Keycloak 26.1.0 (auth.lab.taipei)
- Docker Mailserver (10.1.0.254)

Architecture Highlights:
- Session管理由 Keycloak + Redis 統一控制
- 支援多系統 (HR/WebMail/Calendar/Drive/Office) 共享 session
- Token 自動刷新,異質服務整合
- 未來可無縫遷移到雲端

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-23 20:12:43 +08:00

106 lines
2.7 KiB
Python

"""
HR Portal Backend API
FastAPI 主應用程式
"""
import traceback
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from app.core.config import settings
from app.core.logging_config import setup_logging
from app.db.session import get_engine
from app.db.base import Base
# 設置日誌
setup_logging()
# 創建 FastAPI 應用
app = FastAPI(
title=settings.PROJECT_NAME,
version=settings.VERSION,
description="HR Portal - 人力資源管理系統 API",
docs_url="/docs",
redoc_url="/redoc",
openapi_url="/openapi.json",
)
# CORS 設定
app.add_middleware(
CORSMiddleware,
allow_origins=settings.get_allowed_origins(),
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 全局異常處理器
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
"""全局異常處理器 - 記錄所有未捕獲的異常"""
print(f"\n{'=' * 80}")
print(f"[ERROR] Unhandled Exception in {request.method} {request.url.path}")
print(f"Exception Type: {type(exc).__name__}")
print(f"Exception Message: {str(exc)}")
print(f"Traceback:")
print(traceback.format_exc())
print(f"{'=' * 80}\n")
return JSONResponse(
status_code=500,
content={
"detail": str(exc),
"type": type(exc).__name__,
"path": request.url.path
}
)
# 啟動事件
@app.on_event("startup")
async def startup_event():
"""應用啟動時執行"""
# 資料庫表格由 Alembic 管理,不需要在啟動時創建
print(f"[OK] {settings.PROJECT_NAME} v{settings.VERSION} started!")
print(f"[*] Environment: {settings.ENVIRONMENT}")
print(f"[*] API Documentation: http://{settings.HOST}:{settings.PORT}/docs")
# 關閉事件
@app.on_event("shutdown")
async def shutdown_event():
"""應用關閉時執行"""
print(f"[*] {settings.PROJECT_NAME} stopped")
# 健康檢查端點
@app.get("/health", tags=["Health"])
async def health_check():
"""健康檢查"""
return JSONResponse(
content={
"status": "healthy",
"service": settings.PROJECT_NAME,
"version": settings.VERSION,
"environment": settings.ENVIRONMENT,
}
)
# 根路徑
@app.get("/", tags=["Root"])
async def root():
"""根路徑"""
return {
"message": f"Welcome to {settings.PROJECT_NAME}",
"version": settings.VERSION,
"docs": "/docs",
"redoc": "/redoc",
}
# 導入並註冊 API 路由
from app.api.v1.router import api_router
app.include_router(api_router, prefix="/api/v1")