import os from dotenv import load_dotenv, find_dotenv # ─── Load .env from project root or any parent ──────────────────────────────── dotenv_path = find_dotenv() if dotenv_path: load_dotenv(dotenv_path, override=True) # ─── Paths ──────────────────────────────────────────────────────────────────── CONFIG_DIR = os.path.dirname(os.path.abspath(__file__)) PROJECT_ROOT = os.path.dirname(CONFIG_DIR) class Config: # ─── Environment ───────────────────────────────────────────────────────────── ENV = ( os.getenv('FLASK_ENV') or os.getenv('DOCKER_ENV') or 'production' ).lower() # ─── Secret Key ────────────────────────────────────────────────────────────── if ENV == 'production': SECRET_KEY = os.getenv('SECRET_KEY') if not SECRET_KEY: raise RuntimeError( "SECRET_KEY environment variable not set! " "Generate one with `openssl rand -hex 32` and export it." ) else: # dev/test: fall back to env or a random one SECRET_KEY = os.getenv('SECRET_KEY') or os.urandom(24).hex() # ─── Uploads ──────────────────────────────────────────────────────────────── # Default to PROJECT_ROOT/static/uploads; if UPLOAD_FOLDER env is set, resolve relative to PROJECT_ROOT _env_upload = os.getenv('UPLOAD_FOLDER', '') if _env_upload: # if absolute, use directly; otherwise join to project root UPLOAD_FOLDER = _env_upload if os.path.isabs(_env_upload) else os.path.join(PROJECT_ROOT, _env_upload) else: UPLOAD_FOLDER = os.path.join(PROJECT_ROOT, "static", "uploads") MAX_CONTENT_LENGTH = int(os.getenv('MAX_CONTENT_LENGTH', 20 * 1024**3)) ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'} # ─── Celery ──────────────────────────────────────────────────────────────── CELERY_BROKER_URL = os.getenv('CELERY_BROKER_URL') if not CELERY_BROKER_URL: raise RuntimeError("CELERY_BROKER_URL environment variable not set!") CELERY_RESULT_BACKEND = os.getenv('CELERY_RESULT_BACKEND', CELERY_BROKER_URL) # ─── MySQL ────────────────────────────────────────────────────────────────── MYSQL_USER = os.getenv('MYSQL_USER') MYSQL_PASSWORD = os.getenv('MYSQL_PASSWORD') if not MYSQL_PASSWORD: raise RuntimeError("MYSQL_PASSWORD environment variable not set!") MYSQL_HOST = os.getenv('MYSQL_HOST', 'db') MYSQL_PORT = int(os.getenv('MYSQL_PORT', 3306)) MYSQL_DATABASE = os.getenv('MYSQL_DATABASE') if not MYSQL_DATABASE: raise RuntimeError("MYSQL_DATABASE environment variable not set!") SQLALCHEMY_DATABASE_URI = ( f"mysql+pymysql://{MYSQL_USER}:{MYSQL_PASSWORD}" f"@{MYSQL_HOST}:{MYSQL_PORT}/{MYSQL_DATABASE}" ) SQLALCHEMY_TRACK_MODIFICATIONS = False # ─── Cookies / Session ────────────────────────────────────────────────────── SESSION_COOKIE_SECURE = True SESSION_COOKIE_HTTPONLY = True SESSION_COOKIE_SAMESITE = 'Lax' REMEMBER_COOKIE_SECURE = True REMEMBER_COOKIE_HTTPONLY = True REMEMBER_COOKIE_SAMESITE = 'Lax' PREFERRED_URL_SCHEME = 'https' # ─── Toggles ──────────────────────────────────────────────────────────────── ENABLE_DB_SEEDING = os.getenv('ENABLE_DB_SEEDING', '0') == '1' DOCKER_ENV = os.getenv('DOCKER_ENV', 'production') # ─── Neo4j ────────────────────────────────────────────────────────────────── NEO4J_URI = os.getenv('NEO4J_URI', 'bolt://neo4j:7687') NEO4J_USER = os.getenv('NEO4J_USER', 'neo4j') NEO4J_PASSWORD = os.getenv('NEO4J_PASSWORD') if not NEO4J_PASSWORD: raise RuntimeError("NEO4J_PASSWORD environment variable not set!") # ─── Misc ────────────────────────────────────────────────────────────────── STANDARD_IMG_SIZE = tuple( map(int, os.getenv('STANDARD_IMG_SIZE', '300x200').split('x')) ) PLANT_CARDS_BASE_URL = "https://plant.cards" ALLOW_REGISTRATION = False