from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.openapi.utils import get_openapi
from fastapi.staticfiles import StaticFiles
from scalar_fastapi import get_scalar_api_reference
from app.admin_manager import init_admin
from app.core.common import lifespan
from app.database.session import engine
# User 모델 import (테이블 메타데이터 등록용)
from app.user.models import User, RefreshToken # noqa: F401
from app.archive.api.routers.v1.archive import router as archive_router
from app.home.api.routers.v1.home import router as home_router
from app.user.api.routers.v1.auth import router as auth_router, test_router as auth_test_router
from app.user.api.routers.v1.social_account import router as social_account_router
from app.lyric.api.routers.v1.lyric import router as lyric_router
from app.song.api.routers.v1.song import router as song_router
from app.sns.api.routers.v1.sns import router as sns_router
from app.video.api.routers.v1.video import router as video_router
from app.social.api.routers.v1.oauth import router as social_oauth_router
from app.social.api.routers.v1.upload import router as social_upload_router
from app.utils.cors import CustomCORSMiddleware
from config import prj_settings
tags_metadata = [
{
"name": "Auth",
"description": """카카오 소셜 로그인 및 JWT 토큰 관리 API
## 인증 흐름
1. `GET /user/auth/kakao/login` - 카카오 로그인 URL 획득
2. 사용자를 auth_url로 리다이렉트 → 카카오 로그인
3. 카카오에서 인가 코드(code) 발급
4. `GET /user/auth/kakao/callback` - 인가 코드로 JWT 토큰 발급 (카카오 리다이렉트)
5. 이후 API 호출 시 `Authorization: Bearer {access_token}` 헤더 사용
## 토큰 관리
- **Access Token**: 1시간 유효, API 호출 시 사용
- **Refresh Token**: 7일 유효, Access Token 갱신 시 사용
## Scalar에서 인증 사용하기
1. 카카오 로그인 또는 테스트 토큰 발급으로 `access_token` 획득
2. 우측 상단 **Authorize** 버튼 클릭
3. `access_token` 값 입력 (Bearer 접두사 없이 토큰만 입력)
4. **Authorize** 클릭하여 저장
5. 이후 인증이 필요한 API 호출 시 자동으로 토큰이 포함됨
""",
},
{
"name": "Social Account",
"description": """소셜 계정 연동 API - YouTube, Instagram, Facebook, TikTok
**인증: 필요** - `Authorization: Bearer {access_token}` 헤더 필수
## 주요 기능
- `GET /user/social-accounts` - 연동된 소셜 계정 목록 조회
- `GET /user/social-accounts/{account_id}` - 소셜 계정 상세 조회
- `POST /user/social-accounts` - 소셜 계정 연동
- `PATCH /user/social-accounts/{account_id}` - 소셜 계정 정보 수정
- `DELETE /user/social-accounts/{account_id}` - 소셜 계정 연동 해제
""",
},
# {
# "name": "Home",
# "description": "홈 화면 및 프로젝트 관리 API",
# },
{
"name": "Search",
"description": """숙박/펜션 검색 API - 네이버 지역 검색 기반 자동완성
**인증: 불필요** (공개 API)
## 사용법
`GET /search/accommodation?query=스테이머뭄`
## 응답 예시
```json
{
"query": "스테이머뭄",
"count": 1,
"items": [
{
"title": "스테이,머뭄",
"address": "전북특별자치도 군산시 신흥동 63-18",
"roadAddress": "전북특별자치도 군산시 절골길 18"
}
]
}
```
""",
},
{
"name": "Crawling",
"description": """네이버 지도 크롤링 API - 장소 정보 및 이미지 수집
**인증: 불필요** (공개 API)
""",
},
{
"name": "Image-Blob",
"description": """이미지 업로드 API - Azure Blob Storage
**인증: 필요** - `Authorization: Bearer {access_token}` 헤더 필수
""",
},
{
"name": "Lyric",
"description": """가사 생성 및 관리 API
**인증: 필요** - `Authorization: Bearer {access_token}` 헤더 필수
## 가사 생성 흐름
1. `POST /lyric/generate` - 가사 생성 요청 (백그라운드 처리)
2. `GET /lyric/status/{task_id}` - 생성 상태 확인
3. `GET /lyric/{task_id}` - 생성된 가사 조회
4. `GET /lyric/list` - 가사 목록 조회 (페이지네이션)
""",
},
{
"name": "Song",
"description": """노래 생성 및 관리 API (Suno AI)
**인증: 필요** - `Authorization: Bearer {access_token}` 헤더 필수
## 노래 생성 흐름
1. `POST /song/generate/{task_id}` - 노래 생성 요청
2. `GET /song/status/{song_id}` - Suno API 상태 확인
""",
},
{
"name": "Video",
"description": """영상 생성 및 관리 API (Creatomate)
**인증: 필요** - `Authorization: Bearer {access_token}` 헤더 필수
## 영상 생성 흐름
1. `GET /video/generate/{task_id}` - 영상 생성 요청
2. `GET /video/status/{creatomate_render_id}` - Creatomate 상태 확인
3. `GET /video/download/{task_id}` - 영상 다운로드 URL 조회
4. `GET /video/list` - 영상 목록 조회 (페이지네이션)
""",
},
{
"name": "Archive",
"description": """아카이브 API - 완료된 영상 목록 조회 및 삭제
**인증: 필요** - `Authorization: Bearer {access_token}` 헤더 필수
## 주요 기능
- `GET /archive/videos/` - 완료된 영상 목록 페이지네이션 조회
- `DELETE /archive/videos/delete/{task_id}` - 아카이브 영상 소프트 삭제
## 참고
- **본인 소유의 데이터만 조회/삭제 가능합니다.**
- status가 'completed'인 영상만 반환됩니다.
- 동일한 task_id가 있는 경우 가장 최근에 생성된 1개만 반환됩니다.
- created_at 기준 내림차순 정렬됩니다.
- 삭제는 소프트 삭제(is_deleted=True) 방식으로 처리되며, 데이터 복구가 가능합니다.
- 삭제 대상: Video, SongTimestamp, Song, Lyric, Image, Project
""",
},
{
"name": "Social OAuth",
"description": """소셜 미디어 계정 연동 API
**인증: 필요** - `Authorization: Bearer {access_token}` 헤더 필수
## 지원 플랫폼
- **YouTube**: Google OAuth 2.0 기반 연동
## 연동 흐름
1. `GET /social/oauth/{platform}/connect` - OAuth 인증 URL 획득
2. 사용자를 auth_url로 리다이렉트 → 플랫폼 로그인
3. 플랫폼에서 권한 승인 후 콜백 URL로 리다이렉트
4. 연동 완료 후 프론트엔드로 리다이렉트
## 계정 관리
- `GET /social/oauth/accounts` - 연동된 계정 목록 조회
- `DELETE /social/oauth/{platform}/disconnect` - 계정 연동 해제
""",
},
{
"name": "Social Upload",
"description": """소셜 미디어 영상 업로드 API
**인증: 필요** - `Authorization: Bearer {access_token}` 헤더 필수
## 사전 조건
- 해당 플랫폼에 계정이 연동되어 있어야 합니다
- 영상이 completed 상태여야 합니다
## 업로드 흐름
1. `POST /social/upload` - 업로드 요청 (백그라운드 처리)
2. `GET /social/upload/{upload_id}/status` - 업로드 상태 폴링
3. `GET /social/upload/history` - 업로드 이력 조회
## 업로드 상태
- `pending`: 업로드 대기 중
- `uploading`: 업로드 진행 중
- `processing`: 플랫폼에서 처리 중
- `completed`: 업로드 완료
- `failed`: 업로드 실패
""",
},
{
"name": "SNS",
"description": """SNS 업로드 API - Instagram Graph API
**인증: 필요** - `Authorization: Bearer {access_token}` 헤더 필수
## 주요 기능
- `POST /sns/instagram/upload/{task_id}` - task_id에 해당하는 비디오를 Instagram에 업로드
## Instagram 업로드 흐름
1. 사용자의 Instagram 계정이 연동되어 있어야 합니다 (Social Account API 참조)
2. task_id에 해당하는 비디오가 생성 완료 상태(result_movie_url 존재)여야 합니다
3. 업로드 성공 시 Instagram media_id와 permalink 반환
""",
},
]
# DEBUG 모드에서만 Test Auth 태그 추가
if prj_settings.DEBUG:
tags_metadata.append(
{
"name": "Test Auth",
"description": """테스트용 인증 API (DEBUG 모드 전용)
**주의: 이 API는 DEBUG 모드에서만 사용 가능합니다.**
카카오 로그인 없이 테스트용 사용자 생성 및 토큰 발급이 가능합니다.
## 테스트 흐름
1. `POST /user/auth/test/create-user` - 테스트 사용자 생성
2. `POST /user/auth/test/generate-token` - JWT 토큰 발급
""",
}
)
app = FastAPI(
title=prj_settings.PROJECT_NAME,
version=prj_settings.VERSION,
description=prj_settings.DESCRIPTION,
lifespan=lifespan,
docs_url=None, # 기본 Swagger UI 비활성화
redoc_url=None, # 기본 ReDoc 비활성화
openapi_tags=tags_metadata,
)
def custom_openapi():
"""커스텀 OpenAPI 스키마 생성 (Bearer 인증 추가)"""
if app.openapi_schema:
return app.openapi_schema
openapi_schema = get_openapi(
title=app.title,
version=app.version,
description=app.description,
routes=app.routes,
tags=tags_metadata,
)
# Bearer 토큰 인증 스키마 추가
openapi_schema["components"]["securitySchemes"] = {
"BearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT",
"description": "JWT 액세스 토큰을 입력하세요. 카카오 로그인 후 발급받은 access_token을 사용합니다.",
}
}
# 인증이 필요하지 않은 엔드포인트 (공개 API)
public_endpoints = [
"/auth/kakao/login",
"/auth/kakao/callback",
"/auth/kakao/verify",
"/auth/refresh",
"/auth/test/", # 테스트 엔드포인트
"/crawling",
"/autocomplete",
"/search", # 숙박 검색 자동완성
"/social/oauth/youtube/callback", # OAuth 콜백 (플랫폼에서 직접 호출)
]
# 보안이 필요한 엔드포인트에 security 적용
for path, path_item in openapi_schema["paths"].items():
for method, operation in path_item.items():
if method in ["get", "post", "put", "patch", "delete"]:
# 공개 엔드포인트가 아닌 경우 인증 필요
is_public = any(public_path in path for public_path in public_endpoints)
if not is_public:
operation["security"] = [{"BearerAuth": []}]
app.openapi_schema = openapi_schema
return app.openapi_schema
app.openapi = custom_openapi
init_admin(app, engine)
custom_cors_middleware = CustomCORSMiddleware(app)
custom_cors_middleware.configure_cors()
app.mount("/static", StaticFiles(directory="static"), name="static")
app.mount("/media", StaticFiles(directory="media"), name="media")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
allow_credentials=True,
max_age=-1,
)
@app.get("/docs", include_in_schema=False)
def get_scalar_docs():
return get_scalar_api_reference(
openapi_url=app.openapi_url,
title="Scalar API",
)
# 예외 핸들러 등록
from app.core.exceptions import add_exception_handlers
add_exception_handlers(app)
app.include_router(home_router)
app.include_router(auth_router, prefix="/user") # Auth API 라우터 추가
app.include_router(social_account_router, prefix="/user") # Social Account API 라우터 추가
app.include_router(lyric_router)
app.include_router(song_router)
app.include_router(video_router)
app.include_router(archive_router) # Archive API 라우터 추가
app.include_router(social_oauth_router, prefix="/social") # Social OAuth 라우터 추가
app.include_router(social_upload_router, prefix="/social") # Social Upload 라우터 추가
app.include_router(sns_router) # SNS API 라우터 추가
# DEBUG 모드에서만 테스트 라우터 등록
if prj_settings.DEBUG:
app.include_router(auth_test_router, prefix="/user") # Test Auth API 라우터