import os import importlib import pkgutil from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from sqlalchemy.orm import Session from app.core.config import settings from app.core.database import Base, engine, SessionLocal from app.core.security import get_password_hash from app.auth.models import AppUser, Role from app.menu.models import GrpMenuAccess from app.auth.router import router as auth_router from app.menu.router import router as menu_router def init_db_and_seed(): """Creates database tables and seeds initial administrator & menu access.""" Base.metadata.create_all(bind=engine) # Auto-migration safeguard for existing databases try: from sqlalchemy import text with engine.connect() as conn: conn.execute(text("ALTER TABLE app_users ADD COLUMN random_no VARCHAR(50);")) conn.commit() except Exception: pass db: Session = SessionLocal() try: # 1. Seed Roles admin_role = db.query(Role).filter(Role.name == "ROLE_ADMIN").first() if not admin_role: admin_role = Role(name="ROLE_ADMIN", description="Admin") db.add(admin_role) user_role = db.query(Role).filter(Role.name == "ROLE_USER").first() if not user_role: user_role = Role(name="ROLE_USER", description="User") db.add(user_role) db.commit() # 2. Seed Admin User admin_user = db.query(AppUser).filter(AppUser.email == "admin@cns.com").first() if not admin_user: admin_user = AppUser( email="admin@cns.com", username="admin", password=get_password_hash("admin123"), full_name="Admin User", first_name="Admin", last_name="User", status="ACTIVE", user_grp_id=1, is_active=True ) admin_user.roles.append(admin_role) db.add(admin_user) db.commit() # 3. Seed Default Menu Items root_menu = db.query(GrpMenuAccess).filter(GrpMenuAccess.menu_id == 0, GrpMenuAccess.menu_item_id == 1).first() if not root_menu: root_menu = GrpMenuAccess( menu_id=0, menu_item_id=1, menu_item_desc="Core Modules", module_name="Core", main_menu_action_name="/cns-portal", main_menu_icon_name="cog", item_seq=1, status="A", role="Admin", usr_grp=1 ) db.add(root_menu) db.commit() # Submenu pointing to Azzu entity sub_menu = GrpMenuAccess( menu_id=1, menu_item_id=2, menu_item_desc="Azzu Management", module_name="Core", main_menu_action_name="/cns-portal/azzu", main_menu_icon_name="file", item_seq=1, status="A", role="Admin", usr_grp=1 ) db.add(sub_menu) db.commit() finally: db.close() def auto_discover_entity_routers(app: FastAPI): """ Automatically discovers and includes any entity controllers located under app/io8ref/*/controllers.py """ io8ref_dir = os.path.join(os.path.dirname(__file__), "io8ref") if not os.path.exists(io8ref_dir): return for item in os.listdir(io8ref_dir): entity_dir = os.path.join(io8ref_dir, item) if os.path.isdir(entity_dir) and os.path.exists(os.path.join(entity_dir, "controllers.py")): try: module = importlib.import_module(f"app.io8ref.{item}.controllers") if hasattr(module, "router"): app.include_router(module.router) print(f"✓ Registered Entity Router: {item}") except Exception as e: print(f"✗ Failed to load entity router {item}: {e}") @asynccontextmanager async def lifespan(app: FastAPI): # Startup: init_db_and_seed() yield # Shutdown app = FastAPI( title=settings.PROJECT_NAME, description="Drop-in Python FastAPI Base Project compatible with Angular Clarity, React, and Code Extractor V2", version="1.0.0", lifespan=lifespan ) # CORS Middleware app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Core Routers app.include_router(auth_router) app.include_router(menu_router) # Dynamically Register Generated Entities auto_discover_entity_routers(app) @app.get("/", tags=["Health"]) @app.get("/health", tags=["Health"]) def health_check(): return { "status": "UP", "service": settings.PROJECT_NAME, "docs_url": "/docs" }