feat: Add environment configuration files and update settings for production and development
Some checks failed
Backend CI / lint (push) Failing after 5m0s
Backend CI / test (push) Successful in 3m39s

This commit is contained in:
JSC
2025-08-09 14:43:20 +02:00
parent 69544b6bb8
commit 734521c5c3
9 changed files with 202 additions and 11 deletions

View File

@@ -207,14 +207,14 @@ async def logout(
httponly=True,
secure=settings.COOKIE_SECURE,
samesite=settings.COOKIE_SAMESITE,
domain="localhost", # Match the domain used when setting cookies
domain=settings.COOKIE_DOMAIN, # Match the domain used when setting cookies
)
response.delete_cookie(
key="refresh_token",
httponly=True,
secure=settings.COOKIE_SECURE,
samesite=settings.COOKIE_SAMESITE,
domain="localhost", # Match the domain used when setting cookies
domain=settings.COOKIE_DOMAIN, # Match the domain used when setting cookies
)
return {"message": "Successfully logged out"}
@@ -303,7 +303,7 @@ async def oauth_callback(
"created_at": time.time(),
}
redirect_url = f"http://localhost:8001/auth/callback?code={temp_code}"
redirect_url = f"{settings.FRONTEND_URL}/auth/callback?code={temp_code}"
logger.info("Redirecting to: %s", redirect_url)
return RedirectResponse(

View File

@@ -1,6 +1,7 @@
"""Main router for v1 endpoints."""
from fastapi import APIRouter
from fastapi.responses import HTMLResponse
from app.core.logging import get_logger
from app.schemas.common import HealthResponse
@@ -15,3 +16,69 @@ def health() -> HealthResponse:
"""Health check endpoint."""
logger.info("Health check endpoint accessed")
return HealthResponse(status="healthy")
@router.get("/scalar-docs", response_class=HTMLResponse)
def scalar_docs() -> HTMLResponse:
"""Serve the API documentation using Scalar."""
return """
<!doctype html>
<html>
<head>
<title>API Documentation - Scalar</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<script
id="api-reference"
data-url="http://localhost:8000/api/openapi.json"
src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
</body>
</html>
"""
@router.get("/rapidoc-docs", response_class=HTMLResponse)
async def rapidoc() -> HTMLResponse:
"""Serve the API documentation using Rapidoc."""
return """
<!doctype html>
<html>
<head>
<title>API Documentation - Rapidoc</title>
<meta charset="utf-8">
<script type="module" src="https://unpkg.com/rapidoc/dist/rapidoc-min.js"></script>
</head>
<body>
<rapi-doc
spec-url="http://localhost:8000/api/openapi.json"
theme="dark"
render-style="read">
</rapi-doc>
</body>
</html>
"""
@router.get("/elements-docs", response_class=HTMLResponse)
async def elements_docs() -> HTMLResponse:
"""Serve the API documentation using Stoplight Elements."""
return """
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>API Documentation - elements</title>
<script src="https://unpkg.com/@stoplight/elements/web-components.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/@stoplight/elements/styles.min.css">
</head>
<body>
<elements-api
apiDescriptionUrl="http://localhost:8000/api/openapi.json"
router="hash"
/>
</body>
</html>
"""

View File

@@ -18,6 +18,13 @@ class Settings(BaseSettings):
PORT: int = 8000
RELOAD: bool = True
# Production URLs (for reverse proxy deployment)
FRONTEND_URL: str = "http://localhost:8001" # Frontend URL in production
BACKEND_URL: str = "http://localhost:8000" # Backend base URL
# CORS Configuration
CORS_ORIGINS: list[str] = ["http://localhost:8001"] # Allowed origins for CORS
# Database Configuration
DATABASE_URL: str = "sqlite+aiosqlite:///data/soundboard.db"
DATABASE_ECHO: bool = False
@@ -38,6 +45,7 @@ class Settings(BaseSettings):
# Cookie Configuration
COOKIE_SECURE: bool = True
COOKIE_SAMESITE: Literal["strict", "lax", "none"] = "lax"
COOKIE_DOMAIN: str | None = "localhost" # Cookie domain (None for production)
# OAuth2 Configuration
GOOGLE_CLIENT_ID: str = ""

View File

@@ -6,6 +6,7 @@ from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.api import api_router
from app.core.config import settings
from app.core.database import get_session_factory, init_db
from app.core.logging import get_logger, setup_logging
from app.middleware.logging import LoggingMiddleware
@@ -47,12 +48,21 @@ async def lifespan(_app: FastAPI) -> AsyncGenerator[None, None]:
def create_app() -> FastAPI:
"""Create and configure the FastAPI application."""
app = FastAPI(lifespan=lifespan)
app = FastAPI(
title="Soundboard API",
description="API for the Soundboard application with authentication, sound management, and real-time features",
version="1.0.0",
lifespan=lifespan,
# Configure docs URLs for reverse proxy setup
docs_url="/api/docs", # Swagger UI at /api/docs
redoc_url="/api/redoc", # ReDoc at /api/redoc
openapi_url="/api/openapi.json" # OpenAPI schema at /api/openapi.json
)
# Add CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:8001"],
allow_origins=settings.CORS_ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],

View File

@@ -70,7 +70,7 @@ class OAuthProvider(ABC):
"""Generate authorization URL with state parameter."""
# Construct provider-specific redirect URI
redirect_uri = (
f"http://localhost:8000/api/v1/auth/{self.provider_name}/callback"
f"{settings.BACKEND_URL}/api/v1/auth/{self.provider_name}/callback"
)
params = {
@@ -86,7 +86,7 @@ class OAuthProvider(ABC):
"""Exchange authorization code for access token."""
# Construct provider-specific redirect URI (must match authorization request)
redirect_uri = (
f"http://localhost:8000/api/v1/auth/{self.provider_name}/callback"
f"{settings.BACKEND_URL}/api/v1/auth/{self.provider_name}/callback"
)
data = {
@@ -150,7 +150,7 @@ class GoogleOAuthProvider(OAuthProvider):
"""Exchange authorization code for access token."""
# Construct provider-specific redirect URI (must match authorization request)
redirect_uri = (
f"http://localhost:8000/api/v1/auth/{self.provider_name}/callback"
f"{settings.BACKEND_URL}/api/v1/auth/{self.provider_name}/callback"
)
data = {

View File

@@ -4,6 +4,7 @@ import logging
import socketio
from app.core.config import settings
from app.utils.auth import JWTUtils
from app.utils.cookies import extract_access_token_from_cookies
@@ -16,7 +17,7 @@ class SocketManager:
def __init__(self) -> None:
"""Initialize the SocketManager with a Socket.IO server."""
self.sio = socketio.AsyncServer(
cors_allowed_origins=["http://localhost:8001"],
cors_allowed_origins=settings.CORS_ORIGINS,
logger=True,
engineio_logger=True,
async_mode="asgi",

View File

@@ -40,7 +40,7 @@ def set_access_token_cookie(
httponly=True,
secure=settings.COOKIE_SECURE,
samesite=settings.COOKIE_SAMESITE,
domain="localhost", # Allow cookie across localhost ports
domain=settings.COOKIE_DOMAIN,
path=path,
)
@@ -58,7 +58,7 @@ def set_refresh_token_cookie(
httponly=True,
secure=settings.COOKIE_SECURE,
samesite=settings.COOKIE_SAMESITE,
domain="localhost", # Allow cookie across localhost ports
domain=settings.COOKIE_DOMAIN,
path=path,
)