ClearFlask 2.7.2 常用 CLI 命令与 API 详解

ClearFlask 2.7.2 运维 CLI 与 REST API 实操全解:systemd 启停与健康核查、Tomcat 自带脚本、MySQL/DynamoDB/S3 三层备份命令实测回显、超管登录双 Set-Cookie 破解、建项目/读写配置/建门户用户/提交反馈/投票/导出全链路 curl 实操与四大坑(connect-token、门户 403 强跳 HTTPS、voteValue 枚举、support.vote 前置)。

ClearFlaskClearFlaskJava2026/11/01

ClearFlask 2.7.2 常用 CLI 命令与 API 详解

目标:覆盖 ClearFlask 2.7.2 运维日常的两条线——服务侧 CLI(进程启停与健康核查、Tomcat 自带脚本、MySQL/DynamoDB/S3 三层备份、搜索索引 JMX 迁移)与 REST API 实操(超管登录、建项目、读写项目配置、建门户用户、提交反馈、投票、导出),全部命令与回显为 2026-11-01 本地实测照抄。

一、环境信息

项目值
组件版本ClearFlask 2.7.2
对外入口http://127.0.0.1:8130(Node 前端服务,API 前缀 /api/v1)
Java 服务内部地址http://127.0.0.1:8080(仅服务网内可达,不对外发布)
Java 运行时Apache Tomcat 9.0.123 + JVM 17.0.20.1+1(Eclipse Adoptium)
索引库MariaDB 10.5.29,库名 clearflask,root 密码 clearflask(内置弱口令,仅内网)
源数据存储DynamoDB 兼容服务(端点 :4566,单表 clearflaskprimary;S3 桶 local-upload)
实测账号超管 admin@localhost / q1w2e3r4;门户用户 feedback@localhost / q1w2e3r4

⚠️ 宿主前置:本文启停/登录主机的命令按真机(systemd 或等价进程管理 + 可直接 SSH 到各服务主机)形态书写。本系列实测环境的各服务进程由部署编排拉起(见 《ClearFlask 2.7.2 Docker Compose 部署指南》),aws/mysqldump/catalina.sh 等命令均在对应服务所在主机上执行,回显与真机等价。

二、服务启停与健康核查

2.1 启停(真机形态)

systemctl restart clearflask-server    # 改配置后重启 Java 服务
systemctl restart clearflask-connect   # 改 connect.config.json 后重启 Node 服务

2.2 健康与进程核查(实测回显)

$ curl -s http://127.0.0.1:8130/api/health
ok
$ ss -ltn | grep ':8130\b'
LISTEN 0 4096 0.0.0.0:8130 0.0.0.0:*
$ ps -ef | grep 'catalina.base' | grep -v grep | head -1
root 792229 792202 15 19:32 ? 00:05:32 /opt/java/openjdk/bin/java -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties ... -Xms256m -Xmx512m ... org.apache.catalina.startup.Bootstrap start

Java 服务就绪的日志标志(实测冷启动):

Server startup in 13309 ms

Node 服务启动标志:App on 9080 (selfhost);页面渲染成功会逐条打 Rendered http://demo.localhost/ in 2 pass(es)。

2.3 Tomcat 自带脚本

Java 服务安装目录 bin 实测清单(11 个):

catalina.sh  ciphers.sh  configtest.sh  daemon.sh  digest.sh
makebase.sh  setclasspath.sh  shutdown.sh  startup.sh  tool-wrapper.sh  version.sh

版本核查实测回显:

$ catalina.sh version
Server version: Apache Tomcat/9.0.123
Server built:   Oct 6 2026 18:59:20 UTC
Server number:  9.0.123.0
OS Name:        Linux
OS Version:     7.0.0-38-generic
Architecture:   amd64
JVM Version:    17.0.20.1+1
JVM Vendor:     Eclipse Adoptium

日常最常用:configtest.sh(校验 Tomcat 层配置)、shutdown.sh/startup.sh(优雅停启,通常由进程管理器代管)、catalina.sh run(前台排障)。

三、备份与恢复(三层数据)

ClearFlask 数据分三层,备份缺一不可:源数据(DynamoDB 兼容层)、搜索索引(MySQL,可由源数据重建)、配置文件(含安装密钥)。

3.1 MySQL 索引库 mysqldump(实测)

$ mysqldump -h <索引库主机> -uroot -pclearflask clearflask > backup-mysql.sql
$ ls -l backup-mysql.sql
-rw-r--r-- 1 root root 9139 Nov  1 20:07 backup-mysql.sql
$ head -5 backup-mysql.sql
/*M!999999\- enable the sandbox mode */
-- MariaDB dump 10.19  Distrib 10.5.29-MariaDB, for debian-linux-gnu (x86_64)
--
-- Host: localhost    Database: clearflask

库内 7 表实测:

$ mysql -h <索引库主机> -uroot -pclearflask -e "use clearflask; show tables;"
Tables_in_clearflask
account
comment
comment_parent_id
idea
idea_funders
idea_tags
user

💡 这 7 张表只是搜索镜像:select count(*) from user 实测为 0 而账号仍可登录——业务源数据在 DynamoDB 兼容层单表 clearflaskprimary。恢复时 MySQL 层可整库重导,也可清空后靠启动参数 createIndexesOnStartup=true + JMX reindexProjects 重建。

3.2 DynamoDB / S3(AWS CLI 指向兼容端点)

官方 README 调试节给出的标准用法——aws --endpoint-url=http://<兼容层>:4566(内置环境亦有 awslocal 快捷方式,实测回显):

$ aws --endpoint-url=http://127.0.0.1:4566 dynamodb list-tables
{
    "TableNames": [
        "clearflaskprimary"
    ]
}
$ aws --endpoint-url=http://127.0.0.1:4566 s3 ls
2026-11-01 11:02:14 local-upload

备份处置:源数据用 dynamodb export/逐项 scan 导出或直接持久化存储目录;附件桶 s3 sync local-upload ./backup/。

3.3 配置与整体导出

  • server/config-selfhost.cfg:含 VAPID/cursor/tokenSigner/SSO/webhook 五组安装密钥,chmod 600 后随备份归档(参数含义见 《ClearFlask 2.7.2 配置参数详解》)。
  • 业务整体导出(超管 API,实测返回 ZIP):
$ curl -s -o project.zip -w '%{http_code}\n' \
    -H "x-cf-connect-token: $CONNECT_TOKEN" -H "Cookie: cf_act_auth=$TOK; cf_sup_auth=$TOK" \
    http://127.0.0.1:8080/api/v1/project/demo-9j4rx8vf/admin/export
200
$ file project.zip
project.zip: Zip archive data

四、REST API 实操(全链路实测)

API 定义即官方仓库 clearflask-api/src/main/openapi/api-*.yaml(OpenAPI,前端与后端共用),路径按域分组:/admin/account/*(超管账号)、/project/admin(建项目)、/project/{projectId}/admin/*(项目内管理)、/project/{projectId}/*(门户用户侧)。

4.1 认证机制(先读这节)

三层会话 Cookie + 一个内部头,实测结论:

凭据获取方式作用域
cf_act_auth超管登录仪表盘账号会话
cf_sup_auth超管登录(同时下发)超级管理员标记
cf_usr_auth_<projectId>门户登录 POST /project/{pid}/login仅该项目门户,会话绑定 projectId,与路径不符直接 403(日志 Potential attack attempt)
x-cf-connect-token部署时生成的共享令牌直连 Java 服务 8080 时必须携带(AuthenticationFilter 的 Role.CONNECT 校验)

两个实测坑:

  1. 登录响应的双 Set-Cookie:第二条带 Domain=... Expires=Thu, 01 Jan 1970 是清除动作,curl -c jar 会被抵消成空 jar。正确做法是手工提取 token 再以 -H "Cookie: ..." 传递:
$ TOK=$(curl -s -D- -X POST http://127.0.0.1:8130/api/v1/admin/account/login \
    -H 'Content-Type: application/json' \
    -d '{"email":"admin@localhost","password":"q1w2e3r4"}' \
    | grep -oP 'cf_sup_auth=\K[^;]+' | head -1)
$ echo "$TOK"
5a2362d290a742088e7a...   # 32 位小写十六进制会话 ID
  1. 门户子域写操作在纯 HTTP 环境被拒:经入口对 demo.localhost 的 POST 返回 403 且带 Location: https://demo.localhost/...(无证书形态下 Node 侧强制 HTTPS)。本地实操请直连 Java 服务内部地址并带 x-cf-connect-token(下文示例均此形态)。

4.2 建项目(POST /project/admin)

先读当前状态(新建环境实测为空):

$ curl -s -H "Cookie: cf_act_auth=$TOK; cf_sup_auth=$TOK" \
    http://127.0.0.1:8130/api/v1/project/config/admin
{"byProjectId":{}}

创建请求体是完整的项目 Config(必填链极深:layout.pages/menu、content.categories[].workflow/support/tagging、style.palette/typography/animation/whitelabel、users.onboarding.notificationMethods.email 等,逐项语义见官方 api-project.yaml)。实测可成功的最小完整体:

{
  "test": false,
  "projectId": "demo-project",
  "name": "DemoBoard",
  "slug": "demo",
  "cookieConsent": {},
  "layout": {
    "pages": [{"pageId": "page1", "name": "Feedback", "slug": "feedback", "components": {}, "panels": []}],
    "menu": [{"menuId": "menu1", "pageIds": ["page1"], "name": "Feedback"}]
  },
  "content": {
    "categories": [{
      "categoryId": "cat1", "name": "Ideas", "userCreatable": true, "visibility": "Public",
      "workflow": {"statuses": [{"statusId": "st1", "name": "Started", "disableFunding": false,
        "disableVoting": false, "disableExpressions": false, "disableIdeaEdits": false, "disableComments": false}]},
      "support": {"fund": false, "comment": true},
      "tagging": {"tags": [], "tagGroups": []}
    }]
  },
  "style": {"palette": {"darkMode": false}, "typography": {}, "animation": {"enableTransitions": true},
            "whitelabel": {"poweredBy": "Show"}},
  "users": {"displayName": {}, "onboarding": {
      "visibility": "Public", "accountFields": {"displayName": "Optional"},
      "notificationMethods": {"browserPush": false, "oauth": [],
        "email": {"mode": "SignupAndLogin", "verification": "None"}}}},
  "integrations": {}
}

实测回显(节选)——注意 projectId 会被服务端重新分配(demo-project → demo-9j4rx8vf),且响应里附带超管在该项目的门户身份 admin-swt4n:

HTTP 200
{"projectId":"demo-9j4rx8vf","config":{"config":{...,"slug":"demo",...},"version":"new"},
 "user":{"userId":"admin-swt4n","name":"Admin","isMod":true,"email":"admin@localhost",...}}

💡 缺字段的报错是逐层引导式的,实测样本:500 NullPointerException(users.onboarding.notificationMethods.email 缺)→400 Json missing non null field verification in EmailSignup→400 Expected a boolean but was BEGIN_OBJECT at path $.users.onboarding.notificationMethods.browserPush→400 Json missing non null field menu in Layout。照上面的完整体抄即可一次通过;日常更推荐走仪表盘向导建项目,再用 API 读回改。

4.3 读 / 改项目配置

$ curl -s -H "x-cf-connect-token: $CONNECT_TOKEN" -H "Cookie: cf_act_auth=$TOK; cf_sup_auth=$TOK" \
    http://127.0.0.1:8080/api/v1/project/demo-9j4rx8vf/admin/config   # GET 读全量 Config
$ curl -s -X PUT -H 'Content-Type: application/json' \
    -H "x-cf-connect-token: $CONNECT_TOKEN" -H "Cookie: cf_act_auth=$TOK; cf_sup_auth=$TOK" \
    --data @config-updated.json \
    http://127.0.0.1:8080/api/v1/project/demo-9j4rx8vf/admin/config   # PUT 回写全量

PUT 是全量替换:先 GET、改目标字段、整体回写。类型校验严格,实测反例——给 support.vote.iWantThis(对象)塞了布尔:

HTTP 500
JsonSyntaxException: Expected BEGIN_OBJECT but was BOOLEAN at path $.content.categories[0].support.vote.iWantThis

本例把分类改为允许投票:"vote": {"enableDownvotes": false} 后 PUT 返回 200。

4.4 门户用户:创建与登录

$ curl -s -X POST -H 'Content-Type: application/json' \
    -H "x-cf-connect-token: $CONNECT_TOKEN" -H "Cookie: cf_act_auth=$TOK; cf_sup_auth=$TOK" \
    -d '{"name":"Feedback User","email":"feedback@localhost","password":"q1w2e3r4"}' \
    http://127.0.0.1:8080/api/v1/project/demo-9j4rx8vf/admin/user
{"userId":"feedback-user-pjv4","name":"Feedback User","email":"feedback@localhost","hasPassword":true,"balance":0,...}

$ curl -s -D- -X POST -H 'Content-Type: application/json' \
    -H "x-cf-connect-token: $CONNECT_TOKEN" \
    -d '{"email":"feedback@localhost","password":"q1w2e3r4"}' \
    http://127.0.0.1:8080/api/v1/project/demo-9j4rx8vf/login | grep -i 'set-cookie\|HTTP/'
HTTP/1.1 200
Set-Cookie: cf_usr_auth_demo-9j4rx8vf=ba2a6e08ea234ae88ede1e135c54f2ee; Path=/; HttpOnly; SameSite=Strict; Expires=Sat, 28 Aug 2027 12:25:27 GMT
Set-Cookie: cf_usr_auth_demo-9j4rx8vf=; Domain=localhost; Expires=Thu, 01 Jan 1970 00:00:00 GMT

门户会话 Cookie 名即 cf_usr_auth_<projectId>(取第一条有效值)。超管账号本身在门户的自动身份(admin-swt4n)无密码,/login 直登实测 401 Email or password incorrect——需要先用 4.4 的方式给用户设密码或用独立邮箱。

4.5 提交反馈与投票

# 管理侧建 idea(也可由门户用户经 /idea 端点创建,需门户会话)
$ curl -s -X POST -H 'Content-Type: application/json' \
    -H "x-cf-connect-token: $CONNECT_TOKEN" -H "Cookie: cf_act_auth=$TOK; cf_sup_auth=$TOK" \
    -d '{"authorUserId":"admin-swt4n","title":"支持导出反馈为CSV","description":"希望仪表盘能一键导出全部反馈数据为CSV。","categoryId":"cat1","tagIds":[],"statusId":"st1"}' \
    http://127.0.0.1:8080/api/v1/project/demo-9j4rx8vf/admin/idea
{"ideaId":"csv-eowcvlnv","authorName":"Admin","title":"支持导出反馈为CSV","categoryId":"cat1","statusId":"st1","vote":{},...}

# 门户用户投票(vote 是枚举字符串,不是数字!)
$ curl -s -X PATCH -H 'Content-Type: application/json' \
    -H "x-cf-connect-token: $CONNECT_TOKEN" \
    -H "Cookie: cf_usr_auth_demo-9j4rx8vf=$USER_SESSION" \
    -d '{"vote":"upvote"}' \
    http://127.0.0.1:8080/api/v1/project/demo-9j4rx8vf/vote/idea/csv-eowcvlnv
{"vote":{"vote":"upvote"},"idea":{"ideaId":"csv-eowcvlnv",...,"voteValue":1,...}}

投票三连实测坑(照抄可避):

写法实测结果
{"vote":"upvote"}✅ 200,voteValue:1(枚举:upvote/downvote/none)
{"vote":1}200 但 vote:{}——非法类型被静默忽略,不报错
分类未开 support.vote 时投票400 {"userFacingMessage":"Voting not allowed"}(先按 4.3 PUT 开启)
用超管会话打 PATCH /project/{pid}/admin/vote/idea/{ideaId}恒 403——该端点要求 PROJECT_MODERATOR_ACTIVE(含订阅态检查),self-host 账号不满足;投票走门户会话

公开检索(门户读,无需登录态):

$ curl -s -X POST -H 'Content-Type: application/json' -H "x-cf-connect-token: $CONNECT_TOKEN" \
    -d '{}' http://127.0.0.1:8080/api/v1/project/demo-9j4rx8vf/ideasearch
{"results":[{"ideaId":"csv-eowcvlnv",...,"vote":{}}],"hits":{"value":1,"isGte":true}}

4.6 其他常用端点速查

端点方法用途
/api/v1/admin/account/loginPOST超管登录(body {email,password})
/api/v1/admin/account/logoutPOST登出
/api/v1/project/config/adminGET全部项目 Config 索引
/api/v1/project/{pid}/admin/exportGET项目整体 ZIP 导出
/api/v1/project/{pid}/admin/idea/{ideaId}GET/PATCH/DELETE反馈读改删
/api/v1/project/{pid}/admin/idea/{ideaId}/merge/{parentId}PATCH反馈合并
/api/v1/project/{pid}/admin/idea/{ideaId}/votersGET投票人列表
/api/v1/project/{pid}/admin/usersearchPOST用户检索
/api/v1/project/{pid}/subscribe/category/{categoryId}PATCH分类订阅
/api/v1/webhook/githubPOSTGitHub App 回调(配置见 C1)

五、搜索索引 JMX 运维(MySQL ↔ ES 迁移)

官方维护流程(README "Migration between Elasticsearch/MySQL"),前提:为 Java 服务开启 JMX 端口发布(9950/9951),连接无需凭据与 SSL:

service:jmx:rmi:///jndi/rmi://localhost:9950/jmxrmi

用 jconsole/VisualVM 调用 com.smotana.clearflask.web.resource.ProjectResource 的两个操作:

操作参数用途
createIndexesMySQL 方向 (false, true);ES 方向 (true, false)在目标引擎建索引
reindexProjectsES→MySQL (true, false, true);MySQL→ES (true, true, false)全量重灌索引

切换节奏:READWRITE_旧 → READ_旧_WRITE_BOTH →(reindex)→ READ_新_WRITE_BOTH → READWRITE_新 → 下线旧引擎;每步重启 Java 服务生效,期间盯日志告警,可用仪表盘"项目高级设置"对单项目强制覆盖引擎做灰度验证。

六、实测排障记录

症状根因处置
curl -c jar 登录后 jar 为空双 Set-Cookie 一设一删手工 grep token 传 -H Cookie(4.1)
门户域 POST 恒 403 + Location: https无证书形态 Node 侧强跳 HTTPS直连 Java 内部 8080 + x-cf-connect-token,或配 TLS
建项目 500/400 连环报错Config 必填链深、类型严格照 4.2 完整体;错误信息逐字段指路
投票 200 但没票数vote 传了数字被静默忽略用枚举字符串 upvote/downvote/none
user 表 0 行但能登录MySQL 仅索引镜像查源数据去 DynamoDB 兼容层 clearflaskprimary
注册 admin@localhost 返回 409账号已存在直接登录;改白名单见 C1

七、相关文章

参考链接

ClearFlask 2.7.2 常用 CLI 命令与 API 详解 - Quzuo