from pydantic_settings import BaseSettings, SettingsConfigDict from pydantic import Field, model_validator import os, secrets, pathlib class Settings(BaseSettings): model_config = SettingsConfigDict(extra='ignore', env_prefix='', case_sensitive=True) app_name: str = "GrowthOps OS" environment: str = "prod" # JWT_SECRET は未設定でも起動できるようにし、後段で生成 jwt_secret: str | None = Field(default=None, alias="JWT_SECRET") port: int = int(os.getenv("PORT", "7860")) use_internal_postgres: bool = Field(default=False, alias="USE_INTERNAL_POSTGRES") postgres_user: str = Field(default="app", alias="POSTGRES_USER") postgres_password: str = Field(default="app", alias="POSTGRES_PASSWORD") postgres_db: str = Field(default="growthops", alias="POSTGRES_DB") redis_url: str = Field(default="redis://127.0.0.1:6379/0", alias="REDIS_URL") otlp_endpoint: str | None = Field(default=None, alias="OTLP_ENDPOINT") @model_validator(mode="after") def _ensure_jwt_secret(self): if self.jwt_secret: return self path = pathlib.Path("/data/jwt_secret") if path.exists(): self.jwt_secret = path.read_text().strip() return self secret = secrets.token_urlsafe(48) try: path.parent.mkdir(parents=True, exist_ok=True) path.write_text(secret) except Exception: pass self.jwt_secret = secret return self def _sqlite_url_for_path(path: str) -> str: # 絶対パスなら "sqlite+pysqlite:////abs/path" return "sqlite+pysqlite:///" + (path if path.startswith("/") else path) def build_database_url(settings: Settings) -> str: if settings.use_internal_postgres: return ( f"postgresql+psycopg2://{settings.postgres_user}:" f"{settings.postgres_password}@127.0.0.1:5432/{settings.postgres_db}" ) # まず /data を試す try: os.makedirs("/data", exist_ok=True) testfile = "/data/.writetest" with open(testfile, "w") as f: f.write("ok") os.remove(testfile) return _sqlite_url_for_path("/data/growthops.sqlite3") except Exception: # /data が使えない環境では /tmp にフォールバック os.makedirs("/tmp", exist_ok=True) return _sqlite_url_for_path("/tmp/growthops.sqlite3")