From df49016e69cadcd2c4c9e935b3053bde54e4fe31 Mon Sep 17 00:00:00 2001 From: Rodrigo Agundez Date: Thu, 20 Nov 2025 16:35:29 +0700 Subject: [PATCH 1/2] Allow users to run locally the application and services without further configuration --- scripts/local_with_uvicorn/.env.example | 146 +++++++++++------- scripts/local_with_uvicorn/docker-compose.yml | 4 + src/app/core/config.py | 43 +++--- 3 files changed, 117 insertions(+), 76 deletions(-) diff --git a/scripts/local_with_uvicorn/.env.example b/scripts/local_with_uvicorn/.env.example index 9f3e5f4..46aeac7 100644 --- a/scripts/local_with_uvicorn/.env.example +++ b/scripts/local_with_uvicorn/.env.example @@ -1,74 +1,110 @@ # ============================================================================ -# WARNING: EXAMPLE CONFIGURATION - DO NOT USE IN PRODUCTION AS-IS +# ENVIRONMENT CONFIGURATION EXAMPLE # ============================================================================ -# This file contains example values for development/testing purposes only. # -# SECURITY CRITICAL: Before deploying to production, you MUST: -# 1. Copy this file to a .env file at the project root -# 2. Generate a new SECRET_KEY using: openssl rand -hex 32 -# 3. Change all passwords (POSTGRES_PASSWORD, ADMIN_PASSWORD, etc.) -# 4. Update all sensitive configuration values +# WARNING: +# This file contains example values for LOCAL DEVELOPMENT AND TESTING only. +# +# DOCKER COMPOSE USAGE: +# This file is NOT required when running locally with docker-compose. +# Default settings in the application code work out of the box. +# +# WHEN TO USE THIS FILE: +# - Testing with external databases or services +# - Overriding default settings found in /src/app/core/config.py +# +# DEPLOYMENTS: +# DO NOT use an .env file to modify the default settings. +# Instead, set environment variables in your deployment host or platform. +# +# SECURITY NOTE: +# Before deploying, you MUST use different sensitive values: +# • SECRET_KEY (generate a new one: openssl rand -hex 32) +# • All passwords (POSTGRES_PASSWORD, ADMIN_PASSWORD, etc.) +# • Usernames for admin and database users # -# Using these example values in production is a SECURITY RISK. # ============================================================================ # ------------- app settings ------------- -APP_NAME="My Project" -APP_DESCRIPTION="My Project Description" -APP_VERSION="0.1" -APP_BACKEND_HOST="http://localhost:8000" -APP_FRONTEND_HOST="http://localhost:3000" -CONTACT_NAME="Me" -CONTACT_EMAIL="my.email@example.com" -LICENSE_NAME="MIT" +# APP_NAME="My Project" +# APP_VERSION="0.1" +# APP_DESCRIPTION="My Project Description" +# APP_BACKEND_HOST="http://localhost:8000" +# APP_FRONTEND_HOST="http://localhost:3000" +# LICENSE_NAME="MIT" +# CONTACT_NAME="Me" +# CONTACT_EMAIL="my.email@example.com" + +# ------------- environment ------------- +# ENVIRONMENT="local" -# ------------- database ------------- -POSTGRES_USER="postgres" -POSTGRES_PASSWORD=1234 -POSTGRES_SERVER="db" -POSTGRES_PORT=5432 -POSTGRES_DB="postgres" -POSTGRES_ASYNC_PREFIX="postgresql+asyncpg://" +# ------------- crypt settings ------------- +# SECRET_KEY=de2132a4a3a029d6a93a2aefcb519f0219990f92ca258a7c5ed938a444dbe1c8 +# ALGORITHM=HS256 +# ACCESS_TOKEN_EXPIRE_MINUTES=30 +# REFRESH_TOKEN_EXPIRE_DAYS=7 -# ------------- crypt ------------- -SECRET_KEY=de2132a4a3a029d6a93a2aefcb519f0219990f92ca258a7c5ed938a444dbe1c8 -ALGORITHM=HS256 -ACCESS_TOKEN_EXPIRE_MINUTES=60 +# ------------- postgres settings ------------- +# POSTGRES_USER="myappuser" +# POSTGRES_PASSWORD=r8andomP@ssw0rd! +# POSTGRES_SERVER="10.0.0.50" +# POSTGRES_PORT=5432 +# POSTGRES_DB="myappdb" +# POSTGRES_SYNC_PREFIX="postgresql://" +# POSTGRES_ASYNC_PREFIX="postgresql+asyncpg://" -# ------------- admin ------------- -ADMIN_NAME="admin" -ADMIN_EMAIL="admin@example.com" -ADMIN_USERNAME="admin" -ADMIN_PASSWORD="Str1ngst!" +# ------------- first user settings ------------- +# ADMIN_NAME="admin" +# ADMIN_EMAIL="admin@example.com" +# ADMIN_USERNAME="admin" +# ADMIN_PASSWORD="Str1ngst!" -# ------------- redis cache ------------- -REDIS_CACHE_HOST="redis" -REDIS_CACHE_PORT=6379 +# ------------- test settings ------------- +# TEST_NAME="Tester User" +# TEST_EMAIL="test@tester.com" +# TEST_USERNAME="testeruser" +# TEST_PASSWORD="Str1ngT3st!" -# ------------- redis queue ------------- -REDIS_QUEUE_HOST="redis" -REDIS_QUEUE_PORT=6379 +# ------------- redis cache settings ------------- +# REDIS_CACHE_HOST="10.0.0.90" +# REDIS_CACHE_PORT=6379 -# ------------- redis rate limit ------------- -REDIS_RATE_LIMIT_HOST="redis" -REDIS_RATE_LIMIT_PORT=6379 +# ------------- redis queue settings ------------- +# REDIS_QUEUE_HOST="10.0.0.90" +# REDIS_QUEUE_PORT=6379 -# ------------- client side cache ------------- -CLIENT_CACHE_MAX_AGE=60 +# ------------- redis rate limiter settings ------------- +# REDIS_RATE_LIMIT_HOST="10.0.0.90" +# REDIS_RATE_LIMIT_PORT=6379 -# ------------- CORS ------------- -CORS_ORIGINS=["*"] -CORS_METHODS=["*"] -CORS_HEADERS=["*"] +# ------------- client side cache settings ------------- +# CLIENT_CACHE_MAX_AGE=60 -# ------------- test ------------- -TEST_NAME="Tester User" -TEST_EMAIL="test@tester.com" -TEST_USERNAME="testeruser" -TEST_PASSWORD="Str1ngT3st!" +# ------------- default rate limit settings ------------- +# DEFAULT_RATE_LIMIT_LIMIT=10 +# DEFAULT_RATE_LIMIT_PERIOD=3600 -# ------------- environment ------------- -ENVIRONMENT="local" +# ------------- crud admin settings ------------- +# CRUD_ADMIN_ENABLED=True +# CRUD_ADMIN_MOUNT_PATH="/admin" +# CRUD_ADMIN_ALLOWED_IPS_LIST=None +# CRUD_ADMIN_ALLOWED_NETWORKS_LIST=None +# CRUD_ADMIN_MAX_SESSIONS=10 +# CRUD_ADMIN_SESSION_TIMEOUT=1440 +# SESSION_SECURE_COOKIES=True +# CRUD_ADMIN_TRACK_EVENTS=True +# CRUD_ADMIN_TRACK_SESSIONS=True +# CRUD_ADMIN_REDIS_ENABLED=False +# CRUD_ADMIN_REDIS_HOST="localhost" +# CRUD_ADMIN_REDIS_PORT=6379 +# CRUD_ADMIN_REDIS_DB=0 +# CRUD_ADMIN_REDIS_PASSWORD=None +# CRUD_ADMIN_REDIS_SSL=False + +# ------------- CORS ------------- +# CORS_ORIGINS=["*"] +# CORS_METHODS=["*"] +# CORS_HEADERS=["*"] # ------------- first tier ------------- -TIER_NAME="free" +# TIER_NAME="free" diff --git a/scripts/local_with_uvicorn/docker-compose.yml b/scripts/local_with_uvicorn/docker-compose.yml index e41c2c9..03afcf6 100644 --- a/scripts/local_with_uvicorn/docker-compose.yml +++ b/scripts/local_with_uvicorn/docker-compose.yml @@ -31,6 +31,10 @@ services: db: image: postgres:13 + environment: + POSTGRES_USER: ${POSTGRES_USER:-postgres} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-password} + POSTGRES_DB: ${POSTGRES_DB:-postgres} env_file: - .env volumes: diff --git a/src/app/core/config.py b/src/app/core/config.py index c031243..667cddd 100644 --- a/src/app/core/config.py +++ b/src/app/core/config.py @@ -7,8 +7,15 @@ from pydantic_settings import BaseSettings, SettingsConfigDict +class EnvironmentOption(str, Enum): + LOCAL = "local" + STAGING = "staging" + PRODUCTION = "production" + + class AppSettings(BaseSettings): - APP_NAME: str = "FastAPI app" + APP_NAME: str = "My Project" + APP_VERSION: str = "0.1" APP_DESCRIPTION: str | None = None APP_VERSION: str | None = None APP_BACKEND_HOST: str = "http://localhost:8000" @@ -27,6 +34,10 @@ def validate_hosts(cls, host: str) -> str: return host +class EnvironmentSettings(BaseSettings): + ENVIRONMENT: EnvironmentOption = EnvironmentOption.LOCAL + + class CryptSettings(BaseSettings): SECRET_KEY: SecretStr = SecretStr("secret-key") ALGORITHM: str = "HS256" @@ -40,8 +51,8 @@ class DatabaseSettings(BaseSettings): class PostgresSettings(DatabaseSettings): POSTGRES_USER: str = "postgres" - POSTGRES_PASSWORD: str = "postgres" - POSTGRES_SERVER: str = "localhost" + POSTGRES_PASSWORD: str = "password" + POSTGRES_SERVER: str = "db" POSTGRES_PORT: int = 5432 POSTGRES_DB: str = "postgres" POSTGRES_ASYNC_PREFIX: str = "postgresql+asyncpg://" @@ -63,7 +74,7 @@ class FirstUserSettings(BaseSettings): ADMIN_NAME: str = "admin" ADMIN_EMAIL: str = "admin@admin.com" ADMIN_USERNAME: str = "admin" - ADMIN_PASSWORD: str = "!Ch4ng3Th1sP4ssW0rd!" + ADMIN_PASSWORD: str = "password" class TestSettings(BaseSettings): @@ -71,7 +82,7 @@ class TestSettings(BaseSettings): class RedisCacheSettings(BaseSettings): - REDIS_CACHE_HOST: str = "localhost" + REDIS_CACHE_HOST: str = "redis" REDIS_CACHE_PORT: int = 6379 @computed_field # type: ignore[prop-decorator] @@ -80,17 +91,13 @@ def REDIS_CACHE_URL(self) -> str: return f"redis://{self.REDIS_CACHE_HOST}:{self.REDIS_CACHE_PORT}" -class ClientSideCacheSettings(BaseSettings): - CLIENT_CACHE_MAX_AGE: int = 60 - - class RedisQueueSettings(BaseSettings): - REDIS_QUEUE_HOST: str = "localhost" + REDIS_QUEUE_HOST: str = "redis" REDIS_QUEUE_PORT: int = 6379 class RedisRateLimiterSettings(BaseSettings): - REDIS_RATE_LIMIT_HOST: str = "localhost" + REDIS_RATE_LIMIT_HOST: str = "redis" REDIS_RATE_LIMIT_PORT: int = 6379 @computed_field # type: ignore[prop-decorator] @@ -99,6 +106,10 @@ def REDIS_RATE_LIMIT_URL(self) -> str: return f"redis://{self.REDIS_RATE_LIMIT_HOST}:{self.REDIS_RATE_LIMIT_PORT}" +class ClientSideCacheSettings(BaseSettings): + CLIENT_CACHE_MAX_AGE: int = 60 + + class DefaultRateLimitSettings(BaseSettings): DEFAULT_RATE_LIMIT_LIMIT: int = 10 DEFAULT_RATE_LIMIT_PERIOD: int = 3600 @@ -125,16 +136,6 @@ class CRUDAdminSettings(BaseSettings): CRUD_ADMIN_REDIS_SSL: bool = False -class EnvironmentOption(str, Enum): - LOCAL = "local" - STAGING = "staging" - PRODUCTION = "production" - - -class EnvironmentSettings(BaseSettings): - ENVIRONMENT: EnvironmentOption = EnvironmentOption.LOCAL - - class CORSSettings(BaseSettings): CORS_ORIGINS: list[str] = ["*"] CORS_METHODS: list[str] = ["*"] From cb3213116fb381c09637dd1571bf5a2a842fd33b Mon Sep 17 00:00:00 2001 From: Rodrigo Agundez Date: Tue, 25 Nov 2025 15:55:59 +0800 Subject: [PATCH 2/2] Resolve duplication of app_version setting --- src/app/core/config.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/core/config.py b/src/app/core/config.py index 667cddd..6dddc93 100644 --- a/src/app/core/config.py +++ b/src/app/core/config.py @@ -17,7 +17,6 @@ class AppSettings(BaseSettings): APP_NAME: str = "My Project" APP_VERSION: str = "0.1" APP_DESCRIPTION: str | None = None - APP_VERSION: str | None = None APP_BACKEND_HOST: str = "http://localhost:8000" APP_FRONTEND_HOST: str | None = None LICENSE_NAME: str | None = None