diff --git a/backend/common/core/config.py b/backend/common/core/config.py new file mode 100644 index 0000000..de7fc7b --- /dev/null +++ b/backend/common/core/config.py @@ -0,0 +1,96 @@ +import secrets +from typing import Annotated, Any, Literal + +from pydantic import ( + AnyUrl, + BeforeValidator, + PostgresDsn, + computed_field, +) +from pydantic_core import MultiHostUrl +from pydantic_settings import BaseSettings, SettingsConfigDict + + +def parse_cors(v: Any) -> list[str] | str: + if isinstance(v, str) and not v.startswith("["): + return [i.strip() for i in v.split(",")] + elif isinstance(v, list | str): + return v + raise ValueError(v) + + +class Settings(BaseSettings): + model_config = SettingsConfigDict( + # Use top level .env file (one level above ./backend/) + env_file="../.env", + env_ignore_empty=True, + extra="ignore", + ) + PROJECT_NAME: str = "SQLBot" + API_V1_STR: str = "/api/v1" + SECRET_KEY: str = secrets.token_urlsafe(32) + # 60 minutes * 24 hours * 8 days = 8 days + ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 8 + FRONTEND_HOST: str = "http://localhost:5173" + + BACKEND_CORS_ORIGINS: Annotated[ + list[AnyUrl] | str, BeforeValidator(parse_cors) + ] = [] + + @computed_field # type: ignore[prop-decorator] + @property + def all_cors_origins(self) -> list[str]: + return [str(origin).rstrip("/") for origin in self.BACKEND_CORS_ORIGINS] + [ + self.FRONTEND_HOST + ] + + POSTGRES_SERVER: str = 'localhost' + POSTGRES_PORT: int = 5432 + POSTGRES_USER: str = 'root' + POSTGRES_PASSWORD: str = "Password123@pg" + POSTGRES_DB: str = "sqlbot" + SQLBOT_DB_URL: str = '' + # SQLBOT_DB_URL: str = 'mysql+pymysql://root:Password123%40mysql@127.0.0.1:3306/sqlbot' + + TOKEN_KEY: str = "X-SQLBOT-TOKEN" + DEFAULT_PWD: str = "SQLBot@123456" + ASSISTANT_TOKEN_KEY: str = "X-SQLBOT-ASSISTANT-TOKEN" + + CACHE_TYPE: Literal["redis", "memory", "None"] = "memory" + CACHE_REDIS_URL: str | None = None # Redis URL, e.g., "redis://[[username]:[password]]@localhost:6379/0" + + LOG_LEVEL: str = "INFO" # DEBUG, INFO, WARNING, ERROR + LOG_DIR: str = "logs" + LOG_FORMAT: str = "%(asctime)s - %(name)s - %(levelname)s:%(lineno)d - %(message)s" + SQL_DEBUG: bool = False + + UPLOAD_DIR: str = "/opt/sqlbot/data/file" + SQLBOT_KEY_EXPIRED: int = 100 # License key expiration timestamp, 0 means no expiration + + @computed_field # type: ignore[prop-decorator] + @property + def SQLALCHEMY_DATABASE_URI(self) -> PostgresDsn | str: + if self.SQLBOT_DB_URL: + return self.SQLBOT_DB_URL + return MultiHostUrl.build( + scheme="postgresql+psycopg", + username=self.POSTGRES_USER, + password=self.POSTGRES_PASSWORD, + host=self.POSTGRES_SERVER, + port=self.POSTGRES_PORT, + path=self.POSTGRES_DB, + ) + + MCP_IMAGE_PATH: str = '/opt/sqlbot/images' + EXCEL_PATH: str = '/opt/sqlbot/data/excel' + MCP_IMAGE_HOST: str = 'http://localhost:3000' + SERVER_IMAGE_HOST: str = 'https://YOUR_SERVE_IP:MCP_PORT/images/' + + LOCAL_MODEL_PATH: str = '/opt/sqlbot/models' + DEFAULT_EMBEDDING_MODEL: str = 'shibing624/text2vec-base-chinese' + EMBEDDING_ENABLED: bool = True + EMBEDDING_SIMILARITY: float = 0.4 + EMBEDDING_TOP_COUNT: int = 5 + + +settings = Settings() # type: ignore