Gancio 1.28.2 常用 CLI 命令与工具详解

gancio CLI 四子命令实测全解:migrate 在 Docker 镜像内坏路径的源码级定位与替代方案、users 七子命令全生命周期回显、settings 值必须 JSON/键名/重启三大陷阱、CLI 调用模板与守护消息、data 目录备份恢复与排障工具链速查。

GancioGancioNodejsDocker日历自托管2026/11/02

Gancio 1.28.2 常用 CLI 命令与工具详解

目标:Gancio 自带一个 Node.js CLI(容器内 /usr/local/bin/gancio),命令面只有 migrate / start / users / settings 四个子命令,但每个都有值得逐字讲清的实测行为——尤其是 gancio migrate 在 Docker 镜像里是一条"永远短路"的坏路径(源码级定位见第四节)。本文基于 1.28.2 镜像内源码逐文件核对(server/cli.js、server/cli/users.js、server/cli/settings.js、server/config.js、server/api/models/index.js)与运行实例实测(users 七个子命令全生命周期、settings 三大陷阱、migrate 三种参数组合、错误路径守护回显),给出逐命令手册与配套的数据管理、排障工具链。配置三层模型见《Gancio 1.28.2 配置参数详解》,部署流程见《Gancio 1.28.2 Docker Compose 部署指南》。

一、环境信息

  • 实测载体:Gancio 1.28.2(Docker Compose 部署,镜像 cisti/gancio:1.28.2,运行时 Node.js v20.19.6),宿主端口 8149 → 服务端口 13120,数据目录 /opt/gancio/data
  • 源码取证:镜像内程序目录 /home/node/server/,CLI 入口 /usr/local/bin/gancio
  • 所有命令回显均为容器内实测逐字记录;文中测试账号 cli-test@example.com 建后即删,现场仅保留 admin

二、CLI 命令面总览

2.1 入口脚本与版本横幅

容器内 gancio 就是 /usr/local/bin/gancio(带 #!/usr/bin/env node shebang 的 server/cli.js 本体),用 yargs 组装命令树。任何调用都会先打一行版本横幅,这是确认 CLI 与镜像版本一致的最快方式:

$ docker exec gancio gancio --version
📅 gancio - v1.28.2 - A shared agenda for local communities (nodejs: v20.19.6)
1.28.2

横幅格式 📅 gancio - v<版本> - <description> (nodejs: <node版本>) 与启动日志横幅同源(cli.js 的 console.info),排查"CLI 与运行中服务版本不一致"时对比这两处即可。

2.2 顶层命令表(实测 --help 全量回显)

$ docker exec gancio gancio --help
📅 gancio - v1.28.2 - A shared agenda for local communities (nodejs: v20.19.6)
Usage gancio <command> [options]

Commands:
  gancio migrate   Run database migration
  gancio start     Start gancio                         [default] [aliases: run]
  gancio users     Manage users
  gancio settings  Manage settings

Options:
      --version  Show version number                                   [boolean]
  -c, --config   Configuration file     [default: "/home/node/data/config.json"]
  -h, --help     Show help                                             [boolean]

Made with ❤ by underscore hacklab - https://gancio.org

四个命令各司其职:

命令作用日常使用频率
start(默认,别名 run)启动服务(内部走 @nuxt/cli-edge 的 start --modern)由容器 entrypoint / restart: always 代管,手动场景少
migrate手动跑数据库迁移Docker 镜像内实测无效,见第四节
users账号管理七个子命令高:忘密、建管理员、禁用用户全靠它
settings数据库 settings 表读写高:无网页后台时用 CLI 改配置

yargs 开了 .recommendCommands(),打错命令会提示相近命令;.demandCommand(1, '') 使裸跑 gancio 直接执行默认命令 start。

2.3 -c/--config 的解析链

cli.js 顶部两行决定了所有配置读取行为:

process.env.cwd = process.env.GANCIO_DATA || path.resolve('./')
process.chdir(path.resolve(__dirname, '..'))
  • -c 的默认值是 path.resolve(process.env.cwd, 'config.json'),即 $GANCIO_DATA/config.json(官方镜像环境变量 GANCIO_DATA=/home/node/data,所以 help 里显示 /home/node/data/config.json);
  • -c 的 coerce 函数会把解析后的绝对路径写进 process.env.config_path,server/config.js 的 load() 据此读文件;
  • 进程随后 chdir 到包根 /home/node——这个细节正是第四节 migrate 坏路径的根源。

三、start:默认启动命令

start 的实现只有一件事:require('@nuxt/cli-edge').run(['start', '--modern']),即拉起 Nuxt 生产服务。Gancio 前端是 SSR 的 Nuxt 应用,没有独立的"前端构建"CLI 步骤(镜像内已构建完毕)。

Docker 部署下你几乎不需要手动 gancio start:容器 entrypoint 就是它,配合 restart: always 自动拉起。需要手动使用的场景:

  1. 进程假死需要前台观察启动过程:docker exec -it gancio gancio start 可直接看到迁移、插件加载、监听横幅;
  2. 裸机(非 Docker)安装:gancio.org/latest.tgz 解包后 node server/cli.js start(或全局链接后 gancio start);
  3. 改 settings 后重启生效:Docker 下用 docker restart gancio 更干净(CLI 改 settings 的回显原话:Note that a restart is needed to get the new setting)。

四、migrate:Docker 镜像内的"坏路径"(实测 + 源码定位)

4.1 现象:配置完好的实例上 migrate 永远短路

对一个早已完成 setup、服务正常运行的实例执行 gancio migrate(实测三种参数组合,结果完全一致):

$ docker exec --workdir /home/node/data gancio gancio migrate
📅 gancio - v1.28.2 - A shared agenda for local communities (nodejs: v20.19.6)
> Reading configuration from: ./config.json
> Configuration file does not exists...
> Cannot run database migration before setup

显式传 -c(放在命令前后两种顺序)也一样:

$ docker exec --workdir /home/node/data gancio gancio -c /home/node/data/config.json migrate
📅 gancio - v1.28.2 - A shared agenda for local communities (nodejs: v20.19.6)
> Reading configuration from: ./config.json
> Configuration file does not exists...
> Cannot run database migration before setup

且退出码为 0(RC=0)——脚本化调用时无法靠 $? 发现它没干活。

4.2 源码定位:migrate 被挂成了 builder,跑在 -c coerce 之前

cli.js 的命令注册:

.command(['migrate'], 'Run database migration', migrate)   // 第三个参数是 builder!

yargs 的 command(cmd, desc, builder, handler) 签名里第三个位置是 builder(用于给子命令加参数),第四个才是 handler。migrate 函数被放在 builder 位置,于是迁移逻辑在 yargs 解析全局 -c 选项的 coerce 之前就被同步执行。此时 process.env.config_path 尚未被 coerce 写入,config.js 的 load() 落到兜底值:

const config_path = process.env.config_path || './config.json'

而进程早已 chdir 到包根 /home/node——那里只有 config.example.json,没有 config.json,于是 fs.existsSync('./config.json') 为 false,状态判定 SETUP,migrate() 打出 Cannot run database migration before setup 后直接 return。users/settings 的子命令挂在 handler 位置(builder 之后执行),coerce 已生效,所以不受影响——这解释了为什么同一实例上 gancio users list 能读到 /home/node/data/config.json 而 migrate 不能。

4.3 为什么不用担心:迁移由 start 自动完成

migrate 做的事(db.runMigrations(),umzug + sequelize storage,迁移文件在 /home/node/migrations/)在每次 start 启动流程里都会自动执行(initialize.server.js 第 32 行同样调用 db.runMigrations())。首启建库的实测回显(节选自启动日志):

2026-11-01T14:57:04.182Z debug: == 20191025133803-tags: migrated (0.006s)
2026-11-01T14:57:04.188Z debug: == 20191025134318-places: migrated (0.005s)
2026-11-01T14:57:04.195Z debug: == 20191025135631-settings: migrated (0.005s)
2026-11-01T14:57:04.201Z debug: == 20191025141447-users: migrated (0.004s)
2026-11-01T14:57:04.209Z debug: == 20191025142724-events: migrated (0.005s)

首次建库共 54 条 migrated 记录。升级场景同理:docker compose pull && docker compose up -d 后新版本启动时自动跑增量迁移(实测 1.27.0 ↔ 1.28.2 来回切换数据完好),umzug 用 SequelizeMeta 表记账,已应用的迁移不会重跑。

4.4 结论与操作口径

  • Docker 部署:忘掉 gancio migrate。它不报错、不干活、退出码还是 0;需要迁移时直接重启服务(docker restart gancio)让 start 流程代跑。
  • migrate 真正有效的场景是裸机安装且 config.json 放在包根(./config.json 恰好能被兜底路径命中)时的离线建库。
  • 若你在 Docker 里看到 Cannot run database migration before setup,不代表实例没配置好,这是 4.2 的坏路径,与实例状态无关。

五、users:七个子命令全生命周期实测

5.1 子命令一览(实测 gancio users --help)

$ docker exec gancio gancio users --help
📅 gancio - v1.28.2 - A shared agenda for local communities (nodejs: v20.19.6)
gancio <command>

Commands:
  gancio users list                         List all users
  gancio users enable <email>               Enable an user
  gancio users disable <email>              Enable an user
  gancio users reset-password <email>       Resets the password of the given
                                            user
  gancio users set_role <email> <role>      Set specified role privileges to the
                                            given user
  gancio users create <email> [password]    Create an user (role could be
  [role]                                    `user`, `admin` or `editor`)
  gancio users remove <email>               Remove an user

Options:
      --version  Show version number                                   [boolean]
  -c, --config   Configuration file     [default: "/home/node/data/config.json"]
  -h, --help     Show help                                             [boolean]

注意 disable 的描述文案写成了 Enable an user——这是上游 cli/users.js 的笔误(复制粘贴未改),不影响功能。

5.2 生命周期实测(建 → 查 → 禁 → 启 → 重置密码 → 改角色 → 删)

以下为一气呵成的实测回显(每步都是独立进程,均带横幅与配置读取行):

$ docker exec --workdir /home/node/data gancio gancio users create cli-test@example.com P@ssw0rd123 user
📅 gancio - v1.28.2 - A shared agenda for local communities (nodejs: v20.19.6)
> Reading configuration from: /home/node/data/config.json
User "cli-test@example.com" created with role user

$ docker exec --workdir /home/node/data gancio gancio users list
...
1	role: admin	enabled: true	email: admin
2	role: user	enabled: true	email: cli-test@example.com

$ docker exec --workdir /home/node/data gancio gancio users disable cli-test@example.com
...
User cli-test@example.com is now disabled!

$ docker exec --workdir /home/node/data gancio gancio users enable cli-test@example.com
...
User cli-test@example.com is now enabled!

$ docker exec --workdir /home/node/data gancio gancio users reset-password cli-test@example.com
...
New password for user cli-test@example.com is '9zaxxcy3ysye'

$ docker exec --workdir /home/node/data gancio gancio users set_role cli-test@example.com editor
...
User cli-test@example.com role is now editor!

$ docker exec --workdir /home/node/data gancio gancio users remove cli-test@example.com
...
User "cli-test@example.com" succesfully removed

users list 的输出是 Tab 分隔的 id / role / enabled / email 四列,可直接管道给 awk 处理。

5.3 细节与陷阱(源码 + 实测)

  1. <email> 是"账号名",不一定是邮箱。Gancio 允许非邮箱形式的账号名(setup 创建的管理员账号就叫 admin),CLI 按 where: { email: args.email } 精确匹配,写错大小写即 not found。

  2. create 的 role 有白名单校验,非法值直接被 yargs 拒绝(实测):

    $ docker exec --workdir /home/node/data gancio gancio users set_role admin superuser
    ...
    Invalid values:
      Argument: role, Given: "superuser", Choices: "user", "admin", "editor"
    

    三种角色权限差异:admin 全站管理,editor 可管理活动/地点/标签但无系统设置,user 仅提交活动。

  3. reset-password 只支持随机密码(helpers.randomString()),不能指定新密码;回显 New password for user <账号> is '<随机串>' 是唯一的取密途径,输出后不落盘,请立即记录。想设固定密码:先 create(可带 [password])或先 reset-password 再在网页端改。

  4. create 不带 [password] 时密码为空串(源码直接透传 args.password),该账号无法登录,务必显式给密码。

  5. 用户不存在时的回显分两种(实测):enable/disable/reset-password/set_role 打 User <账号> not found;remove 打 User "<账号>" not found!(带引号和叹号)。这些提示走 stderr/stdout 混排,脚本判断成败建议以 users list 复查为准。

  6. create/remove 的成功提示走 console.error(stderr),list/enable 等走 stdout——重定向 2>/dev/null 会"吞掉"建号成功的证据。

  7. CLI 操作立即对运行中的服务生效(直接写库,无需重启);禁用用户即刻阻断其登录,实测无需任何额外动作。

  8. 救急场景:管理员密码忘记且 SMTP 未配置(无法网页端找回)时,一条 gancio users reset-password admin 即可夺回控制权;误删 admin 用 gancio users create admin <密码> admin 重建(部署篇 4.x 的实测路径)。

六、settings:list / get / set 三子命令

6.1 子命令与实测回显

$ docker exec gancio gancio settings --help
📅 gancio - v1.28.2 - A shared agenda for local communities (nodejs: v20.19.6)
gancio <command>

Commands:
  gancio settings list                   List all settings
  gancio settings get <setting>          Get value of specified setting
  gancio settings set <setting> <value>  Set value of specified setting

Options:
      --version  Show version number                                   [boolean]
  -c, --config   Configuration file     [default: "/home/node/data/config.json"]
  -h, --help     Show help                                             [boolean]

settings list 输出数据库 settings 表全量 47 键(key: JSON值 逐行),前几行实测:

title: "Quzuo 社区日历"
description: "A shared agenda for local communities"
baseurl: "http://127.0.0.1:8149"
hostname: "127.0.0.1"
instance_timezone: "Europe/Rome"
instance_locale: "zh"

settings get <setting> 输出 <setting>: <JSON值>;键不存在时(实测):

$ docker exec --workdir /home/node/data gancio gancio settings get locale
📅 gancio - v1.28.2 - A shared agenda for local communities (nodejs: v20.19.6)
> Reading configuration from: /home/node/data/config.json
Setting "locale" does not exists

6.2 陷阱一:value 必须是合法 JSON

settings set 的源码在 cli/settings.js:42 对 value 做 JSON.parse(args.value),裸串直接炸出堆栈(实测):

$ docker exec --workdir /home/node/data gancio gancio settings set title 排错测试
title
Old value: "Quzuo 社区日历"
New value: 排错测试

Note that a restart is needed to get the new setting
...
SyntaxError: Unexpected token '排', "排错测试" is not valid JSON
    at JSON.parse (<anonymous>)
    at Object.set [as handler] (/home/node/server/cli/settings.js:42:53)

注意回显顺序迷惑性:先打 Old/New value 和 restart 提示,最后才 JSON.parse 失败——看到 New value: 不代表写库成功。正确写法是给值再包一层 JSON 双引号:

docker exec --workdir /home/node/data gancio gancio settings set title '"新的站点标题"'
docker exec --workdir /home/node/data gancio gancio settings set allow_registration 'false'
docker exec --workdir /home/node/data gancio gancio settings set geocoding_countrycodes '["cn","it"]'

字符串要 "...",布尔/数字/数组/对象直接写 JSON 字面量。写成功后 settings get 复查(实测回显 title: "Quzuo 社区日历" 形态)。

6.3 陷阱二:键名以 settings list 为准

settings 表键名有前缀约定,直觉名往往不存在:语言是 instance_locale(不是 locale)、时区是 instance_timezone(不是 timezone)、站点名是 instance_name。改前先 settings list 或 settings get 确认键名,报 Setting "<key>" does not exists 即键名错了。

6.4 陷阱三:改完必须重启;instance_name 另有联邦警告

  • 所有 set 都会提示 Note that a restart is needed to get the new setting(服务进程内存里缓存 settings,Docker 下 docker restart gancio 生效)。

  • 源码对 instance_name 有专属警告(cli/settings.js):

    Note that this will likely break current federation (all your follower / following connections will be destroyed)!
    

    联邦身份以如实例名派生,改名等于换人,改前想清楚。

七、调用姿势与守护回显

7.1 标准调用模板

CLI 读的是数据目录里的 config.json/SQLite,Docker 部署统一用:

docker exec --workdir /home/node/data gancio gancio <command>
  • --workdir /home/node/data 保证相对路径行为与容器服务一致(GANCIO_DATA=/home/node/data 时 -c 默认值本已正确,带上更保险);
  • 命令里第二个 gancio 是 CLI 程序名,第一个是容器名(按官方模板 container_name: gancio,自行改名则替换)。

7.2 路径错误的守护回显

users/settings 的公共初始化 _initializeDB() 检查 config.status !== 'CONFIGURED' 即退出。把 GANCIO_DATA 指到没有 config.json 的目录实测:

$ docker exec -e GANCIO_DATA=/tmp/nope --workdir /home/node/data gancio gancio users list
📅 gancio - v1.28.2 - A shared agenda for local communities (nodejs: v20.19.6)
> Reading configuration from: /tmp/nope/config.json
> Configuration file does not exists...
> Cannot run CLI before setup (are you in the correct path?)
RC=1

这条 are you in the correct path? 提示(退出码 1)就是"CLI 找不到 config.json"的标准症状:检查 -c 路径、GANCIO_DATA 变量、数据目录挂载是否齐全。与第四节 migrate 的同族消息区别:migrate 的 Cannot run database migration before setup 在 Docker 里是必然出现的坏路径,不代表配置有问题。

八、数据管理工具链

8.1 数据目录结构(容器内 /home/node/data,宿主挂载点)

data/
├── config.json          # 实例配置(setup 后生成,见配置篇)
├── gancio.sqlite        # SQLite 主数据库(dialect=postgresql/mariadb 时无此文件)
├── logs/                # winston 日志目录(config.log_path)
│   ├── gancio.log               # 当日主日志
│   ├── gancio.YYYY-MM-DD.log    # daily rotate 历史
│   └── .<hash>-audit.json       # rotate 策略元数据
└── uploads/             # 活动图片等上传文件(config.upload_path)

备份 = 整个 data 目录:DB + 配置 + 上传文件三者缺一不可,只 dump SQLite 会丢图片。

8.2 备份与恢复(官方口径 + 实测演练)

官方 backup 口径就是在数据目录内打 tar:

cd /opt/gancio/data
tar -czf gancio-$(date +%Y-%m-%d-%H%M%S)-backup.tgz \
  $(ls -d config.json uploads gancio.sqlite logs 2>/dev/null)

实测产物 16131 字节(含一条活动与一张占位数据的完整库)。恢复演练实测流程:

docker stop gancio
mv data data.broken                      # 或删掉 gancio.sqlite 模拟库损
mkdir data && tar -xzf gancio-*-backup.tgz -C data
docker start gancio
curl -s http://127.0.0.1:8149/api/events  # 仍返回备份时的活动 id=1,恢复成功

恢复后首次启动会跑增量迁移(版本一致则无操作),无需手工干预——这正是第四节"迁移交给 start"的又一次佐证。

8.3 日志查看

  • 服务日志:docker logs gancio(容器 stdout,含启动横幅与迁移回显)与 data/logs/gancio.log(winston 文件,daily rotate)内容同源互补,排障先看文件日志(带时间戳与级别)。
  • 日志级别由 config.json 的 log_level 控制(默认 debug,生产可降 info,改后重启生效)。
  • CLI 子进程自带降噪:_initializeDB() 里强制 config.log_level = 'error',所以 users/settings 执行时不会把服务端 debug 日志混进回显。

8.4 直接查库:容器内没有 sqlite3 客户端

实测 which sqlite3 为空——cisti/gancio 镜像是纯运行时,不带 sqlite3 CLI。需要裸查数据库时的三个替代:

# 1. 宿主装 sqlite3,直接开挂载文件(服务运行中慎写,读无妨)
sqlite3 /opt/gancio/data/gancio.sqlite 'SELECT id,role,is_active,email FROM "User";'

# 2. 容器内用自带的 node + sequelize 跑一次性查询
docker exec gancio node -e "
const db=require('/home/node/server/api/models/index');
db.initialize().then(()=>db.models.User.findAll())
  .then(us=>{us.forEach(u=>console.log(u.id,u.role,u.is_active,u.email));return db.close()})"

# 3. 导出 SQL 转储(宿主装 sqlite3 后 .dump)

日常需求优先走 CLI(第五、六节)与 REST API,避免裸写库。

九、排障工具链速查

症状工具/命令实测判据
服务是否活着curl -s http://127.0.0.1:8149/api/pingREADY 后返回 OK
CLI 报"before setup"检查 -c/GANCIO_DATA/挂载见 7.2;migrate 见第四节
忘管理员密码gancio users reset-password admin回显随机新密码
管理员被误删gancio users create admin <密码> admin回显 created with role admin
改 settings 不生效docker restart gancioCLI 原话要求重启
确认某配置值gancio settings get <key>读的是库值,重启后前端才同步
用户登不上gancio users list 看 enabledis_active=false 即被禁用
升级后疑似迁移失败docker logs gancio | grep migratedumzug 逐条 == <ts>-<name>: migrated
联邦/订阅面检查GET /feed/rss、/.well-known/nodeinfoRSS 频道标题即 title 设置值
登录接口手工验证POST /oauth/login(form,client_id=self)返回 access_token 即凭据正常

十、小结与互链

  • Gancio CLI 命令面小而实:users 是账号急救刀,settings 是库内配置的裸接口,start 顺带负责全部迁移,migrate 在 Docker 镜像里是坏路径可以无视。
  • 使用 settings set 记住三件事:值要 JSON、键名查 list、改完重启。
  • 备份认 data 目录整体,恢复靠重启自动对齐 schema。

系列文档:

Gancio 1.28.2 常用 CLI 命令与工具详解 - Quzuo