import os from dataclasses import dataclass from pathlib import Path PROJECT_ROOT = Path(__file__).parents[1].resolve() def load_local_env() -> None: env_path = PROJECT_ROOT / ".env" if not env_path.exists(): return for raw_line in env_path.read_text(encoding="utf-8").splitlines(): line = raw_line.strip() if not line or line.startswith("#") or "=" not in line: continue key, value = line.split("=", 1) key = key.strip() if key not in os.environ or os.environ[key] == "": os.environ[key] = value.strip().strip('"').strip("'") load_local_env() AUTH_REQUIRE_HTTPS = os.getenv("AUTH_REQUIRE_HTTPS", "1") == "1" AUTH_ALLOW_INSECURE_LOCAL = os.getenv("AUTH_ALLOW_INSECURE_LOCAL", "1") == "1" AUTH_COOKIE_SECURE = os.getenv("AUTH_COOKIE_SECURE", "1") == "1" # 阿里云 OSS 配置预留:可以直接在这里填写,也可以用同名环境变量覆盖。 OSS_ENDPOINT = os.getenv("OSS_ENDPOINT", "") OSS_INTERNAL_ENDPOINT = os.getenv("OSS_INTERNAL_ENDPOINT", "") OSS_USE_INTERNAL = os.getenv("OSS_USE_INTERNAL", "0") == "1" OSS_BUCKET_NAME = os.getenv("OSS_BUCKET_NAME", "") OSS_ACCESS_KEY_ID = os.getenv("OSS_ACCESS_KEY_ID", "") OSS_ACCESS_KEY_SECRET = os.getenv("OSS_ACCESS_KEY_SECRET", "") OSS_PUBLIC_BASE_URL = os.getenv("OSS_PUBLIC_BASE_URL", "") OSS_UPLOAD_PREFIX = os.getenv("OSS_UPLOAD_PREFIX", "product-images") WECHAT_PAY_APPID = os.getenv("WECHAT_PAY_APPID", "") WECHAT_PAY_MCH_ID = os.getenv("WECHAT_PAY_MCH_ID", "") WECHAT_PAY_MCH_SERIAL_NO = os.getenv("WECHAT_PAY_MCH_SERIAL_NO", "") WECHAT_PAY_API_V3_KEY = os.getenv("WECHAT_PAY_API_V3_KEY", "") WECHAT_PAY_PRIVATE_KEY_PATH = os.getenv("WECHAT_PAY_PRIVATE_KEY_PATH", "") WECHAT_PAY_PUBLIC_KEY_PATH = os.getenv("WECHAT_PAY_PUBLIC_KEY_PATH", "") WECHAT_PAY_NOTIFY_URL = os.getenv("WECHAT_PAY_NOTIFY_URL", "") WECHAT_PAY_API_BASE_URL = os.getenv("WECHAT_PAY_API_BASE_URL", "https://api.mch.weixin.qq.com") WECHAT_PAY_SKIP_NOTIFY_VERIFY = os.getenv("WECHAT_PAY_SKIP_NOTIFY_VERIFY", "0") == "1" DEPLOY_WEBHOOK_SECRET = os.getenv("DEPLOY_WEBHOOK_SECRET", "") DEPLOY_PROJECT_DIR = os.getenv("DEPLOY_PROJECT_DIR", str(PROJECT_ROOT)) DEPLOY_FRONT_DIR = os.getenv("DEPLOY_FRONT_DIR", "front") DEPLOY_BRANCH = os.getenv("DEPLOY_BRANCH", "") DEPLOY_SCRIPT_PATH = os.getenv("DEPLOY_SCRIPT_PATH", str(PROJECT_ROOT / "scripts" / "deploy_front.sh")) DEPLOY_COMMAND_TIMEOUT = int(os.getenv("DEPLOY_COMMAND_TIMEOUT", "600")) DEPLOY_LOCK_PATH = os.getenv("DEPLOY_LOCK_PATH", "/tmp/py-web-deploy.lock") DEPLOY_LOG_PATH = os.getenv("DEPLOY_LOG_PATH", "/tmp/py-web-deploy.log") DEPLOY_NODE_BIN_DIR = os.getenv("DEPLOY_NODE_BIN_DIR", "") @dataclass(frozen=True) class OSSSettings: endpoint: str internal_endpoint: str use_internal: bool bucket_name: str access_key_id: str access_key_secret: str public_base_url: str upload_prefix: str @property def is_configured(self) -> bool: return all( [ self.endpoint, self.bucket_name, self.access_key_id, self.access_key_secret, ] ) @property def upload_endpoint(self) -> str: if self.use_internal and self.internal_endpoint: return self.internal_endpoint return self.endpoint oss_settings = OSSSettings( endpoint=OSS_ENDPOINT, internal_endpoint=OSS_INTERNAL_ENDPOINT, use_internal=OSS_USE_INTERNAL, bucket_name=OSS_BUCKET_NAME, access_key_id=OSS_ACCESS_KEY_ID, access_key_secret=OSS_ACCESS_KEY_SECRET, public_base_url=OSS_PUBLIC_BASE_URL, upload_prefix=OSS_UPLOAD_PREFIX, ) @dataclass(frozen=True) class AuthSettings: require_https: bool allow_insecure_local: bool cookie_secure: bool auth_settings = AuthSettings( require_https=AUTH_REQUIRE_HTTPS, allow_insecure_local=AUTH_ALLOW_INSECURE_LOCAL, cookie_secure=AUTH_COOKIE_SECURE, ) @dataclass(frozen=True) class WeChatPaySettings: appid: str mch_id: str mch_serial_no: str api_v3_key: str private_key_path: str public_key_path: str notify_url: str api_base_url: str skip_notify_verify: bool @property def is_configured(self) -> bool: return all( [ self.appid, self.mch_id, self.mch_serial_no, self.api_v3_key, self.private_key_path, self.notify_url, ] ) wechat_pay_settings = WeChatPaySettings( appid=WECHAT_PAY_APPID, mch_id=WECHAT_PAY_MCH_ID, mch_serial_no=WECHAT_PAY_MCH_SERIAL_NO, api_v3_key=WECHAT_PAY_API_V3_KEY, private_key_path=WECHAT_PAY_PRIVATE_KEY_PATH, public_key_path=WECHAT_PAY_PUBLIC_KEY_PATH, notify_url=WECHAT_PAY_NOTIFY_URL, api_base_url=WECHAT_PAY_API_BASE_URL, skip_notify_verify=WECHAT_PAY_SKIP_NOTIFY_VERIFY, ) @dataclass(frozen=True) class DeploySettings: webhook_secret: str project_dir: str front_dir: str branch: str script_path: str command_timeout: int lock_path: str log_path: str node_bin_dir: str @property def is_configured(self) -> bool: return bool(self.webhook_secret and self.script_path) deploy_settings = DeploySettings( webhook_secret=DEPLOY_WEBHOOK_SECRET, project_dir=DEPLOY_PROJECT_DIR, front_dir=DEPLOY_FRONT_DIR, branch=DEPLOY_BRANCH, script_path=DEPLOY_SCRIPT_PATH, command_timeout=DEPLOY_COMMAND_TIMEOUT, lock_path=DEPLOY_LOCK_PATH, log_path=DEPLOY_LOG_PATH, node_bin_dir=DEPLOY_NODE_BIN_DIR, )