MyMidas/backend/app/main.py
megaproxy 61a7884ee5 Initial commit: MyMidas personal finance tracker
Full-stack self-hosted finance app with FastAPI backend and React frontend.

Features:
- Accounts, transactions, budgets, investments with GBP base currency
- CSV import with auto-detection for 10 UK bank formats
- ML predictions: spending forecast, net worth projection, Monte Carlo
- 7 selectable themes (Obsidian, Arctic, Midnight, Vault, Terminal, Synthwave, Ledger)
- Receipt/document attachments on transactions (JPEG, PNG, WebP, PDF)
- AES-256-GCM field encryption, RS256 JWT, TOTP 2FA, RLS, audit log
- Encrypted nightly backups + key rotation script
- Mobile-responsive layout with bottom nav

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 11:56:10 +00:00

88 lines
2.4 KiB
Python

"""
FastAPI application factory with lifespan management.
"""
from contextlib import asynccontextmanager
import structlog
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from redis.asyncio import Redis, from_url
from app.config import get_settings
from app.core.middleware import CSRFMiddleware, SecurityHeadersMiddleware
from app.db.base import create_engine, create_session_factory
from app.dependencies import set_redis_client, set_session_factory
logger = structlog.get_logger()
@asynccontextmanager
async def lifespan(app: FastAPI):
settings = get_settings()
# Database
engine = create_engine()
session_factory = create_session_factory(engine)
set_session_factory(session_factory)
# Redis
redis: Redis = from_url(settings.redis_url, decode_responses=False)
set_redis_client(redis)
# Seed system categories if needed
from app.services.category_service import seed_system_categories
async with session_factory() as db:
await seed_system_categories(db)
await db.commit()
# Background scheduler
from app.workers.scheduler import start_scheduler, stop_scheduler
await start_scheduler()
logger.info("startup_complete", env=settings.environment)
yield
await stop_scheduler()
await redis.aclose()
await engine.dispose()
logger.info("shutdown_complete")
def create_app() -> FastAPI:
settings = get_settings()
app = FastAPI(
title="Finance Tracker",
version="0.1.0",
docs_url="/docs" if settings.is_development else None,
redoc_url="/redoc" if settings.is_development else None,
openapi_url="/openapi.json" if settings.is_development else None,
lifespan=lifespan,
)
# CORS — only allow same origin in production
origins = ["http://localhost:5173"] if settings.is_development else []
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.add_middleware(SecurityHeadersMiddleware)
app.add_middleware(CSRFMiddleware)
# Health check (no auth required)
@app.get("/health")
async def health():
return {"status": "ok"}
# API routers
from app.api.router import router
app.include_router(router, prefix="/api/v1")
return app
app = create_app()