import logging import os from contextlib import asynccontextmanager from typing import List from dotenv import load_dotenv load_dotenv() from fastapi import FastAPI, Depends, HTTPException, Path from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel from sqlalchemy.ext.asyncio import AsyncSession from src.database.config import get_db, init_db from src.auth.router import router as auth_router, admin_router as auth_admin_router from src.auth.dependencies import require_auth, require_superadmin from src.auth.models import TokenData from src.auth.password import hash_password from src.tenant import manager as tenant_manager from src.tenant.manager import TenantNotFoundError, InvalidTenantIdError from src.api.machines import router as machines_router from src.api.equipment_parts import router as equipment_parts_router logger = logging.getLogger(__name__) @asynccontextmanager async def lifespan(app: FastAPI): await init_db() logger.info("Database initialized") yield app = FastAPI(title="FactoryOps v2 API", lifespan=lifespan) app.include_router(auth_router) app.include_router(auth_admin_router) app.include_router(machines_router) app.include_router(equipment_parts_router) CORS_ORIGINS = ( os.getenv("CORS_ORIGINS", "").split(",") if os.getenv("CORS_ORIGINS") else [] ) if not CORS_ORIGINS: CORS_ORIGINS = ["http://localhost:3100", "http://127.0.0.1:3100"] app.add_middleware( CORSMiddleware, allow_origins=CORS_ORIGINS, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.get("/api/health") async def health_check(): return {"status": "ok", "version": "2.0.0"} class TenantCreate(BaseModel): id: str name: str industry_type: str = "general" @app.get("/api/tenants") async def list_accessible_tenants( current_user: TokenData = Depends(require_auth), db: AsyncSession = Depends(get_db), ): all_tenants = await tenant_manager.list_tenants(db) if current_user.role == "superadmin": return {"tenants": [t for t in all_tenants if t.get("is_active", True)]} if current_user.tenant_id: return { "tenants": [ t for t in all_tenants if t["id"] == current_user.tenant_id and t.get("is_active", True) ] } return {"tenants": []} @app.get("/api/admin/tenants") async def list_all_tenants( current_user: TokenData = Depends(require_superadmin), db: AsyncSession = Depends(get_db), ): return {"tenants": await tenant_manager.list_tenants(db)} @app.post("/api/admin/tenants") async def create_tenant( tenant: TenantCreate, current_user: TokenData = Depends(require_superadmin), db: AsyncSession = Depends(get_db), ): try: result = await tenant_manager.create_tenant( db, tenant.id, tenant.name, tenant.industry_type ) return {"status": "success", "tenant": result} except InvalidTenantIdError as e: raise HTTPException(status_code=400, detail=str(e)) except ValueError as e: raise HTTPException(status_code=409, detail=str(e)) @app.get( "/api/admin/tenants/{tenant_id}", ) async def get_tenant( tenant_id: str = Path(..., pattern=r"^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$"), current_user: TokenData = Depends(require_superadmin), db: AsyncSession = Depends(get_db), ): try: return await tenant_manager.get_tenant(db, tenant_id) except TenantNotFoundError as e: raise HTTPException(status_code=404, detail=str(e)) if __name__ == "__main__": import uvicorn uvicorn.run( "main:app", host=os.getenv("HOST", "0.0.0.0"), port=int(os.getenv("PORT", "8000")), reload=True, )