Files
cmhub/docs/env.md
T

18 KiB
Raw Blame History

环境变量与配置

本文集中约定运行 cmhub 所需配置项。真实密钥、数据库密码、支付凭证不得写入代码、文档样例或提交记录;本文件只写变量名、用途和占位示例。

一、配置来源

  • Django 运行级配置走环境变量或 .env(.env 不提交)。
  • 仓库根目录提供 .env.example 作为无密钥样例;新增配置项时同步更新 .env.example 与本文。
  • 上游 AI 模型的 api_key 存入数据库前必须用 AI_KEY_ENCRYPTION_KEY 加密,admin 脱敏展示且不回显明文。
  • 微信、支付宝商户密钥/证书走环境变量或部署机安全文件路径,不写入数据库明文字段。
  • 本地开发、测试、生产使用同一套变量名;差异只在变量值。

二、Django 基础配置

变量 必填 示例 说明
DJANGO_SECRET_KEY 是 change-me Django SECRET_KEY;生产必须使用高强度随机值
DJANGO_DEBUG 是 false 生产必须为 false
DJANGO_ALLOWED_HOSTS 是 cmhub.example.com,127.0.0.1 逗号分隔
DJANGO_CSRF_TRUSTED_ORIGINS 生产是 https://cmhub.example.com 用户端表单、admin、充值页需要
DJANGO_TIME_ZONE 否 Asia/Shanghai 默认按中国业务时区
DJANGO_EMAIL_BACKEND 否 django.core.mail.backends.console.EmailBackend 邮件发送后端;当前注册为免邮箱验证,不依赖邮件服务,后续密码找回/通知等邮件能力启用时生产应改为真实 SMTP / 邮件服务
DJANGO_DEFAULT_FROM_EMAIL 邮件能力启用时是 noreply@cmhub.example.com Django/allauth 邮件默认发件人
DJANGO_SESSION_COOKIE_SECURE 生产是 true 生产 HTTPS 下 session cookie 仅允许安全连接传输
DJANGO_CSRF_COOKIE_SECURE 生产是 true 生产 HTTPS 下 CSRF cookie 仅允许安全连接传输
DJANGO_SECURE_SSL_REDIRECT 否 false 若 Nginx/宝塔已强制 HTTPS,可保持 false;若由 Django 强制跳转则设 true
DJANGO_SECURE_HSTS_SECONDS 否 31536000 全站确认只走 HTTPS 后再启用;未确认前保持 0,避免 HSTS 误锁域名
DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS 否 false 是否把 HSTS 应用于子域名
DJANGO_SECURE_HSTS_PRELOAD 否 false 是否声明 HSTS preload;提交 preload 前必须确认全部子域长期 HTTPS
DJANGO_SECURE_PROXY_SSL_HEADER 反代 HTTPS 是 true Nginx 反代并传 X-Forwarded-Proto https 时开启,避免 Django 误判当前请求为 HTTP
ACCOUNT_SIGNUP_RATE_LIMIT 否 20/m/ip allauth 注册限流配置;T-608 后注册会赠送可消费点数,生产必须保留或收紧该限制

三、数据库配置

开发和生产都使用 MySQL 8.4 LTS 独立实例,不使用 SQLite。

变量 必填 示例 说明
MYSQL_HOST 是 127.0.0.1 cmhub 专用 MySQL 实例地址
MYSQL_PORT 是 3306 MySQL 默认端口;若与已有实例共存,可改为专用独立端口
MYSQL_DATABASE 是 cmhub 数据库名
MYSQL_USER 是 cmhub 应用账号
MYSQL_PASSWORD 是 change-me 数据库密码
MYSQL_CHARSET 是 utf8mb4 必须为 utf8mb4
MYSQL_CONNECT_TIMEOUT 否 30 客户端连接 MySQL 的超时秒数;远程测试库建议显式设置,避免默认值过短造成误判
MYSQL_READ_TIMEOUT 否 120 客户端等待 MySQL 响应的读取超时秒数
MYSQL_WRITE_TIMEOUT 否 120 客户端向 MySQL 写入数据的超时秒数

四、AI 与加密配置

变量 必填 示例 说明
AI_KEY_ENCRYPTION_KEY 是 base64-fernet-key Fernet 主密钥,用于加密 AiModel.api_key_encrypted;生产不可更换,除非完成密钥轮换
DEVICE_IDENTIFIER_PEPPER 生产是 change-me-device-identifier-pepper T-624 设备安装标识的服务端 HMAC pepper;生产必须独立于 DJANGO_SECRET_KEY 配置,不写入客户端或日志
DEVICE_SESSION_TTL_SECONDS 否 3600 T-624 设备会话令牌有效秒数,默认 1 小时;过期后客户端重新登记刷新令牌
DEVICE_ACTIVITY_UPDATE_SECONDS 否 86400 T-624 同一设备更新最后活跃时间的最小间隔秒数,默认每日一次,避免每次生成写库
MIGRATION_REQUEST_TTL_SECONDS 否 900 T-627 存量迁移网页确认请求有效秒数,默认 15 分钟;过期后客户端必须重新申请,凭证明文不在服务端恢复
CMSHOPEE_SUBSCRIPTION_MODE 否 open T-632 账号订阅运行模式:open 开发测试开放、shadow 放行但记录真实权益、enforce 按真实权益强制拦截。非法值阻止 Django 启动
CMSHOPEE_SUBSCRIPTION_ENFORCEMENT 否 false T-630 旧兼容开关;仅当 CMSHOPEE_SUBSCRIPTION_MODE 未设置时生效,true 映射为 enforce,否则映射为 open。新部署不要再用它控制模式
CMSHOPEE_CLIENT_SUBSCRIPTION_POLICY 否 off T-636 django-admin 尚未创建 ClientSubscriptionPolicy 单例时的客户端策略兜底:off 不检测不门禁、observe 检测但不门禁、enforce 检测并门禁;只控制客户端体验,不改变服务端专属接口授权。非法值阻止 Django 启动
CMSHOPEE_CLIENT_SUBSCRIPTION_POLICY_UPDATED_AT .env 兜底为 observe / enforce 时是 2026-07-28T10:00:00+08:00 .env 兜底策略的最后变更时间,必须是带时区 ISO 8601。默认 off 未设置时使用仓库基线时间;其他模式缺失或非法值阻止 Django 启动。后台单例存在时接口改用其 updated_at
AI_IMAGE_UPSTREAM_DEADLINE_SECONDS 否 180 T-612 生图上游读取硬截止秒数;只作用于 generate_image 的上游请求和上游返回图片 URL 下载,Provider 实际读取超时取 min(AiModel.timeout_seconds 或分辨率默认值, 本值);生产按真实图片 smoke 耗时校准,外层 Gunicorn / Nginx / 客户端超时必须大于该值
PUBLIC_BASE_URL 否 https://cm.833729.com 站点公开基础 URL;异步 worker 没有 request 时可用它生成绝对媒体 URL
MEDIA_PUBLIC_BASE_URL 否 https://cm.833729.com 媒体文件公开基础 URL;优先于 PUBLIC_BASE_URL,用于 T-614 异步生图 worker 返回 result.image_url
IMAGE_TASK_RETENTION_HOURS 否 24 异步生图任务元数据保留窗口,默认至少 24 小时,覆盖客户端重启后继续轮询
GENERATED_IMAGE_RETENTION_HOURS 否 72 生成图片文件保留窗口口径,默认 72 小时;实际清理任务后续单独实现时按此值执行
IMAGE_TASK_REAPER_INTERVAL_SECONDS 否 60 异步生图 worker 循环中扫描僵尸 running 任务的间隔秒数
IMAGE_TASK_LEASE_SECONDS 否 600 异步生图任务租约秒数;worker 认领任务后写 lease_expires_at / heartbeat_at,超时由 reaper 判失败并退点
IMAGE_TASK_MAX_RETRIES 否 2 T-616 异步生图临时性上游失败最大重试次数;默认 2 表示最多 3 次上游调用
IMAGE_TASK_RETRY_BACKOFF_SECONDS 否 10,30 T-616 异步生图重试退避秒数列表;第 1 次失败等 10 秒,第 2 次失败等 30 秒,列表不足时复用最后一个值

AI_KEY_ENCRYPTION_KEY 必须是 cryptography.fernet.Fernet.generate_key() 生成的 base64 字符串,可用 py -3.12 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" 生成。

AiModel.url、AiModel.model、AiModel.api_type、AiModel.capabilities 与加密后的 api_key 由后台或数据迁移维护,不通过环境变量硬编码具体模型。

AI 上游连接超时由 AiModel.connect_timeout_seconds 控制。文本读取超时由 AiModel.timeout_seconds 控制;当读取超时为 0 时,Provider 按分辨率使用内置默认值。生图读取超时在此基础上再套 AI_IMAGE_UPSTREAM_DEADLINE_SECONDS 硬截止,避免慢 / 卡死图片上游长期占用生成池线程。

T-614 起新增异步生图任务接口:提交任务仍同步审核 prompt 和预扣点;worker 成功后返回 cmhub 托管媒体 URL。生产必须配置 MEDIA_PUBLIC_BASE_URL 或 PUBLIC_BASE_URL 为 HTTPS 域名,否则 worker 只能返回相对 /media/... URL,不利于桌面端直接下载。T-616 起临时性上游失败会按 IMAGE_TASK_MAX_RETRIES 与 IMAGE_TASK_RETRY_BACKOFF_SECONDS 自动重试;若 AI_IMAGE_UPSTREAM_DEADLINE_SECONDS=220 且默认重试 2 次,单个任务最坏耗时约为 220 * 3 + 10 + 30 = 700 秒,桌面端轮询总超时必须覆盖该窗口。

五、对外 API 安全配置

变量 必填 示例 说明
API_GENERATE_THROTTLE_RATE 否 60/min 生成标题 / 图片 / 多图理解接口按 API Key 或用户限流,DRF throttle rate 格式
API_AUTH_FAILURE_THROTTLE_RATE 否 30/min 缺失、畸形或无效 API Key 的认证失败按 IP 限流
IMAGE_URL_MAX_BYTES 否 10485760 image_url 服务端下载的最大响应字节数,默认 10 MiB
IMAGE_URL_MAX_REDIRECTS 否 3 image_url 手动跟随重定向次数上限;每跳都会重新校验目标地址
IMAGE_URL_CONNECT_TIMEOUT_SECONDS 否 10 image_url 下载连接超时秒数
IMAGE_URL_READ_TIMEOUT_SECONDS 否 60 image_url 下载读取超时秒数
IMAGE_MAX_INPUT_IMAGES 否 8 图生图接口单次最多接受的输入图片数量,最小按 1 处理
IMAGE_MAX_INPUT_IMAGE_BYTES 否 10485760 图生图接口每张图片解码或下载后的最大字节数,默认 10 MiB
IMAGE_MAX_INPUT_TOTAL_BYTES 否 33554432 图生图接口单次所有图片的总字节数上限,默认 32 MiB
VISION_MAX_IMAGES 否 8 多图理解接口单次最多接受的图片数量,最小按 1 处理
VISION_MAX_IMAGE_BYTES 否 10485760 多图理解接口每张图片解码或下载后的最大字节数,默认 10 MiB
VISION_MAX_TOTAL_BYTES 否 33554432 多图理解接口单次所有图片的总字节数上限,默认 32 MiB
RECHARGE_MAX_AMOUNT_CNY 否 100000.00 用户端单笔充值金额上限,超过则拒绝创建订单

image_url 只允许 http / https,服务端会在请求前解析域名,拒绝私有网段、回环、链路本地、保留地址、组播、未指定地址;重定向后的目标地址也会重复执行同样校验。内网图片不应通过 image_url 传入,调用方应改用 image_base64。

图生图和多图理解接口都会先审核 prompt,再按 images 顺序下载或解码图片;图生图的 images[0] 是主商品图、images[1:] 是参考图。单图分别受 IMAGE_MAX_INPUT_IMAGE_BYTES / VISION_MAX_IMAGE_BYTES 限制,URL 下载不会绕过现有 SSRF、重定向和超时检查。两组 *_MAX_* 限制应按上游模型输入上限与 Web worker 内存共同校准。

六、内容安全 / 本地敏感词配置

变量 必填 示例 说明
MODERATION_ENABLED 否 false 内容安全总开关;默认关闭时所有审核 no-op
MODERATION_PROVIDER 启用时是 keyword T-604 只支持 keyword 本地敏感词 provider;云厂商后续扩展
MODERATION_FAIL_CLOSED 否 true 审核 provider 不可用时是否拦截;生产建议 true
MODERATION_BLOCK_ON_REVIEW 否 false review 是否按拦截处理;T-604 keyword MVP 不使用 review
MODERATION_CACHE_VERSION_KEY 否 moderation:sensitive_words:version 共享 cache 中的词库版本号 key,用于多 worker matcher 失效

T-604 只做 prompt 本地敏感词快筛。命中时返回 content_blocked,不扣点、不写调用记录、不调上游;并且必须在下载 image_url 前执行 prompt 审核。详细规则见 moderation.md。

T-635/T-636 的客户端策略与 CMSHOPEE_SUBSCRIPTION_MODE 是两套独立开关:前者只通过公开版本检查接口通知新版客户端是否检查 / 展示会员门禁,后者才决定产品专属提交接口是否在服务端拦截。日常运营应由超级管理员在 /admin/licensing/clientsubscriptionpolicy/ 保存单一 off -> observe -> enforce 枚举和必填原因,保存后下一次公开请求立即生效,无需重启;该单例不存在时才使用本节两个 .env 变量,修改 .env 仍需重启 cmhub-web 与 cmhub-generate。不支持使用两个独立布尔环境变量,避免发布矛盾的 false/true 配置。

生产多 Gunicorn worker 下,敏感词 matcher 刷新依赖共享 cache 的版本号;若仍使用 LocMemCache,只能刷新当前进程,不能保证所有 worker 及时更新。

七、静态文件与限流缓存

变量 必填 示例 说明
STATIC_URL 否 /static/ 静态资源 URL 前缀
STATIC_ROOT 生产是 /www/wwwroot/cmhub/staticfiles collectstatic 输出目录,Nginx/宝塔需托管此目录
DJANGO_CACHE_BACKEND 生产是 django.core.cache.backends.db.DatabaseCache Django cache 后端;开发可用 locmem,生产多 Gunicorn worker 必须用共享后端
DJANGO_CACHE_LOCATION 生产是 cmhub_cache cache 位置;DatabaseCache 时为表名,Redis/Memcached 时为连接地址

生产限流依赖 Django cache。默认 LocMemCache 只适合单进程本地开发;多 worker 部署时每个进程各算一份限流,会放大实际请求速率。MVP 可先用 MySQL 的 DatabaseCache,部署时执行 python3.12 manage.py createcachetable cmhub_cache;高并发后再换 Redis / Memcached 等共享 cache,并同步安装对应 backend 依赖。ACCOUNT_SIGNUP_RATE_LIMIT、生成接口限流和认证失败限流都依赖这套 cache。

八、支付配置

变量 必填 示例 说明
PAYMENT_CALLBACK_MODE 是 mock / sdk 回调验签模式;本地/测试可用 mock,生产必须为 sdk
PAYMENT_MOCK_CALLBACK_SECRET mock 是 change-me mock 回调 HMAC 密钥;仅用于本地/测试,不得冒充真实支付验签
PAYMENT_QR_EXPIRES_MINUTES 否 10 扫码下单返回的二维码本地有效期提示;不作为自动阻断延迟回调的依据

微信 V3 native

变量 必填 示例 说明
WECHAT_PAY_APPID 真实支付是 wx... 微信 appid
WECHAT_PAY_MCHID 真实支付是 1900000001 商户号
WECHAT_PAY_API_V3_KEY 真实支付是 change-me API v3 key
WECHAT_PAY_CERT_SERIAL_NO 真实支付是 ABC... 商户证书序列号
WECHAT_PAY_PRIVATE_KEY_PATH 真实支付是 /secure/wechat/apiclient_key.pem 私钥文件路径
WECHAT_PAY_NOTIFY_URL 真实支付是 https://cmhub.example.com/api/v1/recharge/callback/wechat 公网回调地址
SOFTWARE_WECHAT_PAY_NOTIFY_URL 软件订阅真实支付是 https://cmhub.example.com/api/v1/software-orders/callback/wechat 软件套餐订单专用微信回调地址;必须与充值回调地址不同,未配置时 SDK 下单失败且订单标为 failed

支付宝当面付

变量 必填 示例 说明
ALIPAY_APPID 真实支付是 202100... 支付宝 appid
ALIPAY_APP_PRIVATE_KEY_PATH 真实支付是 /secure/alipay/app_private_key.pem 应用私钥路径
ALIPAY_PUBLIC_KEY_PATH 真实支付是 /secure/alipay/alipay_public_key.pem 支付宝公钥路径
ALIPAY_NOTIFY_URL 真实支付是 https://cmhub.example.com/api/v1/recharge/callback/alipay 公网回调地址
ALIPAY_DEBUG 否 false 生产必须为 false

缺少真实商户配置时,充值任务只能使用 mock 支付客户端;mock 必须在代码和测试中标明,不得伪装成真实支付。

九、对象存储配置

图片结果默认返回 URL,避免大 base64 进入同步响应体。对象存储选型未最终落地前,可使用本地开发存储,但生产必须给出可公开访问或可签名访问的 URL。

变量 必填 示例 说明
STORAGE_BACKEND 否 local local / s3;MVP 可先 local
MEDIA_ROOT local 是 D:\chengma\cmhub\media 本地媒体文件目录
MEDIA_URL local 是 /media/ 本地媒体 URL 前缀
S3_ENDPOINT_URL s3 是 https://s3.example.com S3 兼容 endpoint
S3_BUCKET_NAME s3 是 cmhub-media bucket
S3_ACCESS_KEY_ID s3 是 change-me access key
S3_SECRET_ACCESS_KEY s3 是 change-me secret key

十、上线前检查

  • DJANGO_DEBUG=false。
  • DJANGO_SECRET_KEY、AI_KEY_ENCRYPTION_KEY、数据库密码、支付密钥均已使用生产值。
  • ALLOWED_HOSTS、CSRF_TRUSTED_ORIGINS、支付 notify_url 使用同一公网域名。
  • STATIC_ROOT 已执行 collectstatic,Nginx/宝塔已托管 /static/;若继续用本地媒体存储,也必须托管 /media/ 或切换对象存储。
  • DJANGO_CACHE_BACKEND 已切到共享后端(如 DatabaseCache / Redis / Memcached),不是默认 LocMemCache。
  • 生产 HTTPS 下 DJANGO_SESSION_COOKIE_SECURE=true、DJANGO_CSRF_COOKIE_SECURE=true,反代场景按需开启 DJANGO_SECURE_PROXY_SSL_HEADER=true。
  • 全站 HTTPS 稳定后再设置 DJANGO_SECURE_HSTS_SECONDS;未确认子域名 HTTPS 前不要启用 includeSubDomains / preload。
  • MySQL 为 8.4 LTS / InnoDB / utf8mb4,不是 SQLite 或已有 MySQL 5.7。
  • image_url 下载上限、生成限流、认证失败限流、单笔充值金额上限已按生产容量调整。
  • 若启用 MODERATION_ENABLED,MODERATION_PROVIDER=keyword,敏感词词库已配置,且共享 cache 可用于多 worker matcher 版本失效。
  • 图片同步链路的客户端、Nginx、Gunicorn 超时均大于 AI_IMAGE_UPSTREAM_DEADLINE_SECONDS;上游 read timeout 由 AiModel.timeout_seconds / 分辨率默认值和该硬截止共同决定。