diff --git a/app/core/config.py b/app/core/config.py index 74d1d92..f3269f7 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -1,3 +1,4 @@ +import os from pathlib import Path from pydantic_settings import BaseSettings, SettingsConfigDict @@ -10,9 +11,14 @@ class Settings(BaseSettings): extra="ignore" ) + # API settings API_V1_STR: str = "/api/v1" PROJECT_NAME: str = "Todo Application" + # Server settings + HOST: str = "0.0.0.0" # Listen on all available interfaces + PORT: int = 8000 # Default port + # Database settings DB_DIR: Path = Path("/app") / "storage" / "db" SQLALCHEMY_DATABASE_URL: str = f"sqlite:///{DB_DIR}/db.sqlite" @@ -20,6 +26,14 @@ class Settings(BaseSettings): def __init__(self, **data): super().__init__(**data) self.DB_DIR.mkdir(parents=True, exist_ok=True) + + # Override settings from environment variables if they exist + if "PORT" in os.environ: + try: + self.PORT = int(os.environ["PORT"]) + except ValueError: + # If PORT env var isn't a valid int, keep the default + pass settings = Settings() \ No newline at end of file diff --git a/main.py b/main.py index d313dde..5c867b8 100644 --- a/main.py +++ b/main.py @@ -1,9 +1,17 @@ +import logging +import socket +import sys + import uvicorn from fastapi import FastAPI from app.api.api import api_router from app.core.config import settings +# Configure logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + app = FastAPI( title=settings.PROJECT_NAME, openapi_url=f"{settings.API_V1_STR}/openapi.json", @@ -13,5 +21,44 @@ app = FastAPI( app.include_router(api_router, prefix=settings.API_V1_STR) +def find_available_port(start_port, max_attempts=10): + """Try to find an available port starting from start_port""" + for port in range(start_port, start_port + max_attempts): + try: + # Try to create a socket with the port + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.bind((settings.HOST, port)) + # If we get here, the port is available + return port + except OSError: + # Port is already in use, try the next one + continue + + # If we get here, no ports were available in our range + port_range_end = start_port + max_attempts - 1 + logger.warning( + f"Could not find an available port in range {start_port}-{port_range_end}" + ) + return None + if __name__ == "__main__": - uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True) \ No newline at end of file + port = settings.PORT + + # Check if the port is already in use + try: + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.bind((settings.HOST, port)) + except OSError: + logger.warning( + f"Port {port} is already in use. Trying to find an available port..." + ) + port = find_available_port(port + 1) + + if port is None: + logger.error("Could not find an available port. Exiting.") + sys.exit(1) + + logger.info(f"Using alternative port: {port}") + + # Start the server with the available port + uvicorn.run("main:app", host=settings.HOST, port=port, reload=True) \ No newline at end of file