VoucherVault 1.30.3 配置参数详解

逐项详解 VoucherVault v1.30.3 全部 31 个环境变量:默认值、取值规则、Django 源码机制与实测行为,附个人/生产两套推荐配置基线与排障实录。

VoucherVaultVoucherVaultDjango优惠券管理2026/10/25

VoucherVault 1.30.3 配置参数详解

目标:逐项讲清 VoucherVault v1.30.3 的全部环境变量(官方 README 列出的 30 项 + 源码中隐含的 1 个 OIDC 密钥项),包括默认值、取值规则、背后的 Django 机制与实测行为。安装步骤见《Ubuntu 22.04 安装 VoucherVault 指南》与《VoucherVault Docker Compose 部署指南》。

一、配置加载机制

VoucherVault 是标准 Django 应用,配置只通过环境变量注入,加载路径有两条(myproject/settings.py 与 myproject/wsgi.py):

from dotenv import load_dotenv
load_dotenv()                       # settings.py:从当前工作目录找 .env
load_dotenv(os.path.join(project_folder, '.env'))  # wsgi.py:从项目根目录找 .env

要点:

  • 部署形态决定注入方式:Compose 形态用 environment: 列表直接注入;裸机形态在项目根(manage.py 同级目录)放 .env 文件;
  • 修改任何变量后必须重启应用进程(uWSGI/Celery),Django 不会热加载;
  • 所有布尔型变量的判定逻辑是 os.environ.get('X','False').lower() in ['true'],即只有字面量 True/true 生效,写 1、yes 一律视为 False;
  • manage.py 命令(migrate、shell 等)同样会读 .env,保证 CLI 与 Web 进程配置一致。

二、变量总览表

按功能分组,默认值与必选性取自官方 README(查询日期 2026-10-25)与 settings.py 源码核对:

分组变量默认值必选
网络/安全DOMAINlocalhost✅ 必选
网络/安全PORT8000可选
网络/安全SECURE_COOKIESFalse可选
网络/安全SECRET_KEY每次启动自动生成可选(生产强烈建议设置)
网络/安全DEBUGFalse可选
网络/安全CSP_FRAME_ANCESTORS'none'可选
会话SESSION_COOKIE_AGE30(分钟)可选
会话SESSION_EXPIRE_AT_BROWSER_CLOSETrue可选
通知EXPIRY_THRESHOLD_DAYS30可选
通知EXPIRY_LAST_NOTIFICATION_DAYS7可选
时区TZEurope/Berlin可选
数据库DB_ENGINEsqlite3可选
数据库POSTGRES_HOSTdb可选
数据库POSTGRES_PORT5432可选
数据库POSTGRES_USERvouchervault可选
数据库POSTGRES_PASSWORDvouchervault可选
数据库POSTGRES_DBvouchervault可选
队列REDIS_URLredis://redis:6379/0可选
队列CELERY_WORKER_CONCURRENCY1可选
队列CELERY_WORKER_PREFETCH_MULTIPLIER1可选
OIDCOIDC_ENABLEDFalse可选
OIDCOIDC_AUTOLOGINFalse可选
OIDCOIDC_CREATE_USERTrue可选
OIDCOIDC_RP_SIGN_ALGOHS256可选
OIDCOIDC_RP_CLIENT_IDvouchervault(settings 中默认)可选
OIDCOIDC_RP_CLIENT_SECRET无可选
OIDCOIDC_OP_JWKS_ENDPOINT无RS256 时必选
OIDCOIDC_OP_AUTHORIZATION_ENDPOINT无OIDC 时必选
OIDCOIDC_OP_TOKEN_ENDPOINT无OIDC 时必选
OIDCOIDC_OP_USER_ENDPOINT无OIDC 时必选
OIDCOIDC_RP_IDP_SIGN_KEY(仅源码)无HS256 时可用

三、网络与安全组

3.1 DOMAIN(必选)

决定 Django 的 ALLOWED_HOSTS 与 CSRF_TRUSTED_ORIGINS。源码逻辑(settings.py 第 42-56 行):

DOMAIN = str(os.environ.get("DOMAIN", "localhost"))
TRUSTED_PORT = str(os.environ.get("PORT", "8000"))
# 对每个域名生成 4 条信任源:
#   http://<domain>:<PORT>  http://<domain>  https://<domain>:<PORT>  https://<domain>
  • 支持逗号分隔多个域名/IP,如 DOMAIN=vouchervault.example.com,192.168.1.10;
  • 实测佐证:域名不匹配时,登录表单 POST 会被 CSRF 中间件拒绝,返回 403「CSRF verification failed」(见 B1 篇排障实录第 1 条);
  • 写域名时不要带 http:// 前缀与尾部斜杠,源码会先剥离这两个成分再拼装。

3.2 PORT

仅在访问端口不是 8000/80/443 时需要设置,用于拼 CSRF_TRUSTED_ORIGINS。例如 Compose 把宿主机 8130 映射到容器 8000,就应设 PORT=8130 并配合 DOMAIN=127.0.0.1,否则表单 POST 403。

3.3 SECURE_COOKIES

True 时同时开启三件事(settings.py 第 66-73 行):

SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = "31536000"
SECURE_HSTS_PRELOAD = True
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
  • 仅在反向代理已上 HTTPS 后开启;纯 HTTP 环境开了会导致浏览器拒发 Cookie、直接无法登录;
  • HSTS 一年期且含 preload、子域,一旦开启回退 HTTP 很麻烦,确认 TLS 稳定后再开。

3.4 SECRET_KEY

Django 签名密钥。不设置时每次进程启动都随机生成(secrets.token_urlsafe(32)),副作用是重启即踢掉所有已登录会话、既有签名链接全部失效。生产环境务必生成固定值写入配置:

python3 -c "import secrets; print(secrets.token_urlsafe(32))"

3.5 DEBUG

True 开启 Django 调试模式:错误页显示堆栈与配置细节(实测中曾用它定位 CSRF 失败原因,错误页会给出 Reason given for failure)。仅限排障时临时开启,用完立即改回 False 并重启。

3.6 CSP_FRAME_ANCESTORS

控制 CSP frame-ancestors 响应头(即本站允许被哪些页面 iframe 嵌入)。默认 'none' 完全禁止嵌入。如需嵌入内部门户,设为逗号分隔的来源列表,例如:

CSP_FRAME_ANCESTORS="'self',https://portal.example.com"

注意每项需自带单引号或完整 URL,源码只做逗号切分与去空格,不做格式校验。

四、会话组

变量机制
SESSION_COOKIE_AGE会话最大分钟数,源码中 int(...) * 60 转成秒,默认 30 分钟
SESSION_EXPIRE_AT_BROWSER_CLOSE默认 True:关闭浏览器即失效;想在 SESSION_COOKIE_AGE 周期内保持登录,显式设 False

会话 Cookie 名固定为 VVSESS,SameSite=Lax(均硬编码,无对应变量)。两个变量组合建议:个人使用 SESSION_COOKIE_AGE=1440 + SESSION_EXPIRE_AT_BROWSER_CLOSE=False(一天免登);共享终端保持默认。

五、通知组

到期提醒由 Celery Beat 每天触发 check_expiration 任务(源码 myapp/management/commands/check_expiration.py):

threshold_days = os.getenv('EXPIRY_THRESHOLD_DAYS', 30)
last_chance_threshold_days = os.getenv('EXPIRY_LAST_NOTIFICATION_DAYS', 7)
  • EXPIRY_THRESHOLD_DAYS:到期前 N 天发送首次提醒;
  • EXPIRY_LAST_NOTIFICATION_DAYS:到期前 N 天发送最后一次提醒;
  • 两者必须是整数字符串,非整数会让任务直接报错退出(源码有显式校验与报错输出);
  • 提醒投递渠道在 Web 界面的用户设置里按用户配置 Apprise URL,不在环境变量层;这两个变量只控制时机。

六、时区 TZ

设置 Django TIME_ZONE,影响页面时间显示与日期比较。建议国内部署设 TZ=Asia/Shanghai。

⚠️ 实测发现的一个坑:settings.py 中 CELERY_TIMEZONE = 'Europe/Berlin' 是硬编码,不跟随 TZ。默认周期任务按柏林时间每天 09:00 触发,即北京时间 15:00/16:00(视夏令时)。如需调整触发时间,到 Django 管理后台 /en/admin/django_celery_beat/periodictask/ 直接修改周期任务的 crontab。

七、数据库组

DB_ENGINE = os.environ.get("DB_ENGINE", "sqlite3")
  • 缺省 sqlite3:数据文件 <项目根>/database/db.sqlite3,零依赖,个人/家庭够用;
  • 设 postgres 时启用以下五个变量:POSTGRES_HOST(默认 db)、POSTGRES_PORT(默认 5432)、POSTGRES_USER/POSTGRES_PASSWORD/POSTGRES_DB(默认均为 vouchervault);
  • POSTGRES_PASSWORD 属敏感信息,Compose 形态建议改用 .env 文件配合 ${VAR} 引用,不要写进版本库;
  • 切换引擎后需要重新跑 manage.py migrate 建表,原 SQLite 数据不会自动迁移(需自行用 dumpdata/loaddata 搬迁)。

八、队列组

REDIS_URL = os.environ.get('REDIS_URL')
if REDIS_URL == None:
    CELERY_BROKER_URL = 'redis://redis:6379/0'
else:
    CELERY_BROKER_URL = REDIS_URL
  • REDIS_URL 同时用作 Celery broker 与 result backend;Compose 形态默认值 redis://redis:6379/0 里的主机名就是 Redis 服务名,无需改动;裸机形态必须显式设为 redis://127.0.0.1:6379/0;
  • CELERY_WORKER_CONCURRENCY:worker 并发进程数,默认 1。本应用任务量极小(每天一次扫描),默认即可;
  • CELERY_WORKER_PREFETCH_MULTIPLIER:预取倍数,默认 1,保持默认。

九、OIDC 组(企业单点登录)

开启 OIDC_ENABLED=True 后,登录走外部 IdP(Authentik/Keycloak/Authelia 等),基于 mozilla-django-oidc 实现:

变量说明
OIDC_AUTOLOGINTrue 时访问登录页自动跳转 IdP,不再展示本地登录表单
OIDC_CREATE_USER默认 True:IdP 首次登录自动建本地账号
OIDC_RP_SIGN_ALGORS256(推荐,公钥验签)或 HS256(共享密钥)
OIDC_OP_JWKS_ENDPOINTRS256 必选,IdP 的 JWKS 地址
OIDC_RP_IDP_SIGN_KEYHS256 时的 IdP 签名密钥(README 未列,仅见于 settings.py)
OIDC_RP_CLIENT_ID / OIDC_RP_CLIENT_SECRETIdP 侧注册的客户端凭据
OIDC_OP_AUTHORIZATION_ENDPOINT / OIDC_OP_TOKEN_ENDPOINT / OIDC_OP_USER_ENDPOINTIdP 三个标准端点 URL

开启 OIDC 后 URL 路由会追加 /oidc/ 前缀端点(settings.py 条件挂载 mozilla_django_oidc.urls),IdP 的回调地址填 https://<你的域名>/oidc/callback/。本地账号密码登录仍然保留(/en/accounts/login/),便于 IdP 故障时兜底。

十、推荐配置基线

个人/家庭(SQLite,直接 HTTP):

DOMAIN=192.168.1.10
PORT=8130
SECRET_KEY=<固定随机串>
TZ=Asia/Shanghai
SESSION_COOKIE_AGE=1440
SESSION_EXPIRE_AT_BROWSER_CLOSE=False
EXPIRY_THRESHOLD_DAYS=30
EXPIRY_LAST_NOTIFICATION_DAYS=7
REDIS_URL=redis://127.0.0.1:6379/0

生产(HTTPS 反代 + PostgreSQL):

DOMAIN=vouchervault.example.com
SECURE_COOKIES=True
SECRET_KEY=<固定随机串>
TZ=Asia/Shanghai
DB_ENGINE=postgres
POSTGRES_HOST=127.0.0.1
POSTGRES_PORT=5432
POSTGRES_USER=vouchervault
POSTGRES_PASSWORD=<强口令>
POSTGRES_DB=vouchervault
REDIS_URL=redis://127.0.0.1:6379/0
CSP_FRAME_ANCESTORS='none'

十一、排障实录

1. 布尔变量写了 1 不生效

判定逻辑只认 true(大小写不敏感)。SECURE_COOKIES=1 等价于 False,一律写 True/False。

2. 每次重启后所有用户被踢出登录

SECRET_KEY 未固定,每次启动随机重生成。设置固定 SECRET_KEY 后重启不再丢会话。

3. 到期提醒在北京时间下午才触发

CELERY_TIMEZONE 硬编码 Europe/Berlin,与 TZ 无关。到 Django 后台调整 Periodic tasks 的 crontab 时区/时刻。

4. 改完 .env 没效果

环境变量只在进程启动时读取。裸机形态重启 uWSGI 与两个 Celery 进程;Compose 形态 docker compose up -d --force-recreate app。

5. RS256 模式 OIDC 登录报验签失败

缺 OIDC_OP_JWKS_ENDPOINT 或 IdP 侧 JWKS 地址不可达。该变量在 RS256 模式下是必选,README 表格「Optional」列标注的 Optional 仅对 HS256 成立。

十二、参考链接