feat: Add environment configuration files and update settings for production and development
This commit is contained in:
55
.env.development.template
Normal file
55
.env.development.template
Normal file
@@ -0,0 +1,55 @@
|
||||
# Development Environment Configuration
|
||||
# Copy this file to .env for development setup
|
||||
|
||||
# Application Configuration
|
||||
HOST=localhost
|
||||
PORT=8000
|
||||
RELOAD=true
|
||||
|
||||
# Development URLs (for local development)
|
||||
FRONTEND_URL=http://localhost:8001
|
||||
BACKEND_URL=http://localhost:8000
|
||||
CORS_ORIGINS=["http://localhost:8001"]
|
||||
|
||||
# Database Configuration
|
||||
DATABASE_URL=sqlite+aiosqlite:///data/soundboard.db
|
||||
DATABASE_ECHO=false
|
||||
|
||||
# Logging Configuration
|
||||
LOG_LEVEL=debug
|
||||
LOG_FILE=logs/app.log
|
||||
LOG_MAX_SIZE=10485760
|
||||
LOG_BACKUP_COUNT=5
|
||||
|
||||
# JWT Configuration (Use a secure key even in development)
|
||||
JWT_SECRET_KEY=development-secret-key-change-for-production
|
||||
JWT_ACCESS_TOKEN_EXPIRE_MINUTES=15
|
||||
JWT_REFRESH_TOKEN_EXPIRE_DAYS=7
|
||||
|
||||
# Cookie Configuration (Development settings)
|
||||
COOKIE_SECURE=false
|
||||
COOKIE_SAMESITE=lax
|
||||
COOKIE_DOMAIN=localhost
|
||||
|
||||
# OAuth2 Configuration (Get these from OAuth providers)
|
||||
# Google: https://console.developers.google.com/
|
||||
# Redirect URI: http://localhost:8000/api/v1/auth/google/callback
|
||||
GOOGLE_CLIENT_ID=your-google-client-id
|
||||
GOOGLE_CLIENT_SECRET=your-google-client-secret
|
||||
|
||||
# GitHub: https://github.com/settings/developers
|
||||
# Redirect URI: http://localhost:8000/api/v1/auth/github/callback
|
||||
GITHUB_CLIENT_ID=your-github-client-id
|
||||
GITHUB_CLIENT_SECRET=your-github-client-secret
|
||||
|
||||
# Audio Normalization Configuration
|
||||
NORMALIZED_AUDIO_FORMAT=mp3
|
||||
NORMALIZED_AUDIO_BITRATE=256k
|
||||
NORMALIZED_AUDIO_PASSES=2
|
||||
|
||||
# Audio Extraction Configuration
|
||||
EXTRACTION_AUDIO_FORMAT=mp3
|
||||
EXTRACTION_AUDIO_BITRATE=256k
|
||||
EXTRACTION_TEMP_DIR=sounds/temp
|
||||
EXTRACTION_THUMBNAILS_DIR=sounds/originals/extracted/thumbnails
|
||||
EXTRACTION_MAX_CONCURRENT=2
|
||||
50
.env.production.template
Normal file
50
.env.production.template
Normal file
@@ -0,0 +1,50 @@
|
||||
# Production Environment Configuration
|
||||
# Copy this file to .env and configure for your production environment
|
||||
|
||||
# Application Configuration
|
||||
HOST=0.0.0.0
|
||||
PORT=8000
|
||||
RELOAD=false
|
||||
|
||||
# Production URLs (configure for your domain)
|
||||
FRONTEND_URL=https://yourdomain.com
|
||||
BACKEND_URL=https://yourdomain.com
|
||||
CORS_ORIGINS=["https://yourdomain.com"]
|
||||
|
||||
# Database Configuration (consider using PostgreSQL in production)
|
||||
DATABASE_URL=sqlite+aiosqlite:///data/soundboard.db
|
||||
DATABASE_ECHO=false
|
||||
|
||||
# Logging Configuration
|
||||
LOG_LEVEL=info
|
||||
LOG_FILE=logs/app.log
|
||||
LOG_MAX_SIZE=10485760
|
||||
LOG_BACKUP_COUNT=5
|
||||
|
||||
# JWT Configuration (IMPORTANT: Generate secure keys for production)
|
||||
JWT_SECRET_KEY=your-super-secure-secret-key-change-this-in-production
|
||||
JWT_ACCESS_TOKEN_EXPIRE_MINUTES=15
|
||||
JWT_REFRESH_TOKEN_EXPIRE_DAYS=7
|
||||
|
||||
# Cookie Configuration (Production settings)
|
||||
COOKIE_SECURE=true
|
||||
COOKIE_SAMESITE=lax
|
||||
COOKIE_DOMAIN= # Leave empty for same-origin cookies in production with reverse proxy
|
||||
|
||||
# OAuth2 Configuration (Configure with your OAuth providers)
|
||||
GOOGLE_CLIENT_ID=your-google-client-id
|
||||
GOOGLE_CLIENT_SECRET=your-google-client-secret
|
||||
GITHUB_CLIENT_ID=your-github-client-id
|
||||
GITHUB_CLIENT_SECRET=your-github-client-secret
|
||||
|
||||
# Audio Normalization Configuration
|
||||
NORMALIZED_AUDIO_FORMAT=mp3
|
||||
NORMALIZED_AUDIO_BITRATE=256k
|
||||
NORMALIZED_AUDIO_PASSES=2
|
||||
|
||||
# Audio Extraction Configuration
|
||||
EXTRACTION_AUDIO_FORMAT=mp3
|
||||
EXTRACTION_AUDIO_BITRATE=256k
|
||||
EXTRACTION_TEMP_DIR=sounds/temp
|
||||
EXTRACTION_THUMBNAILS_DIR=sounds/originals/extracted/thumbnails
|
||||
EXTRACTION_MAX_CONCURRENT=2
|
||||
@@ -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(
|
||||
|
||||
@@ -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>
|
||||
"""
|
||||
|
||||
@@ -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 = ""
|
||||
|
||||
14
app/main.py
14
app/main.py
@@ -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=["*"],
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user