Filestash 配置参数详解

Filestash 配置体系实测详解:/api/config 与 /admin/api/config 双端点、全量参数 Schema 清单、在线修改四参数行为验证、全量替换清空连接的事故回放与还原基线(v0.6.20261022,2026-10-22 实测)。

FilestashFilestash文件管理2026/10/22

Filestash 配置参数详解

Filestash 的全部行为都由一份集中式配置驱动:管理员密码、存储后端连接、界面偏好、日志级别、共享策略、Office 协同……都落在同一份配置里。与多数软件"改配置文件 + 重启"不同,Filestash 的配置可以通过管理 API 在线修改并立即生效(无需重启进程)。本文是 Filestash 专栏的配置专题:完整给出配置体系的两个 API 端点、落盘文件位置、全部参数的 Schema 清单与字段语义,并对四个有代表性的参数做"在线修改 → 行为验证 → 还原"的全流程实测,包括一次真实的"全量替换把存储后端连接清空"的事故回放。全部命令与回显均来自真实服务器执行记录(版本 v0.6.20261022,实测日期 2026-10-22)。

相关阅读:《Ubuntu 22.04 安装 Filestash 指南》完成了安装、systemd 托管与初始化(设置管理员密码、添加存储后端);本文在其基础上展开"配置到底有哪些、怎么改、改了会发生什么"。

一、环境信息说明

项目值
演示服务器filestash-server(10.89.57.2),独立 Ubuntu 22.04 服务器
操作系统Ubuntu 22.04.5 LTS(x86_64)
服务端口8334
Filestash 版本v0.6.20261022(构建 commit 244e8b74ebdf60cdbd650a24c1f22569d839a814,从 /api/config 的 version 字段读取)
配置落盘文件/opt/filestash/data/state/config/config.json
管理员账号admin / q1w2e3r4(本文所有管理操作均以此身份登录)
存储后端 ASFTP 服务器 10.89.57.3(OpenSSH,账号 root / q1w2e3r4)
存储后端 BWebDAV 服务器 10.89.57.4:8080(wsgidav,账号 admin / q1w2e3r4)
连接标签connections 中已配置 {type: sftp, label: SFTP 服务器} 与 {type: webdav, label: WebDAV 服务器}

版本与 commit 的取证方式(/api/config 无需认证,响应中直接带有版本信息):

$ curl -s http://127.0.0.1:8334/api/config | python3 -m json.tool | grep -E '"(version|license|name)"'
    "license": "agpl",
    "name": "Filestash",
    "version": "244e8b74ebdf60cdbd650a24c1f22569d839a814",

二、配置体系总览:两个端点 + 一个落盘文件

Filestash 的配置管理由三部分组成:

组成地址/路径谁在用说明
前台配置端点GET /api/configWeb 前端、脚本公开只读,无需认证。返回前端需要的配置子集 + 内置 MIME 注册表(约 308 个扩展名映射)+ 版本号
管理配置端点GET/POST /admin/api/config管理后台 /admin/、运维脚本需要管理员会话(见第三节)。GET 返回全量参数 Schema;POST 提交修改
配置落盘文件/opt/filestash/data/state/config/config.jsonFilestash 进程唯一事实源。通过 API 提交的修改立即写入该文件并即时生效,无需重启

三者关系:管理员在 Web 后台 /admin/ 界面上做的每一次修改,底层就是一次 POST /admin/api/config;POST 成功后配置写入 config.json 并立刻影响运行中的进程。因此"改配置"与"脚本化改配置"走的是同一条路,本文以 API 视角完整实测。

/api/config(前台端点)返回内容节选——注意它的键名是"前端视角"的扁平命名(如 default_view、enable_share),与后台 Schema 的层级命名(general.filepage_default_view、features.share.enable)存在对应关系:

$ curl -s http://127.0.0.1:8334/api/config | python3 -m json.tool | head -20
{
    "result": {
        "auth": [],
        "connections": [
            {"label": "SFTP 服务器", "type": "sftp"},
            {"label": "WebDAV 服务器", "type": "webdav"}
        ],
        "default_sort": "type",
        "default_view": "grid",
        "display_hidden": false,
        "editor": "emacs",
        "enable_chromecast": true,
        "enable_search": true,
        "enable_share": true,
        "enable_tags": true,
        "license": "agpl",
        "logout": "",
        ...(mime 注册表,约 308 个扩展名映射)...
        "name": "Filestash",
        "open_mode": "single_click",
        "origin": "",
        "refresh_after_upload": false,
        "share_default_access": "editor",
        "share_redirect": "",
        "thumbnailer": ["image/jpeg", "image/png", "image/gif"],
        "upload_button": false,
        "upload_chunk_size": 0,
        "upload_pool_size": 0,
        "version": "244e8b74ebdf60cdbd650a24c1f22569d839a814"
    },
    "status": "ok"
}

字段对应关系(实测验证,见第八节):default_view ← general.filepage_default_view,display_hidden ← general.display_hidden,enable_share ← features.share.enable,share_default_access ← features.share.default_access,name ← general.name,connections ← 配置里的 connections 数组。

三、管理会话与访问控制

3.1 未登录访问管理端点:403

/admin/api/config 受管理员口令保护。未携带会话直接访问,返回 403:

$ curl -s -D- http://127.0.0.1:8334/admin/api/config
HTTP/1.1 403 Forbidden
Content-Type: text/plain
...

同一时刻服务端日志(/opt/filestash/data/state/log/access.log)中的对应记录:

2026/10/22 04:50:14 HTTP 403 GET 0.0ms /admin/api/config

3.2 建立管理会话

用管理员密码 POST /admin/api/session 建立会话(Cookie 保持):

$ curl -s -c /tmp/admin.cookie -H 'X-Requested-With: XmlHttpRequest' \
    -H 'Content-Type: application/json' \
    -d '{"password": "q1w2e3r4"}' \
    http://127.0.0.1:8334/admin/api/session
{"status": "ok", "result": true}

日志中的对应记录(注意耗时约 1.5 秒——这是服务端做 bcrypt 口令校验的成本,属正常现象而非卡顿):

2026/10/22 04:44:34 HTTP 200 POST 1538.7ms /admin/api/session

会话建立后,同一 Cookie 连接即可 GET /admin/api/config。管理 API 还要求请求头 X-Requested-With: XmlHttpRequest(与前台 API 一致),缺失时同样会被拒绝。

四、配置项 Schema 结构详解

GET /admin/api/config 返回的不是"当前值"那么简单,而是全量参数的 Schema 表单:每个参数都带标签、类型、描述、默认值与当前值。这是理解 Filestash 配置的钥匙——官方把配置做成了"自描述表单",Web 管理后台就是拿这份 Schema 渲染出来的。

4.1 参数节点的公共字段

每个叶子参数节点都包含以下字段(以 general.display_hidden 为例):

"display_hidden": {
    "label": "display_hidden",
    "type": "boolean",
    "description": "Should files starting with a dot be visible by default?",
    "readonly": false,
    "default": false,
    "value": null,
    "required": false
}
字段含义实测备注
label参数名Web 后台的表单标签
type数据类型见 4.2 类型表
description官方描述(英文原文)最权威的参数说明来源
placeholder输入提示部分参数带有默认值提示,如 Default: 8334
readonly是否只读constant 节点下的参数为 true
default默认值未显式配置时生效的值
value当前值null 表示未显式设置、跟随 default
required是否必填general.secret_key 为 true
options可选项列表仅 select 类型有,如 filepage_default_view 的 [list, grid]
pattern值的正则约束secret_key 为 [a-zA-Z0-9]{16}
id跨层引用 ID被 enable 型开关的 target 引用(见 4.3)
target联动目标列表仅 enable 型开关有(见 4.3)

value: null 的语义是本文最重要的一个 Schema 结论:null 不代表"空",而是"跟随默认值"。落盘文件里对应键也是 null(见第九节还原基线)。修改参数 = 把 value 从 null 改成显式值;还原参数 = 把它改回 null。

4.2 参数类型一览(9 种)

type含义实例
text单行文本general.name、general.host
number数字general.port、general.cookie_timeout
boolean布尔开关general.display_hidden、log.telemetry
select单选(带 options)log.level(DEBUG/INFO/WARNING/ERROR)、general.editor(base/emacs/vim)
password密码/密钥general.secret_key、email.password
long_text多行文本general.custom_css
enable功能总开关(带 target 联动)features.share.enable、log.enable
hidden隐藏字段(不在后台表单渲染)middleware.identity_provider.type/params
bcryptbcrypt 口令哈希auth.admin

4.3 enable 型开关的 target 联动机制

enable 型参数是"功能总开关",其 target 字段列出被它管辖的子参数,被管辖的子参数用 id 标识。以日志为例:

"log": {
    "enable": {
        "label": "enable", "type": "enable",
        "target": ["log_level"],        // 管辖 id 为 log_level 的参数
        "default": true, "value": null, ...
    },
    "level": {
        "id": "log_level",              // 被 log.enable 的 target 引用
        "label": "level", "type": "select",
        "options": ["DEBUG", "INFO", "WARNING", "ERROR"],
        "default": "INFO", "value": null, ...
    },
    ...
}

同样的结构出现在 features.workflow.enable(target: workflow_workers / workflow_max_queue)、features.office.enable(target: office_server / filestash_server / rewrite_discovery_url)、features.site.enable(target: site_autoindex / site_cors_allow_origins)、features.video.enable_transcoder(target: transcoding_blacklist_format / transcoding_video_encoder)。工程含义:关掉总开关后,其 target 子参数即失效,无需逐个清理。

五、general 参数全表(20 项)

以下全部字段取自 GET /admin/api/config 实测返回(2026-10-22,v0.6.20261022)。"默认值"列即 Schema 的 default;"当前值"列为实测环境初始状态(null = 跟随默认)。

参数类型默认值官方描述(译注)
general.nametextFilestash界面上显示的站点名称
general.portnumber8334服务监听端口
general.hosttextnull用户访问本服务所用的主机名(影响对外链接生成)
general.secret_keypassword(required)首启随机生成 16 位加解密密钥。修改会使既有用户会话与共享链接全部失效
general.force_sslbooleannull启用 HSTS(Strict Transport Security)
general.editorselectemacs编辑器键位:base / emacs / vim
general.logouttext""(空串)点击退出按钮后的跳转 URL
general.display_hiddenbooleanfalse是否默认显示点开头的隐藏文件
general.refresh_after_uploadbooleanfalse上传完成后刷新目录列表
general.open_modeselectsingle_click文件/文件夹打开方式:single_click / double_click
general.upload_buttonbooleanfalse在任何设备上显示上传按钮
general.upload_pool_sizenumber15并行上传文件数上限
general.upload_chunk_sizenumber0上传分块大小(MB)
general.buffer_sizeselectmedium传输 I/O 缓冲:small / medium / large(大缓冲提升高带宽网络吞吐,占用更多内存)
general.filepage_default_viewselectgrid文件页默认布局:list / grid
general.filepage_default_sortselecttype文件页默认排序:type / date / name
general.cookie_timeoutnumber10080认证 Cookie 有效期(分钟),默认 60×24×7 = 一周
general.extended_sessionbooleanfalse会话中存储额外认证数据
general.custom_csslong_text""站点自定义 CSS
general.licensetextnull商业许可密钥(联系 support@filestash.app 获取)

重点解读三条:

  1. general.secret_key 是整个系统的信任根:用户会话 Cookie 与共享链接 URL 都用它加密/签名。描述原文明确警告 "Update this settings will invalidate existing user sessions and shared links, use with caution!"(修改此项将使现有用户会话与共享链接失效,谨慎操作)。改它等于全员登出 + 全部共享链接作废,变更窗口要规划好。
  2. general.host 与共享链接/回调有关:填错会导致生成的对外链接不可达;反代场景下应填用户实际访问的域名。
  3. general.cookie_timeout 控制登录态寿命:默认 10080 分钟(一周)。对安全敏感的部署建议按需调小。

六、features 参数组全表(11 组 28 项)

6.1 share(共享)

参数类型默认值说明
features.share.enableenabletrue启用/禁用共享功能(target 无子参数;实测仅影响前端入口,见 8.4)
features.share.default_accessselecteditor共享链接默认权限:editor / viewer
features.share.redirecttextnull共享链接重定向模板,如 https://example.com?full_path={{path}}

6.2 protection(防护)

参数类型默认值说明
features.protection.enableenabletrue针对扫描器的主动防护
features.protection.zip_timeoutnumber60打包下载/解压 zip 的超时(秒)
features.protection.disable_svgbooleantrue禁用 SVG 展示(防 XSS,placeholder 注明 Default: true)
features.protection.disable_cspbooleanfalse关闭内容安全策略(除非完全信任存储内容,否则不应开启)
features.protection.iframetext""允许 iframe 嵌入本站的域名列表,如 https://example.com
features.protection.signaturetext""强制对认证 URL 参数验签
features.protection.enable_chromecastbooleantrue允许投屏到 Chromecast(浏览器需访问 Google 服务器下载 SDK)

6.3 其余功能组

组参数(类型/默认值)说明
apifeatures.api.enable(boolean / true)启用/禁用 REST API
collaborativefeatures.collaborative.enable(enable / true)协同编辑
workflowfeatures.workflow.enable(enable / true);workers(number / 1);max_queue(number / 5)工作流引擎总开关;并行 worker 数;每工作流最大排队任务数
officefeatures.office.enable(enable / false);office_server(text / http://127.0.0.1:9980);filestash_server(text / http://app:8334);rewrite_discovery_url(text / "")WOPI Office 协同:Office 服务器地址(Collabora 默认 9980);Office 服务器回访 Filestash 的地址;浏览器侧 Discovery URL 重写(反代/编排部署用)
mcpfeatures.mcp.enable(enable / false)Model Context Protocol 网关(供 AI Agent 访问文件)
sitefeatures.site.enable(enable / false);autoindex(boolean / false);cors_allow_origins(text / null)通过共享链接建站,站点挂在 /public/{shareID}/;无 index 文件时是否自动列目录;CORS 允许来源(* 或逗号分隔列表)
searchfeatures.search.explore_timeout(number / 1500)全文搜索未启用时,递归探索目录的时限(毫秒,默认 1500ms)
videofeatures.video.enable_transcoder(enable / true);encoder(select / libx264,可选 libx264 / h264_vaapi / h264_nvenc / h264_v4l2m2m);blacklist_format(text / "")按需 HLS 转码(依赖 ffmpeg,未装则自动禁用并打 WARN 日志);硬件编码器选择;不转码的格式黑名单
serverfeatures.server.console_enable(boolean / false)内置交互式 Web 控制台,开启后位于 /admin/tty/(用户名 admin,密码同管理后台)

七、log / email / auth / middleware / constant

7.1 log(日志)

参数类型默认值说明
log.enableenabletrue日志总开关(target: log_level)
log.levelselectINFO日志级别:DEBUG / INFO / WARNING / ERROR。实测 DEBUG 级别的两个可见变化见 8.3
log.telemetrybooleanfalse匿名遥测(官方描述明确:不与任何第三方共享,仅用于改进软件)

日志的落盘位置与逐行格式解读(HTTP 请求行、AUDIT 审计行、SYST 系统行的完整分析)见本专栏下一篇《Filestash 运维管理与 API 实操详解》。

7.2 email(SMTP 邮件,5 项)

参数类型默认值说明
email.servertextsmtp.gmail.comSMTP 服务器地址
email.portnumber587SMTP 端口
email.usernametextnullSMTP 认证用户名
email.passwordpasswordnullSMTP 认证密码
email.fromtextnull发件人地址(收件方可见)

配置后用于共享链接的邮件验证等通知场景(共享访问页存在 email 验证步骤)。

7.3 auth / middleware / constant

"auth": {
    "admin": {
        "label": "admin", "type": "bcrypt",
        "description": "Password of the admin section.",
        "default": "", "value": "$2b$10$hJXK4pqyc9DGDizo6Rp7/Ojc8Tqds5KDSCgGqL/zUDsBE9cUxdCpK", ...
    }
}
  • auth.admin(bcrypt 类型):管理后台口令,存的是 bcrypt 哈希($2b$10$...,cost=10——这也解释了 3.2 节登录耗时约 1.5 秒)。安装初始化时设置,后续可在后台更换。
  • middleware.identity_provider / middleware.attribute_mapping(hidden 类型,各含 type/params 或 related_backend/params 子字段):认证与属性映射插件的挂载点。默认未配置任何插件(/healthz 首启报的 middleware.identity_provider[type=][params=0] 即指这里),安装篇采用的就是"无认证插件 + 本地连接列表"的最简形态。
  • constant(readonly=true,运行时常量,不可改):
"constant": {
    "user":    {"type": "boolean", "readonly": true, "value": "filestash"},
    "license": {"type": "text",    "readonly": true, "value": "agpl"}
}

constant.user 是运行进程的操作系统账号(安装篇创建的 filestash 系统用户),constant.license 是当前许可形态(agpl)。两者只读,用于后台页面展示。

八、配置修改实操与四个参数的行为验证

8.1 修改流程:GET Schema → 提交全量 → 验证

POST /admin/api/config 的请求体是整份配置的"压平-展开"形态:把 Schema 树里的每个叶子节点取 value 组成同构 JSON 提交。实测采用的压平规则(与安装篇初始化脚本一致):

import json, urllib.request, urllib.error, http.cookiejar

cj = http.cookiejar.CookieJar()
op = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj))
H = {"Content-Type": "application/json", "X-Requested-With": "XmlHttpRequest", "User-Agent": "Mozilla/5.0"}
BASE = "http://127.0.0.1:8334"

def call(method, url, payload=None, extra=None):
    data = json.dumps(payload).encode() if payload is not None else None
    hh = dict(H); hh.update(extra or {})
    try:
        r = op.open(urllib.request.Request(url, data=data, headers=hh, method=method))
        return r.status, json.loads(r.read().decode())
    except urllib.error.HTTPError as e:
        return e.code, e.read().decode().strip()[:200]

def flatten(o):
    """取每个叶子节点的 value;number 类型转 int"""
    out = {}
    for k, v in o.items():
        if isinstance(v, dict) and all(kk in v for kk in ("label", "type", "default", "value")):
            val = v["value"]
            if v["type"] == "number":
                try: val = int(val)
                except Exception: val = 0
            out[k] = val
        elif isinstance(v, dict): out[k] = flatten(v)
        else: out[k] = v
    return out

def admin_post(changes):
    """changes 形如 {"general.display_hidden": True};按点分路径嵌套写入"""
    st, adm = call("GET", BASE+"/admin/api/config")
    b = flatten(adm["result"])
    b["connections"] = [{"type": "sftp", "label": "SFTP 服务器"},
                        {"type": "webdav", "label": "WebDAV 服务器"}]   # 关键!见 8.2
    for dotted, v in changes.items():
        parts = dotted.split("."); node = b
        for p in parts[:-1]: node = node[p]
        node[parts[-1]] = v
    return call("POST", BASE+"/admin/api/config", b)

三个实测要点:

  1. number 类型必须转 int:Schema 里 number 的 value 可能是字符串或 0,直接提交字符串会被拒绝或落成意外值,压平时统一 int()。
  2. 修改要基于"最新一次 GET"的压平结果:先 GET 再改再 POST,避免用过期快照覆盖别人的修改。
  3. connections 必须手工带上——这是本次实测踩到的最大的坑,单独成节。

8.2 事故回放:全量替换把存储后端连接清空

POST /admin/api/config 的语义是全量替换而非增量合并:请求体里没有的键,按"还原默认"处理。而压平 Schema 得到的 body 里根本没有 connections 键(4.1 节的 Schema 树不回显连接列表)。实测中第一次提交修改时没有手工补上 connections,后果如下:

## 修改提交成功(但 body 里没有 connections)
2026/10/22 04:52:42 HTTP 200 POST 0.7ms /admin/api/config

## 立即查看前台配置:connections 变成了空数组
$ curl -s http://127.0.0.1:8334/api/config | python3 -m json.tool | grep -A1 connections
        "connections": [],

## 已登录的 SFTP 会话随即失效,文件列表接口返回 403
2026/10/22 04:52:42 SYST DEBUG middleware::session::start action=extractBackend error="Not Allowed"
2026/10/22 04:52:42 HTTP 403 GET 0.0ms /api/files/ls?path=/srv/share/

修复方式:重新 POST /admin/api/config,body 中补上 connections 数组(8.1 脚本第 27-28 行),提交后连接立即恢复、原 SFTP 会话继续可用。

教训:脚本化修改配置时,POST body 必须包含 connections、secret_key、auth.admin 这三类"不在 Schema 表单里回显、但参与全量替换"的数据。8.1 的 admin_post() 已把它们固定在流程里。

8.3 log.level:INFO → DEBUG 的两个可见变化

把 log.level 从默认 INFO 改为 DEBUG 并立即还原(期间发起 4 个请求):

>>> admin_post({"log.level": "DEBUG"})
(200, {'status': 'ok'})
>>> admin_post({"log.level": None})
(200, {'status': 'ok'})

观察 access.log(tail -n 8,时间均为 04:59:46):前 4 行是 DEBUG 生效期间的请求,每行末尾都追加了一段 32 位随机串 trace=<...>;后 4 行恢复 INFO 后不再携带:

2026/10/22 04:59:46 HTTP 200 GET 0.7ms /admin/api/config
2026/10/22 04:59:46 HTTP 200 POST 1.1ms /admin/api/config trace=58sl838M4OgNd7sJMraOqAriiVPvu2wa
2026/10/22 04:59:46 HTTP 200 GET 0.1ms /healthz
2026/10/22 04:59:46 HTTP 200 GET 0.1ms /api/config trace=krSKhWAgKa3XvhCjPRvB1dAmLsfxGKk1
2026/10/22 04:59:46 HTTP 200 GET 0.3ms /admin/api/config trace=Zwq6V8BHjZIltBO1Y32AczsvB2eyWkrN
2026/10/22 04:59:46 HTTP 200 POST 0.7ms /admin/api/config
2026/10/22 04:59:46 HTTP 200 GET 0.1ms /healthz
2026/10/22 04:59:46 HTTP 200 GET 0.1ms /api/config

第二个变化:DEBUG 级别下,内部中间件的异常细节会写入日志(INFO 级别不出现)。8.2 事故窗口里的这行就是 DEBUG 级别的产物:

2026/10/22 04:52:42 SYST DEBUG middleware::session::start action=extractBackend error="Not Allowed"

运维结论:排障时临时把 log.level 切到 DEBUG,可以用 trace 串把一次请求的全部日志行串起来(trace 同时出现在请求行与异常行);排查完记得还原(还原写法是把 value 提交回 null,即跟随默认 INFO)。

8.4 features.share.enable:只关前端入口,API 不设防

先建立共享(用 SFTP 会话),再禁用共享功能,对比行为。创建共享的 API 是 PUT /api/share/<自定义ID>(注意不是 /api/files/share,后者实测返回 404——路径探测记录见 8.5):

## 1. 创建共享(viewer 角色:只读)
$ curl -s -X PUT -H 'X-Requested-With: XmlHttpRequest' -H 'Content-Type: application/json' \
    -H "Authorization: Bearer <SFTP会话令牌>" \
    -d '{"path": "/srv/share/docs/", "can_read": true, "can_write": false, "can_upload": false}' \
    "http://127.0.0.1:8334/api/share/c1tst001"
{"status": "ok"}

## 2. 列出该路径的共享
$ curl -s -H "Authorization: Bearer <SFTP会话令牌>" \
    "http://127.0.0.1:8334/api/share?path=/srv/share/docs/"
{"status": "ok", "results": [{"id": "c1tst001", "path": "/", "can_read": true, "can_write": false, "can_upload": false}]}

## 3. 共享链接页面可访问(HTTP 200)
$ curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8334/s/c1tst001
200

现在禁用共享并干净地重测(专门核实了落盘值确为 false,排除提交残留干扰):

>>> admin_post({"features.share.enable": False})
(200, {'status': 'ok'})
$ grep -A4 '"share"' /opt/filestash/data/state/config/config.json
"share": {"enable": false, "default_access": null, "redirect": null}
$ curl -s http://127.0.0.1:8334/api/config | python3 -m json.tool | grep enable_share
        "enable_share": false,

前端标记 enable_share 已下发为 false(Web 界面的"分享"按钮随之隐藏)。但 API 层的行为完全不变:

## 禁用状态下创建新共享:仍然成功
$ curl -s -X PUT ... -d '{"path": "/srv/share/docs/", "can_read": true, ...}' \
    "http://127.0.0.1:8334/api/share/c1dis001"
{"status": "ok"}

## 禁用状态下访问已有共享链接:仍然 200
$ curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8334/s/c1tst001
200

实测结论(v0.6.20261022):features.share.enable=false 只是"前端开关"——隐藏 Web 界面的分享入口;已建共享的创建/访问 API 照常工作。若目标是"服务端彻底禁用共享",仅靠这个参数不够,需要叠加反向代理规则拦截 /api/share 与 /s/ 路径,或用授权插件收敛权限。测试后已将共享删除、参数还原:

$ curl -s -X DELETE -H "Authorization: Bearer <SFTP会话令牌>" http://127.0.0.1:8334/api/share/c1dis001
{"status": "ok"}
>>> admin_post({"features.share.enable": True})
(200, {'status': 'ok'})
$ curl -s http://127.0.0.1:8334/api/config | python3 -m json.tool | grep enable_share
        "enable_share": true,

8.5 display_hidden 与 filepage_default_view:改了立即生效

general.display_hidden:置 true 后,/api/config 的 display_hidden 立即翻转,且落盘文件同步写入(无需重启):

>>> admin_post({"general.display_hidden": True})
(200, {'status': 'ok'})
$ grep -n 'display_hidden' /opt/filestash/data/state/config/config.json
10:        "display_hidden": true,
$ curl -s http://127.0.0.1:8334/api/config | python3 -m json.tool | grep display_hidden
        "display_hidden": true,

一个需要澄清的行为细节:SFTP 后端的 ls 接口无论该参数真假,都会返回点开头文件(实测 display_hidden=false 时 .c1hidden.txt 也在 results 里):

$ curl -s -H "Authorization: Bearer <SFTP会话令牌>" \
    "http://127.0.0.1:8334/api/files/ls?path=/srv/share/" | python3 -m json.tool
{
    "status": "ok",
    "results": [
        {"name": "sample.bin", ...},
        {"name": ".c1hidden.txt", ...},
        {"name": "docs", ...},
        {"name": "b1-verify", ...}
    ]
}

即 display_hidden 只控制 Web 界面是否默认展示隐藏文件,过滤发生在前端渲染层,不是服务端接口行为。

general.filepage_default_view:从默认 grid 改为 list 后,/api/config 的 default_view 字段立即从 grid 变为 list(Web 界面文件页随之默认用列表布局展示):

>>> admin_post({"general.filepage_default_view": "list"})
(200, {'status': 'ok'})
$ curl -s http://127.0.0.1:8334/api/config | python3 -m json.tool | grep default_view
        "default_view": "list",

以上两个参数实测后均已还原为 null(见第九节),落盘文件回到 null 基线。

九、配置还原与落盘基线

把 8.3/8.4/8.5 修改过的四个参数(display_hidden、filepage_default_view、log.level、share.enable)全部提交回 null,即还原为"跟随默认值":

>>> admin_post({"general.display_hidden": None, "general.filepage_default_view": None,
...             "log.level": None, "features.share.enable": True})
(200, {'status': 'ok'})
$ curl -s http://127.0.0.1:8334/api/config | python3 -m json.tool | grep -E '"(display_hidden|default_view|enable_share)"'
        "default_view": "grid",
        "display_hidden": false,
        "enable_share": true,

还原后的落盘基线(config.json 全文,节略 mime 等无关段):所有未显式设置的键都是 null,密钥、口令哈希、connections 保留原值——这就是"出厂默认 + 必要初始化"的标准形态:

$ cat /opt/filestash/data/state/config/config.json
{
    "general": {
        "name": null,
        "port": 0,
        "host": null,
        "secret_key": "iUIfIl67pLEWX98S",
        "force_ssl": null,
        "editor": null,
        "logout": null,
        "display_hidden": null,
        "refresh_after_upload": null,
        "open_mode": null,
        "upload_button": null,
        "upload_pool_size": 0,
        "upload_chunk_size": 0,
        "buffer_size": null,
        "filepage_default_view": null,
        "filepage_default_sort": null,
        "cookie_timeout": 0,
        "extended_session": null,
        "custom_css": null,
        "license": null
    },
    "features": {
        "api": {"enable": null},
        "share": {"enable": null, "default_access": null, "redirect": null},
        ...(protection/collaborative/workflow/office/mcp/site/search/video/server,同构,均为 null/0)...
    },
    "log": {"enable": null, "level": null, "telemetry": null},
    "email": {"server": null, "port": 0, "username": null, "password": null, "from": null},
    "auth": {"admin": "$2b$10$hJXK4pqyc9DGDizo6Rp7/Ojc8Tqds5KDSCgGqL/zUDsBE9cUxdCpK"},
    "middleware": {"identity_provider": {"type": null, "params": null},
                   "attribute_mapping": {"related_backend": null, "params": null}},
    "constant": {"user": "filestash", "license": "agpl"},
    "connections": [
        {"type": "sftp", "label": "SFTP 服务器"},
        {"type": "webdav", "label": "WebDAV 服务器"}
    ]
}

基线管理的三点建议:

  1. 改前备份:cp config.json config.json.bak.$(date +%s)。虽然 API 能逐项还原 null,但一份改动前的落盘快照永远是最后的退路。
  2. secret_key 不可轻易轮换:如 4.4 所述会使全部会话与共享链接失效,只能安排在维护窗口。
  3. 还原 = 提交 null,而不是"删键":请求体里删掉某键与显式提交 null 等价(全量替换语义),但显式 null 语义更清晰,不易误伤其他键。

十、附:共享链接 API 速查

8.4 实测中顺带确认了共享功能的完整 API 面(详细的行为与参数说明见本专栏《Filestash 运维管理与 API 实操详解》):

操作方法与路径说明
创建/更新共享PUT /api/share/<ID>body:{"path": "...", "can_read": true, "can_write": false, "can_upload": false},可选 url/password/expire/users;ID 为自定义短串(Web 端用 7 位随机串)
列出路径已有共享GET /api/share?path=<路径>返回 results 数组,元素含 id/path/can_read/can_write/can_upload
删除共享DELETE /api/share/<ID>返回 {"status": "ok"}
访问共享GET /s/<ID>共享落地页(HTTP 200,口令/邮件验证时先走 proof 流程)
共享口令验证GET /api/share/<ID>/proof带 - 参数校验口令/邮件验证码(前端分享页调用)

注:老版本文档/社区资料中的 POST /api/files/share 在本版本实测返回 404 Not Found;PUT/DELETE 该路径则返回 405 Method Not Allowed。以本文实测的 /api/share 系列为准。

至此 Filestash 配置体系的四个维度——端点、Schema、修改语义、落盘形态——都有了实测答案。下一篇《Filestash 运维管理与 API 实操详解》将以运维视角收尾:日志逐行解读、健康检查全形态、共享与打包下载等核心 API 的实操全集。