Admin-Desk2 / app /main.py
Fred808's picture
Upload main.py
3d2a7d9 verified
from fastapi import FastAPI, Request, WebSocket
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import RedirectResponse
from app.core.config import settings
from app.db.database import async_engine as engine, Base
from app.api import auth, products, orders, users, analytics, files, notifications, calendar, scheduler, maintenance, branches, staff_analytics, sessions
from app.utils.rate_limiter import rate_limiter
from app.utils.logger import log_api_request
from app.utils.tasks import run_periodic_tasks, sync_pos_metrics_task
from app.services.websocket import connect, disconnect
from app.realtime.subscriber import subscribe_order_events
from app.routes.websocket import websocket_endpoint, manager, router as websocket_router, staff_metrics_websocket
import socketio
import time
import logging
import asyncio
from typing import List, Dict, Optional
from app.db.models import ensure_tables
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Create Socket.IO server
sio = socketio.AsyncServer(async_mode='asgi', cors_allowed_origins='*')
socket_app = socketio.ASGIApp(sio)
app = FastAPI(title=settings.PROJECT_NAME, version=settings.VERSION, openapi_url=f"{settings.API_V1_STR}/openapi.json")
# Store background tasks
background_tasks = set()
# Configure CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:5173", "http://localhost:3000"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Socket.IO event handlers
@sio.event
async def connect(sid: str, environ: dict, auth: Optional[dict] = None):
logger.info(f"Client connected: {sid}")
return True
@sio.event
async def disconnect(sid: str):
logger.info(f"Client disconnected: {sid}")
@sio.event
async def message(sid: str, data: dict):
logger.info(f"Message from {sid}: {data}")
await sio.emit('message', {'response': 'Message received'}, room=sid)
# Mount Socket.IO app
app.mount("/socket.io", socket_app)
@app.get("/api/v1/health")
async def health_check() -> Dict[str, str]:
"""Public health check endpoint that doesn't require authentication"""
return {
"status": "healthy",
"version": settings.VERSION,
"service": settings.PROJECT_NAME
}
# WebSocket endpoint
@app.websocket("/ws")
async def websocket_handler(websocket: WebSocket):
await websocket_endpoint(websocket)
@app.websocket("/ws/orders")
async def orders_websocket_handler(websocket: WebSocket):
await websocket_endpoint(websocket)
# Request logging and rate limiting middleware
@app.middleware("http")
async def middleware(request: Request, call_next):
await rate_limiter.check_rate_limit(request)
start_time = time.time()
response = await call_next(request)
end_time = time.time()
duration = end_time - start_time
log_api_request(
method=request.method,
path=request.url.path,
status_code=response.status_code,
duration=duration
)
return response
# Application startup and shutdown events
@app.on_event("startup")
async def startup_event():
# Create all database tables
await ensure_tables()
# Start Redis subscriber in background
task = asyncio.create_task(subscribe_order_events(manager))
background_tasks.add(task)
task.add_done_callback(background_tasks.discard)
# Start periodic tasks
task = asyncio.create_task(run_periodic_tasks())
background_tasks.add(task)
task.add_done_callback(background_tasks.discard)
# Start POS metrics sync task
task = asyncio.create_task(sync_pos_metrics_task())
background_tasks.add(task)
task.add_done_callback(background_tasks.discard)
@app.on_event("shutdown")
async def shutdown_event():
# Cancel background tasks
for task in background_tasks:
task.cancel()
# Dashboard compatibility routes
@app.get(f"{settings.API_V1_STR}/sales")
async def sales_redirect():
return RedirectResponse(url=f"{settings.API_V1_STR}/analytics/sales")
@app.get(f"{settings.API_V1_STR}/customers")
async def customers_redirect():
return RedirectResponse(url=f"{settings.API_V1_STR}/analytics/customers")
@app.get(f"{settings.API_V1_STR}/brands")
async def brands_redirect():
return RedirectResponse(url=f"{settings.API_V1_STR}/analytics/brands")
@app.get(f"{settings.API_V1_STR}/products")
async def products_redirect():
return RedirectResponse(url=f"{settings.API_V1_STR}/analytics/products")
# Include routers
app.include_router(auth.router, prefix=f"{settings.API_V1_STR}/auth", tags=["auth"])
app.include_router(users.router, prefix=f"{settings.API_V1_STR}/users", tags=["users"])
app.include_router(products.router, prefix=f"{settings.API_V1_STR}/products", tags=["products"])
app.include_router(orders.router, prefix=f"{settings.API_V1_STR}/orders", tags=["orders"])
app.include_router(analytics.router, prefix=f"{settings.API_V1_STR}/analytics", tags=["analytics"])
app.include_router(files.router, prefix=f"{settings.API_V1_STR}/files", tags=["files"])
app.include_router(notifications.router, prefix=f"{settings.API_V1_STR}/notifications", tags=["notifications"])
app.include_router(calendar.router, prefix=f"{settings.API_V1_STR}/calendar", tags=["calendar"])
app.include_router(scheduler.router, prefix=f"{settings.API_V1_STR}/scheduler", tags=["scheduler"])
app.include_router(maintenance.router, prefix=f"{settings.API_V1_STR}/maintenance", tags=["maintenance"])
app.include_router(branches.router, prefix=f"{settings.API_V1_STR}/branches", tags=["branches"])
app.include_router(staff_analytics.router, prefix=f"{settings.API_V1_STR}/staff", tags=["staff"])
app.include_router(sessions.router, prefix=f"{settings.API_V1_STR}/sessions", tags=["sessions"])
app.include_router(websocket_router)
@app.get("/")
async def root():
return {
"message": f"Welcome to {settings.PROJECT_NAME} v{settings.VERSION}",
"docs_url": "/docs",
"openapi_url": "/openapi.json"
}