bmc_hub/main.py

232 lines
8.2 KiB
Python
Raw Normal View History

2025-12-05 14:22:39 +01:00
"""
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
2025-12-05 14:22:39 +01:00
from contextlib import asynccontextmanager
from pathlib import Path
2025-12-05 14:22:39 +01:00
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
2025-12-05 14:22:39 +01:00
# 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())}")
2025-12-05 14:22:39 +01:00
logger.info("✅ System initialized successfully")
yield
# Shutdown
logger.info("👋 Shutting down...")
email_scheduler.stop()
backup_scheduler.stop()
2025-12-05 14:22:39 +01:00
# 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")
2025-12-05 14:22:39 +01:00
# 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"])
2025-12-05 14:22:39 +01:00
# 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"}
2025-12-05 14:22:39 +01:00
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/*"],
2025-12-05 14:22:39 +01:00
log_level="info"
)
else:
uvicorn.run(
"main:app",
host="0.0.0.0",
port=8000,
reload=False
)