|
1 | | -from fastapi import FastAPI |
| 1 | +from fastapi import Depends, FastAPI, HTTPException, status |
2 | 2 | from fastapi.middleware.cors import CORSMiddleware |
| 3 | +from fastapi.openapi.docs import get_redoc_html, get_swagger_ui_html |
| 4 | +from fastapi.openapi.utils import get_openapi |
| 5 | +from fastapi.responses import JSONResponse |
| 6 | +from fastapi.security import HTTPBasic, HTTPBasicCredentials |
3 | 7 | from dotenv import load_dotenv |
4 | 8 | import os |
| 9 | +import secrets |
5 | 10 |
|
6 | 11 | from core.database import engine, Base |
7 | 12 | from routers import test_db, admin |
|
17 | 22 | Base.metadata.create_all(bind=engine) |
18 | 23 |
|
19 | 24 | # FastAPI 앱 생성 |
| 25 | +is_dev = os.getenv("ENV") == "development" |
20 | 26 | app = FastAPI( |
21 | 27 | title="Devfactory 친해지길바라", |
22 | 28 | description="Devfactory 친해지길바라 API 서버", |
23 | 29 | version="1.0.0", |
24 | | - docs_url="/docs", |
25 | | - redoc_url="/redoc", |
| 30 | + docs_url="/docs" if is_dev else None, |
| 31 | + redoc_url="/redoc" if is_dev else None, |
| 32 | + openapi_url="/openapi.json" if is_dev else None, |
26 | 33 | ) |
27 | 34 |
|
28 | 35 |
|
|
41 | 48 | app.include_router(test_db.test_router, prefix="/api/v1") |
42 | 49 | app.include_router(admin.admin_router, prefix="/api/v1") |
43 | 50 |
|
| 51 | +security = HTTPBasic(auto_error=False) |
| 52 | + |
| 53 | +def verify_docs_credentials(credentials: HTTPBasicCredentials | None = Depends(security)) -> None: |
| 54 | + if is_dev: |
| 55 | + return |
| 56 | + |
| 57 | + expected_user = os.getenv("SWAGGER_USER") |
| 58 | + expected_password = os.getenv("SWAGGER_PASSWORD") |
| 59 | + |
| 60 | + # If credentials aren't configured, keep docs closed by default. |
| 61 | + if not expected_user or not expected_password: |
| 62 | + raise HTTPException( |
| 63 | + status_code=status.HTTP_503_SERVICE_UNAVAILABLE, |
| 64 | + detail="Swagger credentials are not configured.", |
| 65 | + ) |
| 66 | + |
| 67 | + if credentials is None: |
| 68 | + raise HTTPException( |
| 69 | + status_code=status.HTTP_401_UNAUTHORIZED, |
| 70 | + detail="Missing credentials", |
| 71 | + headers={"WWW-Authenticate": "Basic"}, |
| 72 | + ) |
| 73 | + |
| 74 | + is_user_ok = secrets.compare_digest(credentials.username, expected_user) |
| 75 | + is_password_ok = secrets.compare_digest(credentials.password, expected_password) |
| 76 | + if not (is_user_ok and is_password_ok): |
| 77 | + raise HTTPException( |
| 78 | + status_code=status.HTTP_401_UNAUTHORIZED, |
| 79 | + detail="Invalid credentials", |
| 80 | + headers={"WWW-Authenticate": "Basic"}, |
| 81 | + ) |
| 82 | + |
| 83 | +if not is_dev: |
| 84 | + @app.get("/docs", include_in_schema=False) |
| 85 | + def get_swagger_docs(_: None = Depends(verify_docs_credentials)): |
| 86 | + return get_swagger_ui_html(openapi_url="/openapi.json", title="API Docs") |
| 87 | + |
| 88 | + @app.get("/redoc", include_in_schema=False) |
| 89 | + def get_redoc_docs(_: None = Depends(verify_docs_credentials)): |
| 90 | + return get_redoc_html(openapi_url="/openapi.json", title="API Docs") |
| 91 | + |
| 92 | + @app.get("/openapi.json", include_in_schema=False) |
| 93 | + def openapi_json(_: None = Depends(verify_docs_credentials)): |
| 94 | + return JSONResponse( |
| 95 | + get_openapi( |
| 96 | + title=app.title, |
| 97 | + version=app.version, |
| 98 | + description=app.description, |
| 99 | + routes=app.routes, |
| 100 | + ) |
| 101 | + ) |
| 102 | + |
44 | 103 |
|
45 | 104 | @app.get("/") |
46 | 105 | async def read_root(): |
|
0 commit comments