ManageMeals 1.9.3 配置参数详解

ManageMeals api v1.9.3/web v1.7.3 全量配置面:后端 .env 样例 60 参数分类详解、前端 9 项与抓取器/同步器参数、compose 修改生效回滚闭环实测(JWT 30s 过期/分页 5 逐字回显)、生产模板与必改清单、FAQ(实测日期 2026-11-02)

ManageMealsManageMealsDocker ComposeNode.js2026/11/02

ManageMeals 1.9.3 配置参数详解

目标:把 ManageMeals(manage-meals-api v1.9.3 / manage-meals-web v1.7.3,均 2026-09-25 发布)的全部可配置参数讲透——官方 .env 样例共 60 个后端参数、前端 9 个、抓取器 9 个、依赖服务若干,逐项说明默认值、生效方式与自托管取舍,并用实测演示「改参数 → 重建 → 验证生效 → 回滚」的完整闭环。全部回显来自实测环境(Ubuntu 24.04.5 LTS / x86_64 / Docker 29.9.2 / Docker Compose v5.6.1,实测日期 2026-11-02)。栈怎么部署见站内文章 《ManageMeals Docker Compose 部署指南》,组件选型评估见 《ManageMeals 介绍与选型指南》。

⚠️ 验收方式说明:本文参数行为验证全部通过「容器内 printenv + HTTP API 实测回显 + 数据库字段抽查」完成;本次实测环境无浏览器自动化工具,未做 UI 层走查。

一、环境信息

本文假设你已按部署指南把 8 服务栈跑起来,以下为本实测环境的配置相关事实,读者对照替换。

项目值
应用版本manage-meals-api v1.9.3、manage-meals-web v1.7.3(ghcr 仅发 :latest,用 digest 固定,见部署指南 2.2)
配置载体仅 docker-compose.yaml 的 environment: 段——官方镜像没有配置文件挂载点
参数权威来源后端仓库 .env 样例(实测取回 60 键,见 2.2);前端/抓取器参数来自官方 compose 样例与源码
Compose 项目manage-meals-v1.9.3-stack,目录内维护 compose 文件
演示账号admin@example.com / q1w2e3r4(API 直连测试用)
数据库凭据MongoDB root / q1w2e3r4;Typesense API key q1w2e3r4;JWT 密钥 access-jwt-secret-q1w2e3r4 / refresh-jwt-secret-q1w2e3r4
API 地址http://localhost:8148(容器内 3000);Web 地址 http://localhost:8146

💡 提示:本文所有示例凭据(q1w2e3r4 等)均为实验环境明文占位,生产环境必须换成强随机值,见「六、生产配置模板」。

二、前置条件

配置操作前确认三件事:栈在运行、样例文件在手、验证工具可用。

2.1 服务栈运行中

cd /opt/manage-meals && docker compose ps
NAME                                         STATUS
manage-meals-v193-stack-manage-meals-api-1   Up
manage-meals-v193-stack-manage-meals-web-1   Up
manage-meals-v193-stack-mongo-1              Up
manage-meals-v193-stack-mongo-express-1      Up
manage-meals-v193-stack-recipe-scraper-1     Up
manage-meals-v193-stack-search-sync-1        Up
manage-meals-v193-stack-typesense-1          Up
manage-meals-v193-stack-valkey-1             Up

8 服务全部 Up 才能做参数生效验证。

2.2 后端全量参数样例文件

ManageMeals 后端仓库提供 .env 样例,列出了 API 认识的全部环境变量。实测取回并统计键数:

grep -c '=' api-.env.example
awk -F= '{print $1}' api-.env.example | sort | uniq -d
60

共 60 个键,无重复。raw.githubusercontent.com 在国内存在间歇超时(实测重试可成),拿不到时可直接看后端仓库 manage-meals-api 根目录的 .env 样例文件。

2.3 验证工具

  • curl:调 API 验证参数行为;
  • python3(或 jq):解析登录返回与解码 JWT 载荷,验证有效期类参数;
  • docker compose exec <服务> printenv:确认注入值真正进入容器进程环境。

三、参数查看与修改方式

ManageMeals 的配置模型非常简单:环境变量是唯一入口。本章先确认这一点,再给出查看、修改、验证、回滚四步的标准操作。

3.1 确认没有配置文件挂载

官方镜像不含 app.yaml / nginx.conf 之类挂载点,配置全部经 environment: 注入。用容器内环境实测佐证——API 容器内环境变量总数只有 18 个(12 个注入项 + PATH/HOME 等 Docker 基础变量),镜像内没有预置 .env:

docker compose exec -T manage-meals-api printenv | grep -cE '.'
18

这意味着:.env 样例里的其余 48 个参数当前一个都没注入,API 完全靠代码内置默认值运行(这正是 5.x 各节「默认即可用」结论的来源)。

3.2 查看当前生效值

看 compose 声明了什么:

docker compose config | grep -A 20 'manage-meals-api'

看进程实际收到了什么(以此为准):

docker compose exec -T manage-meals-api printenv | grep -E '^(MONGO_|REDIS_|TYPESENSE_|ACCESS_|REFRESH_|AUTH_|RECIPE_|USER_)' | sort
ACCESS_JWT_SECRET=access-jwt-secret-q1w2e3r4
AUTH_ACCESS_TOKEN_EXPIRE_SEC=600
AUTH_REFRESH_TOKEN_EXPIRE_SEC=2629746
MONGO_DB=manage_meals
MONGO_URL=mongodb://root:q1w2e3r4@mongo:27017/
RECIPE_SCRAPER_URL=http://recipe-scraper:8000
REDIS_URL=redis://valkey:6379
REFRESH_JWT_SECRET=refresh-jwt-secret-q1w2e3r4
TYPESENSE_API_KEY=q1w2e3r4
TYPESENSE_HOST=typesense
TYPESENSE_PORT=8108
USER_REGISTER_ENABLED=true

Web 侧 9 项同样验证:

docker compose exec -T manage-meals-web printenv | grep -E '^(PUBLIC_|PASSWORD_|API_URL|COOKIE_|ORIGIN|BODY_SIZE)' | sort
API_URL=http://manage-meals-api:3000/v1
BODY_SIZE_LIMIT=Infinity
COOKIE_ACCESS_TOKEN=mmeals_access_token
COOKIE_ACCESS_TOKEN_EXPIRE_SEC=600
COOKIE_REFRESH_TOKEN=mmeals_refresh_token
COOKIE_REFRESH_TOKEN_EXPIRE_SEC=2629746
ORIGIN=http://localhost:8146
PASSWORD_MIN_LENGTH=6
PUBLIC_MAIN_TITLE=ManageMeals

💡 提示:compose config 显示的是「声明值」,printenv 显示的是「进程值」。排查配置不生效时,两边各看一眼即可定位是 compose 没写对,还是服务没重建。

3.3 修改与生效:改 compose → up -d 指定服务

ManageMeals 没有热加载机制,改参数的标准动作是:编辑 compose 的 environment: → docker compose up -d <受影响服务>(Compose 检测到配置变化会自动 Recreated)→ printenv 确认 → 功能验证。下面用两个真实参数完整走一遍。

实验设计:把 AUTH_ACCESS_TOKEN_EXPIRE_SEC 从 600 改为 30(登录令牌 30 秒过期),并新增 ITEMS_PER_PAGE: 5(列表分页大小)。先采基线:

AT=$(curl -s -X POST http://localhost:8148/v1/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"email":"admin@example.com","password":"q1w2e3r4"}' \
  | python3 -c 'import sys,json;print(json.load(sys.stdin)["accessToken"])')
python3 -c "
import base64,json
p=json.loads(base64.urlsafe_b64decode('$AT'.split('.')[1]+'=='))
print('JWT iat=%d exp=%d diff=%d'%(p['iat'],p['exp'],p['exp']-p['iat']))"
curl -s "http://localhost:8148/v1/recipes" -H "Authorization: Bearer $AT" \
  | python3 -c 'import sys,json;d=json.load(sys.stdin);print("page=%s total=%s perPage=%s"%(d["page"],d["total"],d["perPage"]))'
JWT iat=1793559439 exp=1793560039 diff=600
page=1 total=1 perPage=20

基线:令牌有效期 600 秒、分页 20(未注入 ITEMS_PER_PAGE 时的代码默认值)。改 compose 后重建 API:

docker compose up -d manage-meals-api
Container manage-meals-v193-stack-manage-meals-api-1 Recreated
Container manage-meals-v193-stack-manage-meals-api-1 Starting
Container manage-meals-v193-stack-manage-meals-api-1 Started

验证生效——新登录令牌差值变为 30,列表 perPage 变为 5:

patched JWT iat=1793559462 exp=1793559492 diff=30
patched recipes page=1 total=1 perPage=5
docker compose exec -T manage-meals-api printenv | grep -E '^(AUTH_ACCESS|ITEMS)'
AUTH_ACCESS_TOKEN_EXPIRE_SEC=30
ITEMS_PER_PAGE=5

再用「等 32 秒后旧令牌必失效」验证过期语义真实生效:

立即调用:http=200
32s 后同令牌调用:
{"statusCode":401,"error":"Unauthorized","message":"Invalid Authorization header"}
http=401

💡 技术提示:up -d 不带服务名会检查全部服务,只重建配置有变化的;带服务名更快。改 manage-meals-web 的变量同理 up -d manage-meals-web。另外注意 USER_REGISTER_ENABLED: "true" 这类布尔值在 compose 中要加引号,避免 YAML 布尔解析歧义。

3.4 回滚

改生产配置前先备份 compose,验证完用备份复原:

cp docker-compose.yaml /tmp/compose.bak   # 修改前执行
cp /tmp/compose.bak docker-compose.yaml && docker compose up -d manage-meals-api
docker compose exec -T manage-meals-api printenv | grep AUTH_ACCESS
AUTH_ACCESS_TOKEN_EXPIRE_SEC=600

实测复原后重新登录 JWT diff=600、perPage=20,diff /tmp/compose.bak docker-compose.yaml 无差异,环境回到基线。

四、参数总览

4.1 manage-meals-api(60 项全量)

下表为 .env 样例全部 60 键的分类总览。「自托管必要性」列的判读口径:必配=最小部署必须注入;默认即可=不注入走内置默认且实测可用;条件=启用对应功能才配;不适用=官方 SaaS 计费/压测专用,自托管个人部署无需关心。

#参数样例值分类自托管必要性
1MONGO_URLmongodb://root:example@localhost:27017/数据源必配
2MONGO_DBmanage_meals数据源必配
3REDIS_URLredis://localhost:6379数据源必配
4TYPESENSE_HOSTlocalhost搜索必配
5TYPESENSE_PORT8108搜索必配
6TYPESENSE_API_KEYsecret搜索必配(改强值)
7RECIPE_SCRAPER_URLhttp://recipe-scraper:8000抓取必配
8ACCESS_JWT_SECRETsecret认证必配(改强值)
9REFRESH_JWT_SECRETsecret认证必配(改强值)
10AUTH_ACCESS_TOKEN_EXPIRE_SEC30认证默认即可(实测注入 600)
11AUTH_REFRESH_TOKEN_EXPIRE_SEC300认证默认即可(实测注入 2629746)
12PASSWORD_MIN_LENGTH6认证默认即可(5.x 实测)
13BCRYPT_SALT_ROUNDS10认证默认即可(实测 $2b$10$)
14USER_REGISTER_ENABLEDtrue认证默认即可(关注册时改)
15RESET_PASS_JWT_SECRETsecret认证条件(用忘记密码)
16RESET_PASS_JWT_EXPIRE_SEC7200认证条件
17EMAIL_VERIFY_ENABLEDtrue邮件验证条件(开邮箱验证)
18EMAIL_VERIFY_JWT_SECRETsecret邮件验证条件
19EMAIL_VERIFY_JWT_EXPIRE_SEC172800邮件验证条件
20APP_URLhttps://example.com全局条件(邮件链接指向)
21ITEMS_PER_PAGE20分页默认即可(实测注入 5 生效)
22MAX_FILE_SIZE_BYTES10485760限制默认即可(10MB)
23DEFAULT_RECIPE_IMGhttps://example.com图片默认即可(官方占位图)
24HELP_CONTACT_EMAILalice@example.com支持条件
25INFRA_ENDPOINT_KEYexample运维条件(健康检查)
26RABBITMQ_URLamqp://localhost异步条件(消息队列)
27PROCESS_USER_POST_REGISTERfalse异步默认即可
28PROCESS_IMAGE_POST_IMPORTfalse异步默认即可
29S3_KEYs3key对象存储条件(图片上传)
30S3_SECRETs3secret对象存储条件
31S3_ENDPOINThttps://s3.example.com对象存储条件
32S3_ORIGIN_ENDPOINThttps://bucket.s3.example.com对象存储条件
33S3_CDN_ENDPOINThttps://bucket.fra1.cdn.digitaloceanspaces.com对象存储条件
34S3_REGIONus-east-1对象存储条件
35S3_BUCKETbucket对象存储条件
36SMTP_HOSTlocalhost邮件条件(发信)
37SMTP_USERinfo@example.com邮件条件
38SMTP_PASSasdfasdf123邮件条件
39SMTP_PORT587邮件条件
40SMTP_DEFAULT_FROMManageMeals <hello@example.com>邮件条件
41OAUTH_ENABLEDtrueOAuth条件(第三方登录)
42OAUTH_GOOGLE_CLIENT_IDclientOAuth条件
43OAUTH_GOOGLE_CLIENT_SECRETsecretOAuth条件
44OAUTH_GOOGLE_CALLBACK_URIhttps://example.comOAuth条件
45OAUTH_FACEBOOK_CLIENT_IDclientOAuth条件
46OAUTH_FACEBOOK_CLIENT_SECRETsecretOAuth条件
47OAUTH_FACEBOOK_CALLBACK_URIhttps://example.comOAuth条件
48OAUTH_FRONTEND_CALLBACK_URLhttps://example.com/auth/oauth/callbackOAuth条件
49GOCARDLESS_ACCESS_TOKENsecret计费不适用
50GOCARDLESS_ENVSANDBOX计费不适用
51GOCARDLESS_REDIRECT_URIhttps://example.com计费不适用
52GOCARDLESS_EXIT_URIhttps://example.com计费不适用
53GOCARDLESS_WEBHOOK_SECRETsecret计费不适用
54PAYPAL_APP_CLIENT_IDppclientid计费不适用
55PAYPAL_APP_SECRETsecret计费不适用
56PAYPAL_API_URLhttps://api-m.sandbox.paypal.com计费不适用
57PREMIUM_PRICE_PENNIES190计费不适用
58MOCK_INSTANCEno测试不适用(保持默认)
59MOCK_ALLOWED_URLS/v1/auth/login,/v1/auth/refresh-token测试不适用
60MULTIPLE_MOCK_USERSfalse测试不适用

样例值列保留官方 .env.example 原文(其中 asdfasdf123、example.com 等为官方占位示例),自托管时按第六章模板替换。

4.2 manage-meals-web(9 项)

前端参数实测注入 9 项(3.2 节 printenv 全量回显):PUBLIC_MAIN_TITLE、API_URL、ORIGIN、PASSWORD_MIN_LENGTH、COOKIE_ACCESS_TOKEN、COOKIE_REFRESH_TOKEN、COOKIE_ACCESS_TOKEN_EXPIRE_SEC、COOKIE_REFRESH_TOKEN_EXPIRE_SEC、BODY_SIZE_LIMIT。含义详见 5.12。

4.3 recipe-scraper(9 项可选,源码默认值)

抓取器零配置可用——实测容器内自定义变量 grep 无命中(exit=1),全部走内置默认:

参数默认值用途
PPLX_API_KEY空配置后冷门站点走 Perplexity AI 兜底解析
DEFAULT_RECIPE_IMG官方占位图 URL抓取不到菜谱图时的兜底图
SCRAPER_CACHE_TIMEOUT10缓存超时
SCRAPER_PROXY_URL空抓受限站点时配上游代理
CACHE_REDIS_HOST / CACHE_REDIS_PORT / CACHE_REDIS_DB / CACHE_REDIS_URL / CACHE_REDIS_KEY_PREFIX未设抓取结果 Redis 缓存(可选)

4.4 search-sync 与依赖服务

  • search-sync(5 项):MONGO_URL、MONGO_DB、TYPESENSE_HOST、TYPESENSE_PORT、TYPESENSE_API_KEY,实测 printenv 与 API 侧同值。同步周期不可配置——源码内置 cron.schedule("*/3 * * * *"),固定 3 分钟。
  • mongo(3 项):MONGO_INITDB_ROOT_USERNAME / MONGO_INITDB_ROOT_PASSWORD / MONGO_INITDB_DATABASE,仅首次初始化数据目录时生效。
  • mongo-express(6 项):ME_CONFIG_MONGODB_ADMINUSERNAME / ME_CONFIG_MONGODB_ADMINPASSWORD / ME_CONFIG_MONGODB_URL / ME_CONFIG_BASICAUTH / ME_CONFIG_BASICAUTH_USERNAME / ME_CONFIG_BASICAUTH_PASSWORD。
  • typesense:无环境变量,配置走启动 command:(--data-dir /data --api-key=<密钥> --enable-cors)。

五、核心参数分类详解

以下按功能链路逐类展开,每条结论都标注实测或源码依据。

5.1 数据源与缓存(MONGO_URL / MONGO_DB / REDIS_URL)

MONGO_URL 是含账号密码的完整连接串,compose 网络内指向 mongo:27017;MONGO_DB 指定业务库名(实测库内 7 个集合:tags、syncs、shoppinglists、mealplans、users、cookbooks、recipes)。REDIS_URL 指向 valkey(Redis 协议兼容),API 用它缓存鉴权结果(源码 10 秒 TTL)——实测清缓存无业务影响,重启即清空。三者值改错的表现都是 API 起不来或 5xx,改后务必 docker compose logs manage-meals-api 复查。

5.2 认证、注册与密码策略

这是自托管最需要理解的一组,全部实测过:

  • JWT 双令牌:ACCESS_JWT_SECRET / REFRESH_JWT_SECRET 分别为两类令牌签名密钥,实测登录 accessToken 解码 exp-iat=600,与 AUTH_ACCESS_TOKEN_EXPIRE_SEC: 600 一致;改 30 后重建即 diff=30(3.3 节闭环)。两个密钥是生产必改项,泄露即可伪造任意用户令牌。

  • 过期行为:令牌过期后调用返回 401 Invalid Authorization header;不带请求头返回 401 Missing Authorization header。续期接口 POST /v1/auth/refresh-token,字段名是 token(传 refreshToken 报 400)。

  • PASSWORD_MIN_LENGTH(默认 6):实测用 5 位密码注册直接被 AJV 校验拒绝:

    http=400
    {"statusCode":400,"code":"FST_ERR_VALIDATION","error":"Bad Request","message":"body/password must NOT have fewer than 6 characters"}
    

    Web 侧同名参数控制前端表单校验,两侧应配同值,否则会出现「前端放行、后端 400」的体验割裂。

  • BCRYPT_SALT_ROUNDS(默认 10):实测注册用户落库的 password 字段前缀 $2b$10$,即默认 cost=10 生效。改此值只影响新密码哈希,已有用户仍是旧 cost,登录不受影响。

  • USER_REGISTER_ENABLED(默认 true):实测改 "false" 重建后,注册接口返回:

    {"statusCode":500,"error":"Internal Server Error","message":"User register is not enabled"}
    

    建好管理员账号后建议关闭公网自助注册。注意它是以 500 而非 403 表达拒绝(实现如此,勿按状态码写监控断言)。

  • 忘记密码:RESET_PASS_JWT_SECRET / RESET_PASS_JWT_EXPIRE_SEC(样例 7200 秒)支撑 /v1/auth/forgot-password、/v1/auth/reset-password 流程,但发信依赖 5.3 的 SMTP——没配 SMTP 时该功能形同虚设。

5.3 邮箱验证与 SMTP(EMAIL_VERIFY_* / SMTP_*)

EMAIL_VERIFY_ENABLED 样例值为 true,但不注入时实测不强制验证:新建用户 users 集合中 emailVerified: true 且未验证不拦登录。一旦开启,需同时配 EMAIL_VERIFY_JWT_SECRET、EMAIL_VERIFY_JWT_EXPIRE_SEC(样例 172800=48h)、APP_URL(验证链接域名)与完整 SMTP_* 五件套,否则用户收不到验证邮件会被锁在门外。个人自托管建议保持默认关闭。SMTP 五参数(SMTP_HOST/USER/PASS/PORT/DEFAULT_FROM)为通用 nodemailer 语义,样例端口 587(STARTTLS)。

5.4 OAuth 第三方登录(OAUTH_* 8 项)

OAUTH_ENABLED 总开关 + Google/Facebook 各三件套(client id/secret/callback URI)+ OAUTH_FRONTEND_CALLBACK_URL(前端回调页,形如 https://<你的域名>/auth/oauth/callback)。回调 URI 必须与 Google/Facebook 控制台登记值逐字符一致。纯自托管家庭用户通常用不上,整组不注入即可。

5.5 分页与请求限制(ITEMS_PER_PAGE / MAX_FILE_SIZE_BYTES / BODY_SIZE_LIMIT)

ITEMS_PER_PAGE 控制所有列表接口分页,实测未注入时 GET /v1/recipes 回显 "perPage":20,注入 5 后重建即回显 "perPage":5(3.3 节)。MAX_FILE_SIZE_BYTES(样例 10485760=10MB)限制 API 上传体积;前端 SvelteKit 侧对应 BODY_SIZE_LIMIT(实测注入 Infinity,即 Web 不拦,实际上限由 API 与 S3 决定)。

5.6 搜索链路(TYPESENSE_* + search-sync)

API 与 search-sync 各注入同一组 TYPESENSE_HOST/PORT/API_KEY,API_KEY 必须与 typesense 启动命令 --api-key= 完全一致,否则同步与查询全部 401。recipes 集合由 search-sync 首启自动创建(实测 default_sorting_field: rating)。索引延迟不是配置项:cron 固定 */3 * * * *,实测导入后约 3 分钟才可搜到;查询接口 /v1/search 必填 q、c、p 三参数(c 当前仅 recipes)。

5.7 抓取链路与图片兜底(RECIPE_SCRAPER_URL / scraper / DEFAULT_RECIPE_IMG)

URL 导入链路:API /v1/recipes/import?url= → RECIPE_SCRAPER_URL → scraper 解析。scraper 侧 9 个可选参数见 4.3;实测零注入可用。DEFAULT_RECIPE_IMG 在 API 与 scraper 两侧同名(源码默认官方占位图 https://whatacdn.fra1.cdn.digitaloceanspaces.com/mmeals/images/default1.jpg),自托管想换默认菜谱图时两侧同改。PROCESS_IMAGE_POST_IMPORT(默认 false)开启后导入图片转存走异步队列,依赖 RabbitMQ/S3,个人部署保持默认。

5.8 对象存储(S3_* 7 项)

菜谱图片上传功能需要 S3 兼容存储(官方按 DigitalOcean Spaces 写的样例,MinIO 亦可)。七个参数中 S3_ENDPOINT(API 写入用)、S3_ORIGIN_ENDPOINT、S3_CDN_ENDPOINT(浏览器读取用)三个 URL 最容易配混:写入走 endpoint,展示走 CDN 域名。不配 S3 时图片上传不可用,但 URL 导入的菜谱外链图片不受影响——实测未配 S3 的完整链路(导入 brownies)正常。

5.9 计费与订阅(GOCARDLESS_* / PAYPAL_* / PREMIUM_PRICE_PENNIES)

这 9 项服务于 ManageMeals 官方 SaaS 的订阅收费(GoCardless 扣款、PayPal 收款、PREMIUM_PRICE_PENNIES=190 即 $1.90 定价)。自托管个人部署全部不适用,保持不注入即可;/v1/premium、/v1/subscriptions 路由存在但无实际业务。

5.10 运维与健康检查(INFRA_ENDPOINT_KEY / RABBITMQ_URL)

API 提供顶层健康检查 GET /infra/health(不在 /v1 下、不走用户 JWT),用 INFRA_ENDPOINT_KEY 作访问密钥。实测未配 key 时:

http=403
{"statusCode":403,"error":"Forbidden","message":"Invalid infra key"}

配好后可安全接入 Uptime Kuma 等外部探活。RABBITMQ_URL 仅在启用异步处理(5.7/5.8 的 PROCESS_* 开关)时需要,最小部署不配。

5.11 Mock 与开发专用参数(MOCK_*)

MOCK_INSTANCE(默认 no)是官方开发/压测用的接口挡板,配合 MOCK_ALLOWED_URLS、MULTIPLE_MOCK_USERS 使用。生产环境务必保持 no/不注入——开启会绕过真实鉴权路径,属于高危配置。

5.12 Web 侧参数详解

  • PUBLIC_MAIN_TITLE:站点标题。PUBLIC_ 前缀是 SvelteKit 约定——该前缀变量会暴露给浏览器端代码,任何敏感值都不得用 PUBLIC_ 前缀传。
  • API_URL:SSR 服务端调后端地址(容器网络内 http://manage-meals-api:3000/v1),浏览器不直连它。
  • ORIGIN:站点对外地址,必须与浏览器实际访问地址逐字符一致(协议+域名+端口)。反代/域名访问时忘改是最常见的「登录后仍跳登录页」原因——Cookie 作用域不匹配。
  • COOKIE_ACCESS_TOKEN / COOKIE_REFRESH_TOKEN:登录态 Cookie 名;两个 *_EXPIRE_SEC 应与 API 侧 AUTH_*_EXPIRE_SEC 同值,避免「Cookie 未过期而令牌已过期」的空窗(实测两侧均 600/2629746 对齐)。
  • PASSWORD_MIN_LENGTH:前端注册校验,与 API 同名参数对齐(5.2)。
  • BODY_SIZE_LIMIT:SvelteKit 请求体上限,Infinity 表示前端不拦。

六、生产配置模板

本章给出「compose 插值 + .env 分离」的生产形态。模板中的 ${VAR} 是 Compose 变量插值语法(运行期由同目录 .env 填充),属合法占位写法,不是待填错误。

6.1 docker-compose.yaml(关键服务节选)

name: manage-meals

services:
    manage-meals-api:
        image: ghcr.io/managemeals/manage-meals-api@sha256:01757ea28191c0f92401aacee9c6050568e205cec7eb32646038ce965454de83
        restart: unless-stopped
        environment:
            MONGO_URL: ${MM_MONGO_URL}
            MONGO_DB: manage_meals
            REDIS_URL: redis://valkey:6379
            TYPESENSE_HOST: typesense
            TYPESENSE_PORT: 8108
            TYPESENSE_API_KEY: ${MM_TYPESENSE_KEY}
            ACCESS_JWT_SECRET: ${MM_ACCESS_JWT_SECRET}
            REFRESH_JWT_SECRET: ${MM_REFRESH_JWT_SECRET}
            AUTH_ACCESS_TOKEN_EXPIRE_SEC: 600
            AUTH_REFRESH_TOKEN_EXPIRE_SEC: 2629746
            RECIPE_SCRAPER_URL: http://recipe-scraper:8000
            USER_REGISTER_ENABLED: "false"
            APP_URL: https://meals.example.com
        depends_on: [mongo, valkey, recipe-scraper, typesense, search-sync]
        ports: ["3000"]

    manage-meals-web:
        image: ghcr.io/managemeals/manage-meals-web@sha256:9886d09bbee8ea9da60a320757cb5eb15fa5740b701e5b5b86377d59b1ff0a56
        restart: unless-stopped
        environment:
            PUBLIC_MAIN_TITLE: My Meals
            API_URL: http://manage-meals-api:3000/v1
            ORIGIN: https://meals.example.com
            PASSWORD_MIN_LENGTH: 10
            COOKIE_ACCESS_TOKEN: mmeals_access_token
            COOKIE_REFRESH_TOKEN: mmeals_refresh_token
            COOKIE_ACCESS_TOKEN_EXPIRE_SEC: 600
            COOKIE_REFRESH_TOKEN_EXPIRE_SEC: 2629746
            BODY_SIZE_LIMIT: Infinity
        depends_on: [manage-meals-api]
        ports: ["3000"]

(mongo/valkey/typesense/scraper/search-sync 服务定义与实测栈相同,敏感值同样改为 .env 插值注入;镜像改用 digest 固定保证可复现。生产不对外映射 api/typesense/scraper 端口,仅经 Web 与反代访问。)

6.2 同目录 .env(权限 600,不入库)

MM_MONGO_URL=mongodb://root:<强随机密码>@mongo:27017/
MM_TYPESENSE_KEY=<强随机密钥>
MM_ACCESS_JWT_SECRET=<openssl rand -hex 32 生成>
MM_REFRESH_JWT_SECRET=<openssl rand -hex 32 生成>

6.3 生产必改清单

项参数动作
1ACCESS_JWT_SECRET / REFRESH_JWT_SECRET强随机,泄露=任意伪造登录态
2MONGO_URL 密码 / MONGO_INITDB_ROOT_PASSWORD强随机
3TYPESENSE_API_KEY(三处一致)强随机
4ORIGIN / APP_URL改为真实域名,与反代一致
5USER_REGISTER_ENABLED建好账号后 "false"
6mongo-express 服务生产删除,或 ME_CONFIG_BASICAUTH: "true" 并设账号密码
7PASSWORD_MIN_LENGTH建议 ≥10,Web/API 同值
8api/typesense/scraper 宿主端口映射移除,仅反代暴露 Web
9compose 镜像引用:latest 改 digest 固定
10备份mongodump 定期(命令见 CLI 工具篇)

七、常见问题

Q1:改了 environment 但行为没变? up -d 时注意看该服务是否出现 Recreated;若显示 Running,说明 Compose 认为配置未变(常见于改了 .env 但服务引用变量名拼错)。用 3.2 的 printenv 对比声明值与进程值定位。

Q2:登录成功却反复跳回登录页? 九成是 ORIGIN 与实际访问地址不一致(5.12),Cookie 域不匹配。改成浏览器地址栏同源值后 up -d manage-meals-web。

Q3:注册返回 500 "User register is not enabled"? USER_REGISTER_ENABLED 为 "false"(实测回显见 5.2)。临时改回 "true" 建完账号再关。

Q4:注册 400 "must NOT have fewer than 6 characters"? 密码长度低于 PASSWORD_MIN_LENGTH(默认 6)。想调强策略时 Web/API 两侧同改(6.3 第 7 项)。

Q5:刚导入的菜谱搜不到? search-sync 固定 3 分钟一轮(5.6),非配置错误。等一轮后看 docker compose logs search-sync 是否出现 Imported N recipes。

Q6:/infra/health 返回 403? 未配 INFRA_ENDPOINT_KEY 或 key 不匹配(实测回显见 5.10),属预期防护而非故障。

Q7:开了 EMAIL_VERIFY_ENABLED 后新用户无法登录? 验证邮件发不出去——SMTP 未配或 APP_URL 错误导致链接不可达。个人自托管建议保持不开启(5.3)。

Q8:导入图片显示不出 / 上传功能报错? 未配 S3_* 时图片上传不可用(5.8);URL 导入菜谱的外链图不受影响。

八、参考链接

ManageMeals 1.9.3 配置参数详解 - Quzuo