# Supabase/PostgreSQL SaaS 题库后端配置(服务端/worker 使用,前端不读取此文件) # 复制此文件为 .env 并填写实际值。 # 生产上线前运行: # npm run readiness:production # npm run readiness:production:db # 运行环境。生产必须是 production;本地开发可用 development。 NODE_ENV=development # API 服务端口 PORT=8787 API_HEADERS_TIMEOUT_MS=15000 API_REQUEST_TIMEOUT_MS=120000 API_KEEP_ALIVE_TIMEOUT_MS=5000 API_SHUTDOWN_GRACE_PERIOD_MS=30000 API_MAX_REQUESTS_PER_SOCKET=1000 # Supabase/PostgreSQL 重构 API DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:54322/postgres # 本地 postgres 可留空;生产 API 必须为 tiku_api,worker 必须为 tiku_worker。 DB_EXPECTED_RUNTIME_ROLE= # 不要在 .env 中持久化 SMOKE_SEED_CONFIRM;破坏性测试只通过受控 npm script/CLI 临时确认。 # API/worker 各自进程使用。生产建议 API 10-20、worker 4-8,并结合 PgBouncer/连接池总量核算。 DB_POOL_MAX=10 DB_CONNECTION_TIMEOUT_MS=5000 DB_QUERY_TIMEOUT_MS=35000 DB_STATEMENT_TIMEOUT_MS=30000 DB_LOCK_TIMEOUT_MS=5000 DB_IDLE_IN_TRANSACTION_TIMEOUT_MS=30000 DB_IDLE_TIMEOUT_MS=30000 DB_POOL_MAX_USES=7500 DB_POOL_MAX_LIFETIME_SECONDS=1800 DB_APPLICATION_NAME=tiku-local DEFAULT_TENANT_SLUG=master # 生产只列中央平台/运维的固定 HTTPS Origin,不能包含 *。 # 各租户 H5 Origin 从 active tenant_domains + active tenants 动态准入,不要把数百个租户域名展开到此列表。 CORS_ORIGIN=http://127.0.0.1:5173,http://localhost:5173 CORS_TENANT_DOMAINS_ENABLED=false CORS_TENANT_DOMAIN_CACHE_TTL_MS=60000 CORS_TENANT_DOMAIN_NEGATIVE_CACHE_TTL_MS=10000 CORS_TENANT_DOMAIN_CACHE_MAX_ENTRIES=10000 MAX_JSON_BODY_BYTES=1048576 MAX_IMPORT_JSON_BODY_BYTES=10485760 # 认证迁移期配置:生产环境必须替换为高强度随机值,并配置 Supabase Auth JWT AUTH_SMS_PROVIDER=mock AUTH_CODE_PEPPER=replace_with_a_long_random_secret AUTH_SESSION_SECRET=replace_with_another_long_random_secret # 生产推荐使用 Supabase Auth JWKS: # AUTH_JWT_ISSUER=https://.supabase.co/auth/v1 # AUTH_JWT_JWKS_URL=https://.supabase.co/auth/v1/.well-known/jwks.json # 自托管或旧项目兼容才使用强随机 AUTH_JWT_SECRET;配置 JWKS 时必须同时配置 issuer。 AUTH_JWT_ISSUER= AUTH_JWT_AUDIENCE=authenticated AUTH_JWT_SECRET=development-jwt-secret-change-me AUTH_JWT_JWKS_URL= AUTH_CODE_TTL_SECONDS=300 AUTH_SMS_COOLDOWN_SECONDS=60 AUTH_SMS_TENANT_DAILY_LIMIT=20000 AUTH_SMS_PHONE_DAILY_LIMIT=10 # 校园/运营商 NAT 会共享 IP,IP 配额应宽于手机号/设备配额。 AUTH_SMS_IP_HOURLY_LIMIT=120 AUTH_SMS_DEVICE_HOURLY_LIMIT=10 AUTH_SESSION_TTL_SECONDS=604800 # 远程 Auth/JWKS 验收脚本配置。只在预生产/生产验收命令行临时设置真实 access token, # 不要把真实 token 写入仓库、前端 runtime-config 或长期 .env。 # AUTH_SMOKE_API_BASE_URL=https://api.example.com # AUTH_SMOKE_TENANT_ID= # AUTH_SMOKE_STUDENT_ACCESS_TOKEN= # AUTH_SMOKE_TENANT_ADMIN_ACCESS_TOKEN= # AUTH_SMOKE_PLATFORM_ADMIN_ACCESS_TOKEN= # AUTH_SMOKE_WRONG_TENANT_ID= # AUTH_SMOKE_REQUIRE_ADMIN_TOKENS=true # 安全目标默认关闭迁移期身份头。仅本地兼容旧测试/旧前端时可临时设为 true。 ALLOW_LEGACY_AUTH_HEADERS=false ALLOW_PLATFORM_ADMIN_KEY=false # 迁移期平台管理 API Key。生产环境必须使用平台管理员 JWT/服务端会话,不能开启 ALLOW_PLATFORM_ADMIN_KEY。 PLATFORM_ADMIN_API_KEY=replace_with_platform_admin_key # Worker 配置:CRM webhook、支付补偿、导入复检等后台任务使用 WORKER_CRM_BATCH_SIZE=20 WORKER_CRM_POLL_INTERVAL_MS=10000 WORKER_CRM_MAX_ATTEMPTS=5 WORKER_CRM_BACKOFF_SECONDS=5,30,120,600,1800 WORKER_CRM_REQUEST_TIMEOUT_MS=10000 # 仅本地 fake webhook 测试允许 http://127.0.0.1;生产建议 false WORKER_CRM_ALLOW_INSECURE_LOCALHOST=false # Worker 配置:支付/退款补偿 WORKER_COMMERCE_BATCH_SIZE=20 WORKER_COMMERCE_MIN_AGE_SECONDS=300 WORKER_COMMERCE_REQUEST_TIMEOUT_MS=10000 # Worker 配置:平台 SaaS 订阅自动开票和逾期催缴。 # 自动开票只处理有明确 expires_at 且未开过非 void 订阅账单的订阅。 WORKER_PLATFORM_BILLING_BATCH_SIZE=50 WORKER_PLATFORM_BILLING_DAYS_AHEAD=45 WORKER_PLATFORM_BILLING_DUE_DAYS=15 WORKER_PLATFORM_BILLING_ID=platform-billing-1 # 平台 SaaS 用量超额自动开票默认处理上一个自然月,也可用 YYYY-MM 补跑指定月份。 WORKER_PLATFORM_USAGE_OVERAGE_BATCH_SIZE=100 WORKER_PLATFORM_USAGE_OVERAGE_MONTH= WORKER_PLATFORM_USAGE_OVERAGE_DUE_DAYS=15 WORKER_PLATFORM_USAGE_OVERAGE_ID=platform-usage-overage-1 # 逾期催缴只标记 overdue、写 tenant_invoice_reminders 和审计,不自动停用租户。 WORKER_PLATFORM_DUNNING_BATCH_SIZE=100 WORKER_PLATFORM_DUNNING_ID=platform-dunning-1 # 逾期催缴外部通知会消费 tenant_invoice_reminders,并发送到 generic/钉钉/飞书/企微 webhook。 WORKER_PLATFORM_DUNNING_NOTIFICATION_BATCH_SIZE=50 WORKER_PLATFORM_DUNNING_NOTIFICATION_MAX_ATTEMPTS=5 WORKER_PLATFORM_DUNNING_NOTIFICATION_BACKOFF_SECONDS=10,60,300,900,1800 WORKER_PLATFORM_DUNNING_NOTIFICATION_REQUEST_TIMEOUT_MS=10000 # 仅本地 fake webhook 测试允许 http://127.0.0.1;生产必须 false WORKER_PLATFORM_DUNNING_NOTIFICATION_ALLOW_INSECURE_LOCALHOST=false # Worker 配置:平台审计告警生成和外部通知。 WORKER_PLATFORM_AUDIT_ALERT_BATCH_SIZE=200 WORKER_PLATFORM_AUDIT_ALERT_LOOKBACK_DAYS=14 WORKER_PLATFORM_AUDIT_ALERT_ID=platform-audit-alerts-1 WORKER_PLATFORM_AUDIT_NOTIFICATION_BATCH_SIZE=50 WORKER_PLATFORM_AUDIT_NOTIFICATION_MAX_ATTEMPTS=5 WORKER_PLATFORM_AUDIT_NOTIFICATION_BACKOFF_SECONDS=10,60,300,900,1800 WORKER_PLATFORM_AUDIT_NOTIFICATION_REQUEST_TIMEOUT_MS=10000 # 仅本地 fake webhook 测试允许 http://127.0.0.1;生产必须 false WORKER_PLATFORM_AUDIT_NOTIFICATION_ALLOW_INSECURE_LOCALHOST=false # Worker 配置:内容资源复检。异常托管对象会被标记 failed 并从 active 退回 draft。 WORKER_ASSET_BATCH_SIZE=50 WORKER_ASSET_MIN_AGE_SECONDS=300 WORKER_ASSET_RECHECK_INTERVAL_SECONDS=86400 WORKER_ASSET_REQUEST_TIMEOUT_MS=10000 # 内容资源安全扫描。生产必须使用 metadata_rules,http,HTTP scanner 必须是 HTTPS 且 fail-open=false。 WORKER_ASSET_SECURITY_SCANNER=metadata_rules WORKER_ASSET_SECURITY_SCAN_HTTP_ENDPOINT= WORKER_ASSET_SECURITY_SCAN_HTTP_TOKEN= WORKER_ASSET_SECURITY_SCAN_HTTP_TIMEOUT_MS=10000 WORKER_ASSET_SECURITY_SCAN_FAIL_OPEN=false # Worker 配置:大批量内容导入 WORKER_IMPORT_BATCH_SIZE=5 WORKER_IMPORT_ID=imports-1 WORKER_IMPORT_LEASE_SECONDS=120 WORKER_IMPORT_HEARTBEAT_INTERVAL_MS=30000 WORKER_IMPORT_BACKOFF_SECONDS=30,120,600,1800 # Worker 配置:公共题库自动同步。冲突会保留租户自改题目并等待后台处理。 WORKER_PUBLIC_BANK_SYNC_BATCH_SIZE=5 WORKER_PUBLIC_BANK_SYNC_COPY_LIMIT=1000 WORKER_PUBLIC_BANK_SYNC_ID=public-banks-1 WORKER_PUBLIC_BANK_SYNC_CLAIM_STALE_SECONDS=900 # Worker 配置:题库 PDF/Word 导出。生产建议使用真实对象存储 provider;local_dev 只用于本地开发。 WORKER_EXPORT_BATCH_SIZE=5 WORKER_EXPORT_ID=exports-1 WORKER_EXPORT_BACKOFF_SECONDS=30,120,600,1800 EXPORT_LOCAL_STORAGE_ROOT=.local-storage # Windows 本地可填 C:\Windows\Fonts\NotoSansSC-VF.ttf;Linux 生产建议安装 Noto Sans CJK 并配置路径。 EXPORT_PDF_FONT_PATH= # 对象存储配置:API 和 assets worker 共用 STORAGE_DEFAULT_PROVIDER=local_dev STORAGE_DEFAULT_BUCKET=tenant-assets STORAGE_PUBLIC_BASE_URL= STORAGE_MAX_UPLOAD_BYTES=524288000 STORAGE_ALLOWED_MIME_PREFIXES=image/,video/,audio/ STORAGE_ALLOWED_MIME_TYPES=application/pdf,application/json,application/zip,application/x-zip-compressed,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-powerpoint,application/vnd.openxmlformats-officedocument.presentationml.presentation,application/octet-stream,text/plain,text/markdown,text/csv STORAGE_REQUIRE_TENANT_PREFIX=true ALIYUN_OSS_REGION= ALIYUN_OSS_ENDPOINT= ALIYUN_OSS_ACCESS_KEY_ID= ALIYUN_OSS_ACCESS_KEY_SECRET= ALIYUN_OSS_STS_TOKEN= ALIYUN_OSS_INTERNAL=false TENCENT_COS_REGION= TENCENT_COS_APP_ID= TENCENT_COS_SECRET_ID= TENCENT_COS_SECRET_KEY= TENCENT_COS_SECURITY_TOKEN= SUPABASE_STORAGE_URL= SUPABASE_STORAGE_SERVICE_KEY= # 国内短信/OAuth/支付的租户级密钥不建议放在 .env。 # 正式使用时请通过租户后台写入 app_private.tenant_secrets,并只在 config_public 暴露非敏感配置。