157 lines
4.9 KiB
Python
157 lines
4.9 KiB
Python
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"
|
||
|
|
}
|