""" BMC Hub - FastAPI Application Main application entry point """ import logging from pathlib import Path from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from fastapi.responses import RedirectResponse from contextlib import asynccontextmanager from app.core.config import settings from app.core.database import init_db def get_version(): """Read version from VERSION file""" try: version_file = Path(__file__).parent / "VERSION" return version_file.read_text().strip() except Exception: return "unknown" # Import Feature Routers from app.customers.backend import router as customers_api from app.customers.backend import views as customers_views from app.customers.backend import bmc_office_router # from app.hardware.backend import router as hardware_api # Replaced by hardware module 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.system.backend import sync_router from app.dashboard.backend import views as dashboard_views from app.prepaid.backend import router as prepaid_api from app.prepaid.backend import views as prepaid_views from app.ticket.backend import router as ticket_api from app.ticket.frontend import views as ticket_views from app.vendors.backend import router as vendors_api from app.vendors.backend import views as vendors_views from app.timetracking.backend import router as timetracking_api from app.timetracking.frontend import views as timetracking_views from app.contacts.backend import views as contacts_views from app.contacts.backend import router_simple as contacts_api from app.tags.backend import router as tags_api from app.tags.backend import views as tags_views from app.emails.backend import router as emails_api from app.emails.frontend import views as emails_views from app.settings.backend import router as settings_api from app.settings.backend import views as settings_views from app.backups.backend.router import router as backups_api from app.backups.frontend import views as backups_views from app.backups.backend.scheduler import backup_scheduler from app.conversations.backend import router as conversations_api from app.conversations.frontend import views as conversations_views from app.opportunities.backend import router as opportunities_api from app.opportunities.frontend import views as opportunities_views # Modules from app.modules.webshop.backend import router as webshop_api from app.modules.webshop.frontend import views as webshop_views from app.modules.sag.backend import router as sag_api from app.modules.sag.frontend import views as sag_views from app.modules.hardware.backend import router as hardware_module_api from app.modules.hardware.frontend import views as hardware_module_views from app.modules.locations.backend import router as locations_api from app.modules.locations.frontend import views as locations_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 unified scheduler (handles backups + email fetch) backup_scheduler.start() logger.info("✅ System initialized successfully") yield # Shutdown backup_scheduler.stop() logger.info("👋 Shutting down...") # 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" ) # CORS middleware - use CORS_ORIGINS if set, otherwise fallback to ALLOWED_ORIGINS cors_origins = settings.CORS_ORIGINS.split(",") if settings.CORS_ORIGINS else settings.ALLOWED_ORIGINS app.add_middleware( CORSMiddleware, allow_origins=cors_origins, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Include routers app.include_router(customers_api.router, prefix="/api/v1", tags=["Customers"]) app.include_router(bmc_office_router.router, prefix="/api/v1", tags=["BMC Office"]) # app.include_router(hardware_api.router, prefix="/api/v1", tags=["Hardware"]) # Replaced by hardware module 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(sync_router.router, prefix="/api/v1/system", tags=["System Sync"]) app.include_router(prepaid_api.router, prefix="/api/v1", tags=["Prepaid Cards"]) app.include_router(ticket_api.router, prefix="/api/v1/ticket", tags=["Tickets"]) app.include_router(vendors_api.router, prefix="/api/v1", tags=["Vendors"]) app.include_router(contacts_api.router, prefix="/api/v1", tags=["Contacts"]) app.include_router(timetracking_api, prefix="/api/v1", tags=["Time Tracking"]) app.include_router(tags_api.router, prefix="/api/v1", tags=["Tags"]) app.include_router(emails_api.router, prefix="/api/v1", tags=["Emails"]) app.include_router(settings_api.router, prefix="/api/v1", tags=["Settings"]) app.include_router(backups_api, prefix="/api/v1", tags=["Backups"]) app.include_router(conversations_api.router, prefix="/api/v1", tags=["Conversations"]) app.include_router(opportunities_api.router, prefix="/api/v1", tags=["Opportunities"]) # Module Routers app.include_router(webshop_api.router, prefix="/api/v1", tags=["Webshop"]) app.include_router(sag_api.router, prefix="/api/v1", tags=["Cases"]) app.include_router(hardware_module_api.router, prefix="/api/v1", tags=["Hardware Module"]) app.include_router(locations_api, prefix="/api/v1", tags=["Locations"]) # Frontend Routers app.include_router(dashboard_views.router, tags=["Frontend"]) app.include_router(customers_views.router, tags=["Frontend"]) app.include_router(prepaid_views.router, tags=["Frontend"]) app.include_router(vendors_views.router, tags=["Frontend"]) app.include_router(timetracking_views.router, tags=["Frontend"]) app.include_router(billing_views.router, tags=["Frontend"]) app.include_router(ticket_views.router, prefix="/ticket", tags=["Frontend"]) app.include_router(contacts_views.router, tags=["Frontend"]) app.include_router(tags_views.router, tags=["Frontend"]) app.include_router(settings_views.router, tags=["Frontend"]) app.include_router(emails_views.router, tags=["Frontend"]) app.include_router(backups_views.router, tags=["Frontend"]) app.include_router(conversations_views.router, tags=["Frontend"]) app.include_router(webshop_views.router, tags=["Frontend"]) app.include_router(opportunities_views.router, tags=["Frontend"]) app.include_router(sag_views.router, tags=["Frontend"]) app.include_router(hardware_module_views.router, tags=["Frontend"]) app.include_router(locations_views.router, tags=["Frontend"]) # Serve static files (UI) app.mount("/static", StaticFiles(directory="static", html=True), name="static") app.mount("/docs", StaticFiles(directory="docs"), name="docs") @app.get("/health") async def health_check(): """Health check endpoint""" return { "status": "healthy", "service": "BMC Hub", "version": get_version() } if __name__ == "__main__": import uvicorn import os # Only enable reload in local development (not in Docker) - check both variables enable_reload = ( os.getenv("ENABLE_RELOAD", "false").lower() == "true" or os.getenv("API_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 )