- Added a new column `subscriptions_locked` to the `customers` table to manage subscription access. - Implemented a script to create new modules from a template, including updates to various files (module.json, README.md, router.py, views.py, and migration SQL). - Developed a script to import BMC Office subscriptions from an Excel file into the database, including error handling and statistics reporting. - Created a script to lookup and update missing CVR numbers using the CVR.dk API. - Implemented a script to relink Hub customers to e-conomic customer numbers based on name matching. - Developed scripts to sync CVR numbers from Simply-CRM and vTiger to the local customers database.
203 lines
6.9 KiB
Python
203 lines
6.9 KiB
Python
"""
|
|
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
|
|
from contextlib import asynccontextmanager
|
|
|
|
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
|
|
|
|
# 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()
|
|
|
|
# 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()
|
|
|
|
# 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(emails_api.router, prefix="/api/v1", tags=["Email 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(timetracking_views.router, tags=["Frontend"])
|
|
app.include_router(emails_views.router, tags=["Frontend"])
|
|
|
|
# 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
|
|
}
|
|
|
|
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
|
|
)
|