refactor: portfolio → personal 리네이밍 + Blog/Todo 통합

- portfolio/ 디렉토리를 personal/로 리네이밍
- lotto-backend의 Blog/Todo 라우트·CRUD를 personal 서비스로 이전
- lotto-backend에서 Blog/Todo 코드 제거 (DB 테이블 스키마는 유지)
- nginx: /api/todos, /api/blog/ 라우팅을 personal로 추가
- docker-compose: portfolio → personal 서비스 변��
- deploy 스크립트: portfolio → personal 반영

데이터 마이그레이션은 배포 후 NAS에서 별도 수행 필요:
1. cp data/portfolio/portfolio.db data/personal/personal.db
2. sqlite3 data/lotto.db ".dump todos" | sqlite3 data/personal/personal.db
3. sqlite3 data/lotto.db ".dump blog_posts" | sqlite3 data/personal/personal.db

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-27 16:32:55 +09:00
parent 6004bcf66d
commit e3d5eaf6f3
15 changed files with 516 additions and 263 deletions

39
personal/app/auth.py Normal file
View File

@@ -0,0 +1,39 @@
import os
import uuid
import time
import logging
from fastapi import Header, HTTPException
logger = logging.getLogger("portfolio")
EDIT_PASSWORD = os.getenv("PORTFOLIO_EDIT_PASSWORD", "")
TOKEN_TTL = 86400 # 24시간
_tokens: dict[str, float] = {} # token -> expiry timestamp
def authenticate(password: str) -> dict:
if not EDIT_PASSWORD:
raise HTTPException(status_code=503, detail="Edit password not configured")
if password != EDIT_PASSWORD:
raise HTTPException(status_code=401, detail="Invalid password")
token = uuid.uuid4().hex
_tokens[token] = time.time() + TOKEN_TTL
_cleanup()
return {"token": token, "expires_in": TOKEN_TTL}
def require_auth(authorization: str = Header("")):
token = authorization.replace("Bearer ", "").strip()
if not token or token not in _tokens:
raise HTTPException(status_code=401, detail="Authentication required")
if time.time() > _tokens[token]:
del _tokens[token]
raise HTTPException(status_code=401, detail="Token expired")
def _cleanup():
now = time.time()
expired = [t for t, exp in _tokens.items() if now > exp]
for t in expired:
del _tokens[t]