260706_1_계정, 로그인, 보안 초안 작성

This commit is contained in:
2026-07-07 19:17:13 +09:00
parent 404941c47a
commit f8633bb1fe
4696 changed files with 6110 additions and 642171 deletions
+57 -20
View File
@@ -9,24 +9,31 @@ FastAPI 애플리케이션 진입점
- 라우터 등록 (페이지별 API)
"""
import asyncio
import logging
import os
import subprocess
from contextlib import asynccontextmanager
from pathlib import Path
from fastapi import FastAPI
from fastapi import Depends, FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from A06_Login.A06_Login_Router import router as a06_login_router
from A07_Register.A07_Register_Router import router as a07_register_router
from A08_Support.A08_Support_Router import router as a08_support_router
from A09_Security.A09_Security_Router import router as a09_security_router
from B03_FileInput.B03_FileInput_Router import router as b03_file_input_router
from B04_wf1_Surface.B04_wf1_Surface_Router import router as b04_surface_router
from B05_wf2_Route.B05_wf2_Route_Router import router as b05_route_router
from B06_wf3_ProfileCross.B06_wf3_ProfileCross_Router import router as b06_section_router
from config.config_db import close_db_pool, init_db_pool
from common_util.common_util_auth import verify_session
from config.config_db import close_db_pool, get_db_pool, init_db_pool
# 설정 import
from config.config_system import (
ADMIN_EMAIL,
CORS_ORIGINS,
DEBUG,
ENVIRONMENT,
@@ -47,20 +54,15 @@ logger = logging.getLogger(__name__)
def build_frontend() -> bool:
"""프론트엔드 빌드 (npm run build from config/)"""
"""프론트엔드 빌드 (npm run build from project root)"""
root_dir = Path(__file__).resolve().parent
config_dir = root_dir / "config"
if not config_dir.exists():
logger.warning(f"⚠ config 디렉토리 없음: {config_dir}")
return False
logger.info("[Frontend] npm run build 시작...")
try:
result = subprocess.run(
"npm run build",
shell=True,
cwd=str(config_dir),
cwd=str(root_dir),
capture_output=True,
text=True,
encoding="utf-8",
@@ -82,13 +84,8 @@ def build_frontend() -> bool:
def serve_frontend_dev() -> None:
"""프론트엔드 개발 서버 실행 (npm run dev from config/) — 선택 사항"""
"""프론트엔드 개발 서버 실행 (npm run dev from project root) — 선택 사항"""
root_dir = Path(__file__).resolve().parent
config_dir = root_dir / "config"
if not config_dir.exists():
logger.warning(f"⚠ config 디렉토리 없음, 개발 서버 스킵: {config_dir}")
return
if ENVIRONMENT == "production":
logger.info("[Frontend] 프로덕션 환경: 개발 서버 실행 스킵")
@@ -99,7 +96,7 @@ def serve_frontend_dev() -> None:
subprocess.Popen(
"npm run dev",
shell=True,
cwd=str(config_dir),
cwd=str(root_dir),
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
@@ -108,6 +105,32 @@ def serve_frontend_dev() -> None:
logger.warning(f"⚠ 프론트엔드 개발 서버 실행 실패: {e}")
async def cleanup_expired_sessions() -> None:
"""매시간 만료된 세션과 사용 완료 OTP를 정리한다."""
while True:
await asyncio.sleep(3600)
pool = get_db_pool()
async with pool.acquire() as connection, connection.cursor() as cursor:
await cursor.execute("DELETE FROM sessions WHERE expires_at < CURRENT_TIMESTAMP")
await cursor.execute(
"""DELETE FROM email_otps
WHERE expires_at < CURRENT_TIMESTAMP
OR consumed_at < DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 1 DAY)"""
)
await cursor.execute(
"""DELETE FROM login_logs WHERE
(status = 'SUCCESS'
AND login_at < DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 1 YEAR))
OR (status = 'FAILURE'
AND login_at < DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 6 MONTH))"""
)
await cursor.execute(
"""DELETE FROM activity_logs
WHERE action_at < DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 2 YEAR)"""
)
await connection.commit()
# ─────────────────────────────────────────────────────────────────────────
# 라이프사이클: 앱 시작/종료
# ─────────────────────────────────────────────────────────────────────────
@@ -129,11 +152,20 @@ async def lifespan(app: FastAPI):
# DB 풀 초기화
await init_db_pool()
logger.info("✓ DB 풀 초기화 완료")
if ADMIN_EMAIL:
pool = get_db_pool()
async with pool.acquire() as connection, connection.cursor() as cursor:
await cursor.execute(
"UPDATE users SET role = 'SYSTEM_ADMIN' WHERE email = %s", (ADMIN_EMAIL.lower(),)
)
await connection.commit()
cleanup_task = asyncio.create_task(cleanup_expired_sessions())
yield
# 종료
logger.info("앱 종료 중...")
cleanup_task.cancel()
await close_db_pool()
logger.info("✓ DB 풀 종료 완료")
@@ -197,10 +229,15 @@ async def health():
# app.include_router(a01_router, prefix="/api/a01", tags=["A01_Home"])
#
# 나중에 각 페이지별 라우터가 구현되면 여기에 등록.
app.include_router(b03_file_input_router)
app.include_router(b04_surface_router)
app.include_router(b05_route_router)
app.include_router(b06_section_router)
app.include_router(a06_login_router)
app.include_router(a07_register_router)
app.include_router(a08_support_router)
app.include_router(a09_security_router)
protected = [Depends(verify_session)]
app.include_router(b03_file_input_router, dependencies=protected)
app.include_router(b04_surface_router, dependencies=protected)
app.include_router(b05_route_router, dependencies=protected)
app.include_router(b06_section_router, dependencies=protected)
# ─────────────────────────────────────────────────────────────────────────
# 앱 실행