d582f15681
- Implemented tests for import polling functionality, ensuring correct behavior for active and terminal import statuses. - Created tests for the login page, validating user interactions and error handling. - Added unit tests for money utility functions, covering parsing, formatting, and summation of monetary values. - Developed tests for query keys, ensuring proper scoping and invalidation roots. chore: setup testing environment and configuration - Added setup file for testing with Vitest and Testing Library. - Configured TypeScript and Vite for the frontend project. - Created Docker and Nginx configurations for local development and production environments. - Established a PostgreSQL initialization script for setting up application roles. docs: add README and environment configuration for infra - Included README for the infra directory, detailing local development setup and service descriptions. - Created an example environment configuration file for local development with placeholders for sensitive information.
80 lines
2.4 KiB
Python
80 lines
2.4 KiB
Python
from collections.abc import Awaitable, Callable
|
|
|
|
from fastapi import FastAPI, Request
|
|
from fastapi.responses import JSONResponse
|
|
from sqlalchemy import text
|
|
from sqlalchemy.orm.exc import StaleDataError
|
|
|
|
from app.analytics.router import router as analytics_router
|
|
from app.budgeting.router import router as budgeting_router
|
|
from app.db.session import get_engine
|
|
from app.errors import AppError
|
|
from app.exports.router import router as exports_router
|
|
from app.identity.router import router as identity_router
|
|
from app.imports.router import router as imports_router
|
|
from app.ledger.router import router as ledger_router
|
|
from app.spaces.router import router as spaces_router
|
|
|
|
app = FastAPI(
|
|
title="Finance Tracker API",
|
|
version="0.1.0",
|
|
docs_url="/api/docs",
|
|
openapi_url="/api/v1/openapi.json",
|
|
)
|
|
|
|
|
|
@app.exception_handler(AppError)
|
|
async def app_error_handler(_request: Request, exc: AppError) -> JSONResponse:
|
|
return JSONResponse(
|
|
status_code=exc.status_code,
|
|
content={"error": {"code": exc.code, "message": exc.message, "details": exc.details}},
|
|
)
|
|
|
|
|
|
@app.exception_handler(StaleDataError)
|
|
async def stale_data_handler(_request: Request, _exc: StaleDataError) -> JSONResponse:
|
|
return JSONResponse(
|
|
status_code=409,
|
|
content={"error": {"code": "VERSION_CONFLICT", "message": "Resource changed"}},
|
|
)
|
|
|
|
|
|
@app.middleware("http")
|
|
async def security_headers(request: Request, call_next: Callable[[Request], Awaitable]):
|
|
response = await call_next(request)
|
|
response.headers["X-Content-Type-Options"] = "nosniff"
|
|
response.headers["X-Frame-Options"] = "DENY"
|
|
response.headers["Referrer-Policy"] = "no-referrer"
|
|
response.headers["Permissions-Policy"] = "camera=(), microphone=(), geolocation=()"
|
|
response.headers["Content-Security-Policy"] = "default-src 'none'; frame-ancestors 'none'"
|
|
return response
|
|
|
|
|
|
@app.get("/health")
|
|
def health() -> dict:
|
|
return {"status": "ok"}
|
|
|
|
|
|
@app.get("/healthz", include_in_schema=False)
|
|
def healthz() -> dict:
|
|
return health()
|
|
|
|
|
|
@app.get("/ready")
|
|
def readiness() -> dict:
|
|
with get_engine().connect() as connection:
|
|
connection.execute(text("select 1"))
|
|
return {"status": "ready"}
|
|
|
|
|
|
for api_router in (
|
|
identity_router,
|
|
spaces_router,
|
|
ledger_router,
|
|
budgeting_router,
|
|
imports_router,
|
|
analytics_router,
|
|
exports_router,
|
|
):
|
|
app.include_router(api_router, prefix="/api/v1")
|