Skip to content
Merged
30 changes: 19 additions & 11 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,33 +1,41 @@
# Application settings
APP_ENV=development
API_TITLE=AIE-DXproject Backend
API_DEBUG=true
APP_ENV="development"
API_TITLE="AIE-DXproject Backend"
API_DEBUG="true"

# Database settings
DATABASE_URL="postgresql://user:password@host:port/dbname"

# AWS credentials (optional)
# AWS_ACCESS_KEY_ID=
# AWS_SECRET_ACCESS_KEY=
# AWS_SESSION_TOKEN=
# AWS_REGION=ap-northeast-1
# AWS_ACCESS_KEY_ID="your-access-key-id"
# AWS_SECRET_ACCESS_KEY="your-secret-access-key"
# AWS_SESSION_TOKEN="your-session-token"
# AWS_REGION="ap-northeast-1"

# Upload storage (defaults to local filesystem)
UPLOAD_BACKEND=local
UPLOAD_BACKEND="local"
UPLOAD_LOCAL_DIRECTORY="./var/uploads"
# UPLOAD_S3_BUCKET="my-upload-bucket"
# UPLOAD_BASE_PREFIX="uploads"

# LLM integration
LLM_PROVIDER=mock
LLM_PROVIDER="mock"
# Uncomment and configure when using an external LLM
# LLM_API_BASE="https://api.openai.com/v1/chat/completions"
# LLM_MODEL="gpt-4o-mini"
# LLM_API_KEY="your-api-key"
# LLM_TIMEOUT_SECONDS=15
# LLM_TIMEOUT_SECONDS="15"
# LLM_REQUEST_TEMPLATE="You are an assistant ..."

# Celery background worker
CELERY_BROKER_URL="redis://redis:6379/0"
CELERY_RESULT_BACKEND="redis://redis:6379/1"
# CELERY_TASK_ALWAYS_EAGER=true
# CELERY_TASK_ALWAYS_EAGER="true"

# Cognito authentication
COGNITO_DOMAIN="your-domain.auth.ap-northeast-1.amazoncognito.com"
COGNITO_CLIENT_ID="your-client-id"
COGNITO_LOGOUT_REDIRECT_URI="https://your-app.example.com/"

# Frontend URL
FRONTEND_URL="http://localhost:5173"
57 changes: 53 additions & 4 deletions app/api/auth.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
from fastapi import APIRouter, Request
from urllib.parse import urlencode

from fastapi import APIRouter, HTTPException, Request
from fastapi.responses import RedirectResponse
from pydantic import BaseModel

from app.core.settings import get_settings


router = APIRouter()

ALB_AUTH_COOKIE_NAMES = [
"AWSELBAuthSessionCookie",
"AWSELBAuthSessionCookie-0",
"AWSELBAuthSessionCookie-1",
"AWSELBAuthSessionCookie-2",
"AWSELBAuthSessionCookie-3",
]


class UserInfoResponse(BaseModel):
sub: str | None
Expand All @@ -25,9 +39,6 @@ def get_current_user(request: Request):
)


from app.core.settings import get_settings
from fastapi.responses import RedirectResponse

@router.get("/login", summary="Login Redirect")
def login_redirect():
"""
Expand All @@ -38,3 +49,41 @@ def login_redirect():
# 末尾のスラッシュ調整などは必要に応じて行うが、基本は設定値を信頼
target_url = settings.frontend_url
return RedirectResponse(url=target_url, status_code=302)


@router.get("/logout", tags=["Auth"])
def logout():
settings = get_settings()

if not all([
settings.cognito.domain,
settings.cognito.client_id,
settings.cognito.logout_redirect_uri,
]):
raise HTTPException(
status_code=500, detail="Cognito settings not configured"
)

params = {
"client_id": settings.cognito.client_id,
"logout_uri": settings.cognito.logout_redirect_uri,
}
cognito_logout_url = (
f"https://{settings.cognito.domain}/logout?{urlencode(params)}"
)

response = RedirectResponse(url=cognito_logout_url, status_code=302)

for cookie_name in ALB_AUTH_COOKIE_NAMES:
response.set_cookie(
key=cookie_name,
value="",
max_age=0,
expires=0,
path="/",
httponly=True,
secure=True,
samesite="lax",
)

return response
16 changes: 15 additions & 1 deletion app/core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,19 @@ class CelerySettings(BaseSettings):
task_max_retries: int = 3


class CognitoSettings(BaseSettings):
"""Cognito認証に関する設定。"""

model_config = SettingsConfigDict(
**COMMON_ENV_CONFIG,
env_prefix="COGNITO_",
)

domain: Optional[str] = None
client_id: Optional[str] = None
logout_redirect_uri: Optional[str] = None


class AppSettings(BaseSettings):
"""アプリ全体の設定。機密情報は環境変数や .env から読み込む。"""

Expand All @@ -167,13 +180,14 @@ class AppSettings(BaseSettings):
default="sqlite:///./app_dev.sqlite3", alias="DATABASE_URL"
)
frontend_url: str = Field(
default="http://localhost:3000", alias="FRONTEND_URL"
default="http://localhost:5173", alias="FRONTEND_URL"
)

aws: AWSSettings = Field(default_factory=AWSSettings)
llm: LLMSettings = Field(default_factory=LLMSettings)
storage: StorageSettings = Field(default_factory=StorageSettings)
celery: CelerySettings = Field(default_factory=CelerySettings)
cognito: CognitoSettings = Field(default_factory=CognitoSettings)

@property
def aws_credentials(self) -> Dict[str, str]:
Expand Down
24 changes: 10 additions & 14 deletions app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@

from contextlib import asynccontextmanager
from datetime import datetime
from urllib.parse import urlencode
import os

from fastapi import FastAPI
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from starlette.responses import RedirectResponse

# --- 分割したルーターと設定関連をインポート ---
from app.api import analysis, comments, courses, lectures, metrics, upload
Expand All @@ -19,7 +22,7 @@


# ----------------------------------------------------------------------
# アプリケーションファクトリ (テストの容易性のために関数化)
# アプリケーションファクトリ
# ----------------------------------------------------------------------
def create_app() -> FastAPI:
"""Creates and configures the FastAPI application instance."""
Expand All @@ -43,9 +46,7 @@ async def lifespan(app: FastAPI):
# ------------------------------------------------------------------
# Exception Handlers
# ------------------------------------------------------------------
from fastapi import Request
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from starlette.exceptions import HTTPException as StarletteHTTPException

@app.exception_handler(StarletteHTTPException)
Expand Down Expand Up @@ -102,32 +103,29 @@ async def general_exception_handler(request: Request, exc: Exception):
)

# ------------------------------------------------------------------
# 1. Middleware
# Middleware
# ------------------------------------------------------------------
from app.core.middleware import AuthMiddleware

app.add_middleware(AuthMiddleware, debug=config.debug)

# ------------------------------------------------------------------
# 2. ヘルスチェックエンドポイント
# ヘルスチェック
# ------------------------------------------------------------------
@app.get("/health", tags=["System"])
def health_check():
"""
システムの稼働状況を確認するためのヘルスチェックエンドポイント。
"""
return JSONResponse(
content={
"status": "ok",
"timestamp": datetime.now().isoformat(),
"app_name": app.title,
"environment": config.env,
# 実際にはDB接続やCeleryキューの状態チェックを追加
}
)


# ------------------------------------------------------------------
# 3. ルーターの登録
# API Routers
# ------------------------------------------------------------------
app.include_router(upload.router, prefix="/api/v1", tags=["Upload"])
app.include_router(analysis.router, prefix="/api/v1", tags=["Analysis"])
Expand All @@ -142,15 +140,13 @@ def health_check():
app.include_router(common.router, prefix="/api/v1", tags=["Common"])

from app.api import trends

app.include_router(trends.router, prefix="/api/v1", tags=["Trends"])

from app.api import dashboard

app.include_router(dashboard.router, prefix="/api/v1", tags=["Dashboard"])

return app


# グローバルなアプリケーションインスタンスを作成
# グローバルなアプリケーションインスタンス
app = create_app()
Loading
Loading