From 764c094eed600b630315a550ae9c8d8a930f5243 Mon Sep 17 00:00:00 2001 From: Edwin Eames Date: Thu, 29 Jan 2026 08:43:45 -0500 Subject: [PATCH] Updated claude big changes --- app/common/__init__.py | 0 app/common/responses.py | 53 ++++++++++++++++++++++++++++++++++++ app/constants.py | 20 ++++++++++++++ app/routers/fixstuff_auto.py | 5 ++-- database.py | 8 +++++- main.py | 36 +++++++++++++++++++----- 6 files changed, 112 insertions(+), 10 deletions(-) create mode 100644 app/common/__init__.py create mode 100644 app/common/responses.py create mode 100644 app/constants.py diff --git a/app/common/__init__.py b/app/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/common/responses.py b/app/common/responses.py new file mode 100644 index 0000000..00f1a75 --- /dev/null +++ b/app/common/responses.py @@ -0,0 +1,53 @@ +""" +Standardized API response utilities for FastAPI services. + +Usage: + from app.common.responses import error_response, success_response + + # Error responses + return error_response("User not found", 404) + return error_response("Invalid credentials", 401) + + # Success responses + return success_response({"user": user_data}) + return success_response({"message": "Created"}, 201) +""" +from fastapi.responses import JSONResponse + + +def error_response(message: str, status_code: int = 400, details: str = None) -> JSONResponse: + """ + Create a standardized error response. + + Args: + message: Human-readable error message + status_code: HTTP status code (default 400) + details: Optional additional details (e.g., exception info) + + Returns: + JSONResponse with error data + """ + content = { + "ok": False, + "error": message + } + if details: + content["details"] = details + return JSONResponse(content=content, status_code=status_code) + + +def success_response(data: dict = None, status_code: int = 200) -> JSONResponse: + """ + Create a standardized success response. + + Args: + data: Response data dictionary + status_code: HTTP status code (default 200) + + Returns: + JSONResponse with success data + """ + content = {"ok": True} + if data: + content.update(data) + return JSONResponse(content=content, status_code=status_code) diff --git a/app/constants.py b/app/constants.py new file mode 100644 index 0000000..633f34e --- /dev/null +++ b/app/constants.py @@ -0,0 +1,20 @@ +""" +EAMCO Auto API Constants + +This file contains tank configuration and other constants used throughout +the auto-delivery management service. +""" + +# Default tank size in gallons (most common residential oil tank) +DEFAULT_TANK_SIZE_GALLONS = 275 + +# Maximum fill amounts for different tank sizes (gallons we can actually fill) +# Tanks cannot be filled to 100% capacity for safety reasons +TANK_MAX_FILLS = { + 275: 240, + 330: 280, + 500: 475, +} + +# Default max fill when tank size is unknown (based on 275 gallon tank) +DEFAULT_MAX_FILL_GALLONS = 240 diff --git a/app/routers/fixstuff_auto.py b/app/routers/fixstuff_auto.py index 6035bed..b8277f8 100644 --- a/app/routers/fixstuff_auto.py +++ b/app/routers/fixstuff_auto.py @@ -9,6 +9,7 @@ from decimal import Decimal from app.models.auto import Auto_Delivery, Tickets_Auto_Delivery, Auto_Temp from app.models.delivery import Delivery +from app.constants import DEFAULT_TANK_SIZE_GALLONS logger = logging.getLogger(__name__) @@ -90,9 +91,9 @@ def estimate_customer_gallons(update_db: int): ).order_by(Tickets_Auto_Delivery.fill_date).all() # Get tank size and hot water setting - tank_size = Decimal(ad.tank_size) if ad.tank_size else Decimal('275') + tank_size = Decimal(ad.tank_size) if ad.tank_size else Decimal(DEFAULT_TANK_SIZE_GALLONS) # Adjust effective tank capacity (not filled to 100%) - if tank_size == 275: + if tank_size == DEFAULT_TANK_SIZE_GALLONS: effective_tank = Decimal('250') elif tank_size == 330: effective_tank = Decimal('300') diff --git a/database.py b/database.py index ce18e61..e11e5c0 100644 --- a/database.py +++ b/database.py @@ -19,7 +19,13 @@ url = URL.create( port=ApplicationConfig.POSTGRES_PORT ) -engine = create_engine(url) +engine = create_engine( + url, + pool_pre_ping=True, # Verify connections before use + pool_size=5, # Maintain 5 connections in pool + max_overflow=10, # Allow 10 additional connections when busy + pool_recycle=3600, # Recycle connections after 1 hour +) Session = sessionmaker(autocommit=False, autoflush=False, bind=engine) diff --git a/main.py b/main.py index 6a580b0..96620a7 100644 --- a/main.py +++ b/main.py @@ -1,9 +1,11 @@ import logging import sys +import uuid from app.routers import fixstuff_auto -from fastapi import FastAPI +from fastapi import FastAPI, Request from app.routers import main, delivery, confirm, fixstuff_customer from fastapi.middleware.cors import CORSMiddleware +from starlette.middleware.base import BaseHTTPMiddleware import os from config import load_config from sqlalchemy import create_engine, text @@ -37,8 +39,14 @@ def setup_logging(): logger = setup_logging() -# Database setup -engine = create_engine(ApplicationConfig.SQLALCHEMY_DATABASE_URI) +# Database setup with connection pooling +engine = create_engine( + ApplicationConfig.SQLALCHEMY_DATABASE_URI, + pool_pre_ping=True, + pool_size=5, + max_overflow=10, + pool_recycle=3600, +) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) def check_db_connection(): @@ -56,19 +64,29 @@ def check_db_connection(): app = FastAPI() +# Request ID middleware for request tracking/correlation +class RequestIDMiddleware(BaseHTTPMiddleware): + async def dispatch(self, request: Request, call_next): + request_id = request.headers.get("X-Request-ID") or str(uuid.uuid4())[:8] + request.state.request_id = request_id + response = await call_next(request) + response.headers["X-Request-ID"] = request_id + return response + +app.add_middleware(RequestIDMiddleware) + app.include_router(main.router) app.include_router(delivery.router) app.include_router(confirm.router) app.include_router(fixstuff_auto.router) app.include_router(fixstuff_customer.router) -# print(ApplicationConfig.origins) app.add_middleware( CORSMiddleware, allow_origins=ApplicationConfig.origins, allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], + allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"], + allow_headers=["Authorization", "Content-Type", "Accept", "Origin", "X-Requested-With"], ) @@ -87,7 +105,11 @@ async def startup_event(): logger.info("🤖🤖🤖🤖🤖 Mode: Development 🤖🤖🤖🤖🤖") elif mode in ['PRODUCTION', 'PROD']: logger.info("💀💀💀💀💀💀💀💀💀💀 ⚠️ WARNING PRODUCTION 💀💀💀💀💀💀💀💀💀💀") - logger.info(f"DB: {ApplicationConfig.SQLALCHEMY_DATABASE_URI[:30]}...") + # Sanitize DB URI to avoid logging credentials + db_uri = ApplicationConfig.SQLALCHEMY_DATABASE_URI + if '@' in db_uri: + db_uri = db_uri.split('@')[-1] # Only show host/db portion + logger.info(f"DB: ...@{db_uri[:50]}") logger.info(f"CORS: {len(ApplicationConfig.origins)} origins configured") # Test database connection