Files

126 lines
3.8 KiB
Python
Raw Permalink Normal View History

2026-04-27 21:53:36 +12:00
import logging
2026-04-25 20:43:37 +12:00
import os
import sys
from contextlib import asynccontextmanager
from pathlib import Path
2026-04-27 21:53:36 +12:00
from threading import Lock
2026-04-25 20:43:37 +12:00
if __package__ in {None, ""}:
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import uvicorn
from app.api.access import router as access_router
2026-04-25 20:43:37 +12:00
from app.api.auth import router as auth_router
2026-04-25 22:51:36 +12:00
from app.api.client_access import router as client_access_router
from app.api.dashboard import router as dashboard_router
2026-04-29 23:05:27 +12:00
from app.api.mix_calculator import router as mix_calculator_router
2026-04-25 20:43:37 +12:00
from app.api.mixes import router as mixes_router
from app.api.powerbi import router as powerbi_router
from app.api.products import router as products_router
from app.api.raw_materials import router as raw_materials_router
from app.api.scenarios import router as scenarios_router
from app.core.config import settings
from app.db.session import Base, engine
2026-04-27 21:53:36 +12:00
from app.db.migrations import MigrationReport, bootstrap_schema, sync_tenant_ids
2026-04-25 20:43:37 +12:00
from app.seed import seed_if_empty
2026-04-27 21:53:36 +12:00
logger = logging.getLogger("data_entry_app.startup")
_database_ready = False
_database_ready_lock = Lock()
def ensure_database_ready() -> MigrationReport:
global _database_ready
if _database_ready:
return MigrationReport()
with _database_ready_lock:
if _database_ready:
return MigrationReport()
schema_report = bootstrap_schema(engine, Base.metadata)
seed_if_empty()
tenant_sync_report = sync_tenant_ids(engine)
report = MigrationReport(
created_tables=schema_report.created_tables,
added_columns=schema_report.added_columns,
synced_tenant_rows=tenant_sync_report,
)
logger.info("Database startup checks complete: %s", report.summary())
_database_ready = True
return report
2026-04-25 20:43:37 +12:00
@asynccontextmanager
async def lifespan(_: FastAPI):
2026-04-27 21:53:36 +12:00
ensure_database_ready()
2026-04-25 20:43:37 +12:00
yield
app = FastAPI(title=settings.app_name, lifespan=lifespan)
app.add_middleware(
CORSMiddleware,
2026-04-27 21:53:36 +12:00
allow_origins=list(settings.cors_allow_origins),
allow_origin_regex=settings.cors_allow_origin_regex,
2026-04-25 20:43:37 +12:00
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.include_router(auth_router)
app.include_router(access_router)
2026-04-25 22:51:36 +12:00
app.include_router(client_access_router)
app.include_router(dashboard_router)
2026-04-25 20:43:37 +12:00
app.include_router(raw_materials_router)
app.include_router(mixes_router)
2026-04-29 23:05:27 +12:00
app.include_router(mix_calculator_router)
2026-04-25 20:43:37 +12:00
app.include_router(products_router)
app.include_router(scenarios_router)
app.include_router(powerbi_router)
@app.get("/")
def root():
return {
"app": settings.app_name,
"message": "Use the operator frontend to sign in, manage raw materials, and review downstream mix and product costs.",
"workflow": [
"Sign in as an operator",
"Update raw material prices or add new materials",
"Review mix master recalculations",
"Confirm finished product pricing outputs",
],
"endpoints": {
2026-04-25 22:51:36 +12:00
"client_login": "/api/auth/client/login",
"admin_login": "/api/auth/admin/login",
2026-04-25 20:43:37 +12:00
"raw_materials": "/api/raw-materials",
"mixes": "/api/mixes",
2026-04-29 23:05:27 +12:00
"mix_calculator": "/api/mix-calculator",
2026-04-25 20:43:37 +12:00
"products": "/api/products",
"scenarios": "/api/scenarios",
2026-04-25 22:51:36 +12:00
"client_access": "/api/client-access",
2026-04-25 20:43:37 +12:00
"docs": "/docs",
},
}
@app.get("/health")
def healthcheck():
return {"status": "ok"}
if __name__ == "__main__":
2026-04-27 21:53:36 +12:00
report = ensure_database_ready()
print(f"Database startup checks complete: {report.summary()}")
2026-04-25 20:43:37 +12:00
uvicorn.run(
app,
host=os.getenv("HOST", "0.0.0.0"),
port=int(os.getenv("PORT", "8000")),
)