import os from pathlib import Path from typing import List from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from backend.app.routers import ( customers, projects, finance, settings, ai_settings, email_configs, cloud_docs, cloud_doc_config, portal_links, ) from backend.app.db import Base, engine from backend.app import models # noqa: F401 - ensure models are imported def create_app() -> FastAPI: app = FastAPI( title="Ops-Core", description="Monolithic automation & business ops platform", version="0.1.0", ) # Ensure database tables exist (especially when running in Docker) @app.on_event("startup") def on_startup() -> None: Base.metadata.create_all(bind=engine) # Add new columns to finance_records if they don't exist (Module 6) try: from sqlalchemy import text with engine.connect() as conn: r = conn.execute(text("PRAGMA table_info(finance_records)")) cols = [row[1] for row in r] if "amount" not in cols: conn.execute(text("ALTER TABLE finance_records ADD COLUMN amount NUMERIC(12,2)")) if "billing_date" not in cols: conn.execute(text("ALTER TABLE finance_records ADD COLUMN billing_date DATE")) conn.commit() except Exception: pass # Add customers.tags if missing (customer tags for project 收纳) try: from sqlalchemy import text with engine.connect() as conn: r = conn.execute(text("PRAGMA table_info(customers)")) cols = [row[1] for row in r] if "tags" not in cols: conn.execute(text("ALTER TABLE customers ADD COLUMN tags VARCHAR(512)")) conn.commit() except Exception: pass # CORS raw_origins = os.getenv("CORS_ORIGINS") if raw_origins: origins: List[str] = [o.strip() for o in raw_origins.split(",") if o.strip()] else: origins = ["*"] app.add_middleware( CORSMiddleware, allow_origins=origins, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Routers app.include_router(customers.router) app.include_router(projects.router) app.include_router(finance.router) app.include_router(settings.router) app.include_router(ai_settings.router) app.include_router(email_configs.router) app.include_router(cloud_docs.router) app.include_router(cloud_doc_config.router) app.include_router(portal_links.router) # Static data mount (for quotes, contracts, finance archives, etc.) data_dir = Path("data") data_dir.mkdir(parents=True, exist_ok=True) app.mount("/data", StaticFiles(directory=str(data_dir)), name="data") return app app = create_app()