Full-stack mobile app for pole dance championship management. Backend: FastAPI + SQLAlchemy 2 (async) + SQLite (dev) / PostgreSQL (prod) - JWT auth with refresh token rotation - Championship CRUD with Instagram Graph API sync (APScheduler) - Registration flow with status management - Participant list publish with Expo push notifications - Alembic migrations, pytest test suite Mobile: React Native + Expo (TypeScript) - Auth gate: pending approval screen for new members - Championships list & detail screens - Registration form with status tracking - React Query + Zustand + React Navigation v6 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
57 lines
1.4 KiB
Python
57 lines
1.4 KiB
Python
from contextlib import asynccontextmanager
|
|
|
|
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
|
from fastapi import FastAPI
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
from app.config import settings
|
|
from app.routers import auth, championships, participant_lists, registrations, users
|
|
from app.services.instagram_service import poll_instagram, refresh_instagram_token
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI):
|
|
scheduler = AsyncIOScheduler()
|
|
scheduler.add_job(
|
|
poll_instagram,
|
|
"interval",
|
|
seconds=settings.instagram_poll_interval,
|
|
id="instagram_poll",
|
|
)
|
|
scheduler.add_job(
|
|
refresh_instagram_token,
|
|
"interval",
|
|
weeks=1,
|
|
id="instagram_token_refresh",
|
|
)
|
|
scheduler.start()
|
|
yield
|
|
scheduler.shutdown()
|
|
|
|
|
|
app = FastAPI(
|
|
title="Pole Dance Championships API",
|
|
version="1.0.0",
|
|
lifespan=lifespan,
|
|
)
|
|
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=["*"], # tighten in Phase 7
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
PREFIX = "/api/v1"
|
|
app.include_router(auth.router, prefix=PREFIX)
|
|
app.include_router(users.router, prefix=PREFIX)
|
|
app.include_router(championships.router, prefix=PREFIX)
|
|
app.include_router(registrations.router, prefix=PREFIX)
|
|
app.include_router(participant_lists.router, prefix=PREFIX)
|
|
|
|
|
|
@app.get("/internal/health", tags=["internal"])
|
|
async def health():
|
|
return {"status": "ok"}
|