""" BMC Hub - FastAPI Application Main application entry point """ import logging from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from fastapi.responses import RedirectResponse, FileResponse from contextlib import asynccontextmanager from pathlib import Path from app.core.config import settings from app.core.database import init_db from app.core.module_loader import module_loader from app.services.email_scheduler import email_scheduler # Import CORE Feature Routers (disse forbliver hardcoded) from app.auth.backend import router as auth_api from app.auth.backend import views as auth_views from app.customers.backend import router as customers_api from app.customers.backend import views as customers_views from app.contacts.backend import router as contacts_api from app.contacts.backend import views as contacts_views from app.vendors.backend import router as vendors_api from app.vendors.backend import views as vendors_views from app.settings.backend import router as settings_api from app.settings.backend import views as settings_views from app.hardware.backend import router as hardware_api from app.billing.backend import router as billing_api from app.billing.frontend import views as billing_views from app.system.backend import router as system_api from app.dashboard.backend import views as dashboard_views from app.dashboard.backend import router as dashboard_api from app.devportal.backend import router as devportal_api from app.devportal.backend import views as devportal_views from app.timetracking.backend import router as timetracking_api from app.timetracking.frontend import views as timetracking_views from app.emails.backend import router as emails_api from app.emails.frontend import views as emails_views from app.backups.backend import router as backups_api from app.backups.frontend import views as backups_views from app.backups.backend.scheduler import backup_scheduler from app.ticket.backend import router as ticket_api from app.ticket.frontend import views as ticket_views # Configure logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.StreamHandler(), logging.FileHandler('logs/app.log') ] ) logger = logging.getLogger(__name__) @asynccontextmanager async def lifespan(app: FastAPI): """Lifecycle management - startup and shutdown""" # Startup logger.info("🚀 Starting BMC Hub...") logger.info(f"Database: {settings.DATABASE_URL}") init_db() # Start email scheduler (background job) email_scheduler.start() # Start backup scheduler (background job) backup_scheduler.start() # Load dynamic modules (hvis enabled) if settings.MODULES_ENABLED: logger.info("📦 Loading dynamic modules...") module_loader.register_modules(app) module_status = module_loader.get_module_status() logger.info(f"✅ Loaded {len(module_status)} modules: {list(module_status.keys())}") logger.info("✅ System initialized successfully") yield # Shutdown logger.info("👋 Shutting down...") email_scheduler.stop() backup_scheduler.stop() # Create FastAPI app app = FastAPI( title="BMC Hub API", description=""" Central management system for BMC Networks. **Key Features:** - Customer management - Hardware tracking - Service management - Billing integration """, version="1.0.0", lifespan=lifespan, docs_url="/api/docs", redoc_url="/api/redoc", openapi_url="/api/openapi.json" ) @app.get("/") async def root(): """Redirect root to dashboard""" return RedirectResponse(url="/dashboard") # CORS middleware app.add_middleware( CORSMiddleware, allow_origins=settings.ALLOWED_ORIGINS, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Include routers app.include_router(auth_api.router, prefix="/api/v1/auth", tags=["Authentication"]) app.include_router(customers_api.router, prefix="/api/v1", tags=["Customers"]) app.include_router(contacts_api.router, prefix="/api/v1", tags=["Contacts"]) app.include_router(vendors_api.router, prefix="/api/v1", tags=["Vendors"]) app.include_router(settings_api.router, prefix="/api/v1", tags=["Settings"]) app.include_router(hardware_api.router, prefix="/api/v1", tags=["Hardware"]) app.include_router(billing_api.router, prefix="/api/v1", tags=["Billing"]) app.include_router(system_api.router, prefix="/api/v1", tags=["System"]) app.include_router(dashboard_api.router, prefix="/api/v1/dashboard", tags=["Dashboard"]) app.include_router(devportal_api.router, prefix="/api/v1/devportal", tags=["DEV Portal"]) app.include_router(timetracking_api, prefix="/api/v1/timetracking", tags=["Time Tracking"]) app.include_router(backups_api.router, prefix="/api/v1", tags=["Backup System"]) app.include_router(emails_api.router, prefix="/api/v1", tags=["Email System"]) app.include_router(ticket_api.router, prefix="/api/v1", tags=["Ticket System"]) # Frontend Routers app.include_router(auth_views.router, tags=["Frontend"]) app.include_router(dashboard_views.router, tags=["Frontend"]) app.include_router(customers_views.router, tags=["Frontend"]) app.include_router(contacts_views.router, tags=["Frontend"]) app.include_router(vendors_views.router, tags=["Frontend"]) app.include_router(billing_views.router, tags=["Frontend"]) app.include_router(settings_views.router, tags=["Frontend"]) app.include_router(devportal_views.router, tags=["Frontend"]) app.include_router(backups_views.router, tags=["Frontend"]) app.include_router(timetracking_views.router, tags=["Frontend"]) app.include_router(emails_views.router, tags=["Frontend"]) app.include_router(ticket_views.router, prefix="/ticket", tags=["Frontend - Tickets"]) # Serve static files (UI) app.mount("/static", StaticFiles(directory="static", html=True), name="static") @app.get("/health") async def health_check(): """Health check endpoint""" return { "status": "healthy", "service": "BMC Hub", "version": "1.0.0" } @app.get("/api/v1/modules") async def list_modules(): """List alle dynamic modules og deres status""" return { "modules_enabled": settings.MODULES_ENABLED, "modules": module_loader.get_module_status() } @app.post("/api/v1/modules/{module_name}/enable") async def enable_module_endpoint(module_name: str): """Enable et modul (kræver restart)""" success = module_loader.enable_module(module_name) return { "success": success, "message": f"Modul {module_name} enabled. Restart app for at loade.", "restart_required": True } @app.post("/api/v1/modules/{module_name}/disable") async def disable_module_endpoint(module_name: str): """Disable et modul (kræver restart)""" success = module_loader.disable_module(module_name) return { "success": success, "message": f"Modul {module_name} disabled. Restart app for at unload.", "restart_required": True } @app.get("/docs/{doc_name}") async def serve_documentation(doc_name: str): """Serve markdown documentation files""" docs_dir = Path(__file__).parent / "docs" doc_path = docs_dir / doc_name # Security: Ensure path is within docs directory if not doc_path.resolve().is_relative_to(docs_dir.resolve()): return {"error": "Invalid path"} if doc_path.exists() and doc_path.suffix == ".md": return FileResponse(doc_path, media_type="text/markdown") return {"error": "Documentation not found"} if __name__ == "__main__": import uvicorn import os # Only enable reload in local development (not in Docker) enable_reload = os.getenv("ENABLE_RELOAD", "false").lower() == "true" if enable_reload: uvicorn.run( "main:app", host="0.0.0.0", port=8000, reload=True, reload_includes=["*.py"], reload_dirs=["app"], reload_excludes=[".git/*", "*.pyc", "__pycache__/*", "logs/*", "uploads/*", "data/*"], log_level="info" ) else: uvicorn.run( "main:app", host="0.0.0.0", port=8000, reload=False )