Spaces:
Sleeping
Sleeping
| """ | |
| Main FastAPI application for Tracker Microservice. | |
| """ | |
| import os | |
| from fastapi import FastAPI, Request, status | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.responses import JSONResponse | |
| from fastapi.exceptions import RequestValidationError | |
| from jose import JWTError | |
| from app.core.config import settings | |
| from app.core.logging import setup_logging, get_logger | |
| from app.nosql import connect_to_mongo, close_mongo_connection | |
| from app.postgres import connect_to_postgres, close_postgres_connection | |
| from app.tracker.attendance.router import router as attendance_router | |
| from app.tracker.tasks.router import router as tasks_router | |
| # Initialize logging first | |
| log_level = getattr(settings, 'LOG_LEVEL', 'INFO').strip().upper() | |
| if log_level not in ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']: | |
| log_level = 'INFO' | |
| setup_logging( | |
| level=log_level, | |
| format_type="colored", | |
| app_name="tracker-microservice", | |
| include_correlation=True | |
| ) | |
| logger = get_logger(__name__) | |
| # Create FastAPI app | |
| app = FastAPI( | |
| title=settings.APP_NAME, | |
| description="Employee Tracker - Attendance and Location Tracking", | |
| version=settings.APP_VERSION, | |
| docs_url="/docs", | |
| redoc_url="/redoc", | |
| root_path=os.getenv("ROOT_PATH", ""), | |
| ) | |
| # CORS middleware | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=settings.CORS_ORIGINS, | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| expose_headers=["*"], | |
| ) | |
| # Startup and shutdown events | |
| async def startup_event(): | |
| """Initialize connections on startup""" | |
| logger.info("Starting Tracker Microservice") | |
| await connect_to_mongo() | |
| await connect_to_postgres() | |
| # Create schema and tables | |
| try: | |
| from app.tracker.attendance.models import ScmAttendance | |
| conn = None | |
| try: | |
| from app.postgres import get_postgres_connection, release_postgres_connection | |
| conn = await get_postgres_connection() | |
| # Create trans schema | |
| await conn.execute("CREATE SCHEMA IF NOT EXISTS trans") | |
| logger.info("✅ TRANS schema exists") | |
| # Create table | |
| create_table_sql = """ | |
| CREATE TABLE IF NOT EXISTS trans.scm_attendance ( | |
| id UUID PRIMARY KEY, | |
| merchant_id UUID NOT NULL, | |
| employee_id UUID NOT NULL, | |
| work_date DATE NOT NULL, | |
| check_in_time BIGINT, | |
| check_in_lat DOUBLE PRECISION, | |
| check_in_lon DOUBLE PRECISION, | |
| check_in_geofence_id UUID, | |
| check_out_time BIGINT, | |
| check_out_lat DOUBLE PRECISION, | |
| check_out_lon DOUBLE PRECISION, | |
| total_minutes INTEGER, | |
| created_at TIMESTAMP DEFAULT now(), | |
| updated_at TIMESTAMP DEFAULT now(), | |
| UNIQUE (employee_id, work_date) | |
| ) | |
| """ | |
| await conn.execute(create_table_sql) | |
| logger.info("✅ scm_attendance table created/verified") | |
| # Create indexes | |
| index_sql = """ | |
| CREATE INDEX IF NOT EXISTS idx_scm_attendance_work_date | |
| ON trans.scm_attendance (employee_id, work_date) | |
| """ | |
| await conn.execute(index_sql) | |
| index_merchant_sql = """ | |
| CREATE INDEX IF NOT EXISTS idx_scm_attendance_merchant | |
| ON trans.scm_attendance (merchant_id, work_date) | |
| """ | |
| await conn.execute(index_merchant_sql) | |
| logger.info("✅ Indexes created/verified") | |
| finally: | |
| if conn: | |
| await release_postgres_connection(conn) | |
| except Exception as e: | |
| logger.error("Failed to create database schema", exc_info=e) | |
| raise | |
| logger.info("Tracker Microservice started successfully") | |
| async def shutdown_event(): | |
| """Close connections on shutdown""" | |
| logger.info("Shutting down Tracker Microservice") | |
| await close_mongo_connection() | |
| await close_postgres_connection() | |
| logger.info("Tracker Microservice shut down successfully") | |
| # Health check endpoint | |
| async def health_check(): | |
| """Health check endpoint""" | |
| return { | |
| "status": "healthy", | |
| "service": "tracker-microservice", | |
| "version": settings.APP_VERSION | |
| } | |
| # Include routers | |
| app.include_router(attendance_router, prefix="/tracker") | |
| app.include_router(tasks_router, prefix="/tracker") | |
| # Global exception handlers | |
| async def validation_exception_handler(request: Request, exc: RequestValidationError): | |
| errors = [ | |
| { | |
| "field": " -> ".join(str(loc) for loc in error["loc"]), | |
| "message": error["msg"], | |
| "type": error["type"] | |
| } | |
| for error in exc.errors() | |
| ] | |
| logger.warning( | |
| "Validation error", | |
| extra={ | |
| "path": request.url.path, | |
| "method": request.method, | |
| "error_count": len(errors), | |
| "errors": errors | |
| } | |
| ) | |
| return JSONResponse( | |
| status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, | |
| content={ | |
| "success": False, | |
| "error": "Validation Error", | |
| "errors": errors | |
| } | |
| ) | |
| async def jwt_exception_handler(request: Request, exc: JWTError): | |
| logger.warning( | |
| "JWT authentication failed", | |
| extra={ | |
| "path": request.url.path, | |
| "error": str(exc), | |
| "client_ip": request.client.host if request.client else None | |
| } | |
| ) | |
| return JSONResponse( | |
| status_code=status.HTTP_401_UNAUTHORIZED, | |
| content={ | |
| "success": False, | |
| "error": "Unauthorized", | |
| "detail": "Invalid or expired token" | |
| } | |
| ) | |
| async def general_exception_handler(request: Request, exc: Exception): | |
| logger.error( | |
| "Unhandled exception", | |
| extra={ | |
| "method": request.method, | |
| "path": request.url.path, | |
| "error": str(exc), | |
| "error_type": type(exc).__name__, | |
| "client_ip": request.client.host if request.client else None | |
| }, | |
| exc_info=True | |
| ) | |
| return JSONResponse( | |
| status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, | |
| content={ | |
| "success": False, | |
| "error": "Internal Server Error", | |
| "detail": "An unexpected error occurred" | |
| } | |
| ) | |
| if __name__ == "__main__": | |
| import uvicorn | |
| uvicorn.run( | |
| "app.main:app", | |
| host="0.0.0.0", | |
| port=int(os.getenv("PORT", "8003")), | |
| reload=True, | |
| log_level=log_level.lower() | |
| ) | |