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>
This commit is contained in:
2026-02-23 20:12:43 +08:00
commit 360533393f
386 changed files with 70353 additions and 0 deletions

105
backend/app/main.py Normal file
View File

@@ -0,0 +1,105 @@
"""
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")