significant restructuring, format all code

This commit is contained in:
Micha R. Albert 2025-07-08 11:04:36 -04:00
parent 16e840fb78
commit 43b2e33551
Signed by: mra
SSH key fingerprint: SHA256:2JB0fGfy7m2HQXAzvSXXKm7wPTj9Z60MOjFOQGM2Y/E
16 changed files with 698 additions and 449 deletions

View file

@ -1,24 +1,29 @@
"""FastAPI application main entry point."""
"""Random Access API main entry point."""
import asyncio
import logging
from collections import namedtuple
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request, Response, HTTPException
from fastapi.responses import JSONResponse
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from slack_bolt.adapter.fastapi.async_handler import AsyncSlackRequestHandler
from slowapi import Limiter
from slowapi.errors import RateLimitExceeded
from slowapi.middleware import SlowAPIMiddleware
from slack_bolt.adapter.fastapi.async_handler import AsyncSlackRequestHandler
from random_access.database import get_airtable_base, get_table, airtable_write_worker
from random_access.database import (
airtable_write_worker,
get_airtable_base,
get_table,
validate_all_schemas,
)
from random_access.routes.auth import create_auth_router
from random_access.routes.items import create_items_router, create_user_items_router
from random_access.routes.system import create_system_router
from random_access.routes.users import create_users_router
from random_access.security import get_client_ip, SecurityHeaders
from random_access.security import SecurityHeaders, get_client_ip
from random_access.settings import settings
from random_access.slack_integration import create_slack_app, setup_slack_handlers
@ -36,6 +41,7 @@ USERS = get_table(at_base, "users")
SESSIONS = get_table(at_base, "sessions")
ITEMS = get_table(at_base, "items")
ITEM_ADDONS = get_table(at_base, "item_addons")
ITEM_INSTANCES = get_table(at_base, "item_instances")
slack = create_slack_app()
setup_slack_handlers(slack)
@ -45,20 +51,35 @@ slack_handler = AsyncSlackRequestHandler(slack)
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Application lifespan manager."""
# Validate table schemas on startup
logger.info("Validating Airtable schemas...")
schema_valid = await validate_all_schemas()
if not schema_valid:
if settings.is_production:
logger.error("Schema validation failed in production - startup aborted")
raise RuntimeError("Table schema validation failed")
else:
logger.warning(
"Schema validation failed in development - continuing anyway"
)
# Start the write worker
asyncio.create_task(airtable_write_worker())
yield
app = FastAPI(
title="Random Access API",
version="0.1.0",
lifespan=lifespan,
description="Secure API for Random Access game integration"
title="Random Access API",
version="0.1.0",
lifespan=lifespan,
description="API for Random Access game integration",
)
# Security middleware
app.state.limiter = limiter
# Custom rate limit exception handler
@app.exception_handler(RateLimitExceeded)
async def rate_limit_handler(request: Request, exc: RateLimitExceeded):
@ -67,11 +88,14 @@ async def rate_limit_handler(request: Request, exc: RateLimitExceeded):
status_code=429,
content={"detail": f"Rate limit exceeded: {exc.detail}"},
headers={
"X-RateLimit-Limit": str(getattr(exc, 'limit', settings.rate_limit_requests)),
"Retry-After": "60" # Default retry after 60 seconds
}
"X-RateLimit-Limit": str(
getattr(exc, "limit", settings.rate_limit_requests)
),
"Retry-After": "60", # Default retry after 60 seconds
},
)
app.add_middleware(SlowAPIMiddleware)
# CORS middleware - allows all origins for game compatibility but with secure settings
@ -85,22 +109,24 @@ app.add_middleware(
max_age=3600, # Cache preflight requests for 1 hour
)
# Security headers middleware
@app.middleware("http")
async def add_security_headers(request: Request, call_next):
"""Add security headers to all responses."""
response = await call_next(request)
# Add security headers
security_headers = SecurityHeaders.get_security_headers()
for header, value in security_headers.items():
response.headers[header] = value
# Add rate limit headers
response.headers["X-Content-Security-Policy"] = "default-src 'self'"
return response
# Request size limit middleware
@app.middleware("http")
async def limit_request_size(request: Request, call_next):
@ -110,21 +136,23 @@ async def limit_request_size(request: Request, call_next):
content_length = int(content_length)
if content_length > settings.max_request_size:
from fastapi import HTTPException, status
raise HTTPException(
status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
detail=f"Request too large. Maximum size: {settings.max_request_size} bytes"
detail=f"Request too large. Maximum size: {settings.max_request_size} bytes",
)
response = await call_next(request)
return response
# Include routers
routers = [
create_auth_router(SESSIONS, USERS, SUBMISSIONS, slack),
create_users_router(SESSIONS, USERS),
create_items_router(SESSIONS, USERS, ITEMS, ITEM_ADDONS),
create_user_items_router(SESSIONS, USERS, ITEMS, ITEM_ADDONS),
create_system_router(slack_handler)
create_items_router(SESSIONS, USERS, ITEMS, ITEM_ADDONS, ITEM_INSTANCES),
create_user_items_router(SESSIONS, USERS, ITEMS, ITEM_ADDONS, ITEM_INSTANCES),
create_system_router(slack_handler),
]
for router in routers: