Slink 1.14.5 API 使用详解

Slink v1.14.5 对外 API 实测详解:JWT 与 API Key 双认证、登录续期、Key 管理增删查、外部上传与错误码矩阵、ShareX 配置生成、收藏夹与分享创建、配置读取接口,全部请求响应实测回抄。

SlinkSlink文件传输文件分享自托管2026/10/31

Slink 1.14.5 API 使用详解

目标:讲清 Slink v1.14.5 对外 API 的认证体系与全部可编程调用路径——登录换 JWT、API Key 管理、外部程序化上传、ShareX 配置生成、收藏夹与分享创建、平台配置读取,全部请求与响应均为本地实例实测回抄(环境见第一章)。部署与配置背景见 《Slink 1.14.5 Docker Compose 部署指南》。

一、环境信息

本文接口实测在以下环境完成:

  • 操作系统:Ubuntu 24.04.5 LTS(x86_64),客户端 curl 8.x
  • Slink 版本:v1.14.5(官方镜像 anirdev/slink:v1.14.5,2026-10-31 发布)
  • 实例地址:http://localhost:8130(应用内 3000)
  • 测试账号:admin@slink.local / q1w2e3r4(首个管理员,自动创建)
  • API 调试目录:/opt/slink,测试图片 test-blue.png(64×64 PNG,177 字节)

二、前置条件

  1. 端点:Slink 对外 API 与 Web 界面同域同端口(本文 http://localhost:8130),路径前缀 /api;无需单独开启 API 服务;
  2. 认证方式二选一:
    • JWT 会话令牌:POST /api/auth/login 换取 access_token,请求头 Authorization: Bearer <access_token>,用于管理类接口(Key 管理、上传、收藏夹、分享、配置读取),有效期 1 小时;
    • 个人 API Key:形如 sk_<64位十六进制>,在 Web 界面个人资料页或 POST /api/user/api-keys 创建,请求头同为 Authorization: Bearer sk_...,用于外部集成上传(ShareX、脚本);
  3. 同源要求:所有写请求(POST/PATCH/DELETE)必须携带与实例 ORIGIN 一致的 Origin 请求头,否则被同源校验拒绝(403,详见第七章 Q1)——浏览器自动携带,脚本与 curl 必须手动加;
  4. 客户端工具:curl 即可(本文全部用 curl 实测)。

三、API 总览

  • 协议:HTTP + JSON;上传类接口为 multipart/form-data,其余为 application/json;
  • 认证:统一 Authorization: Bearer 头,令牌分 JWT(1 小时)与 API Key(可设过期)两种;
  • 错误结构:统一 JSON 对象,形如 {"error":{"title":"<异常类>","message":"<概述>","violations":[{"property":"<字段>","message":"<原因>"}]}}——violations 数组是定位参数错误的依据;
  • 列表结构:列表接口统一返回 {"meta":{"size":<页大小>,"total":<总数>},"data":[...]};
  • 限流:官方文档未声明限流约定,本实例未配置。

实测 API 域与端点总表(全部经 curl 实测,状态码为实测值):

API 域方法与端点认证实测状态码
认证POST /api/auth/login无200 / 400
认证POST /api/auth/refreshrefresh_token200
用户GET /api/userJWT200
API KeyPOST /api/user/api-keysJWT200
API KeyGET /api/user/api-keysJWT200
API KeyDELETE /api/user/api-keys/{keyId}JWT204
外部上传POST /api/external/uploadAPI Key201 / 401 / 403
ShareXPOST /api/user/sharex-configJWT200 / 422
会话上传POST /api/uploadJWT201
图片管理PATCH /api/image/{id}JWT204
图片浏览GET /api/images可选 JWT200
收藏夹POST /api/collectionJWT201
收藏夹GET /api/collectionsJWT200
分享POST /api/image/{id}/shareJWT201
分享POST /api/collection/{id}/shareJWT201
分享GET /api/sharesJWT200
配置GET /api/settingsJWT(管理员)200 / 401

四、认证与首次调用

4.1 登录换取 JWT

请求字段名是 username(值可传邮箱),不是 email——传错字段名会得到 422 校验错并提示 username 为空。实测:

curl -s -X POST http://localhost:8130/api/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"username":"admin@slink.local","password":"q1w2e3r4"}' \
  -w '\nHTTP=%{http_code}\n'
{"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE3OTM0MTYzMzksImV4cCI6MTc5MzQxOTkzOSwicm9sZXMiOlsiUk9MRV9VU0VSIiwiUk9MRV9BRE1JTiJdLCJ1dWlkIjoiZTkzYjdkYjEtMDY5NS00OGFmLTkwZmEtMmE2NjQ5YjI4YzQwIn0.<…输出节选(实测首尾,JWT 全长约 876 字符)…>","refresh_token":"c7d0694a-2311-4530-8d02-3fd8240e52e4.1796008339"}
HTTP=200

access_token 为 RS256 签名的 JWT,解码载荷含 roles 与用户 uuid,exp 距 iat 恰为 3600 秒(1 小时);refresh_token 用于无感续期。口令错误实测返回 400(注意不是 401):

{"error":{"title":"Slink.User.Domain.Exception.InvalidCredentialsException","message":"Specification Error","violations":[{"property":"credentials","message":"Invalid credentials. Please check your username\/email and password and try again."}]}} [400]

4.2 首个受保护调用

用 JWT 调用用户信息接口验证令牌可用:

TOK='<上一步返回的access_token>'
curl -s http://localhost:8130/api/user -H "Authorization: Bearer $TOK" -w '\nHTTP=%{http_code}\n'
{"data":{"id":"e93b7db1-0695-48af-90fa-2a6649b28c40","displayName":"Admin"}} [200]

4.3 令牌续期

curl -s -X POST http://localhost:8130/api/auth/refresh \
  -H 'Content-Type: application/json' \
  -d '{"refresh_token":"c7d0694a-2311-4530-8d02-3fd8240e52e4.1796008339"}' \
  -w '\nHTTP=%{http_code}\n'
{"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE3OTM0MTY0OTIsImV4cCI6MTc5MzQyMDA5Miwicm9sZXMiOlsiUk9MRV9VU0VSIiwiUk9MRV9BRE1JTiJdLCJ1dWlkIjoiZTkzYjdkYjEtMDY5NS00OGFmLTkwZmEtMmE2NjQ5YjI4YzQwIn0.<…输出节选(实测首尾)…>
HTTP=200

返回新的 access_token(iat/exp 已前移),实现 1 小时令牌的无感续期。

五、各 API 域详解

5.1 API Key 管理

API Key 是外部集成(ShareX、脚本、CI)的长期凭证,管理三接口实测如下。

创建(响应体只此一次返回明文 Key,之后无法再查看):

curl -s -X POST http://localhost:8130/api/user/api-keys \
  -H "Authorization: Bearer $TOK" -H "Origin: http://localhost:8130" \
  -H 'Content-Type: application/json' \
  -d '{"name":"docs-test"}' -w '\nHTTP=%{http_code}\n'
{"key":"sk_1684d3d29b75e244db13a76a312318a650e31b6532b46b40fa0bd60561a5992e","keyId":"5e0aba44-ec4c-4ab0-992a-c73a5526722a","name":"docs-test"}
HTTP=200

列表(返回元数据而非明文 Key,lastUsedAt 可用于审计,isExpired 标记过期):

curl -s http://localhost:8130/api/user/api-keys -H "Authorization: Bearer $TOK" -w ' [%{http_code}]\n'
{"data":[{"id":"5e0aba44-ec4c-4ab0-992a-c73a5526722a","name":"docs-test","createdAt":"2026-10-31T11:12:52.000000+08:00","expiresAt":null,"lastUsedAt":"2026-10-31T11:13:13.000000+08:00","isExpired":false}]} [200]

撤销(实测新建再删除):

K2=$(curl -s -X POST http://localhost:8130/api/user/api-keys \
  -H "Authorization: Bearer $TOK" -H "Origin: http://localhost:8130" \
  -H 'Content-Type: application/json' -d '{"name":"revoke-me"}')
KID=$(echo "$K2" | python3 -c 'import sys,json;print(json.load(sys.stdin)["keyId"])')
curl -s -X DELETE "http://localhost:8130/api/user/api-keys/$KID" \
  -H "Authorization: Bearer $TOK" -H "Origin: http://localhost:8130" -w ' [%{http_code}]\n'
 [204]

删除后复查列表,revoke-me 已消失、仅剩 docs-test,撤销即时生效。泄漏应急处理即调此接口(或 Web 界面吊销)。

5.2 外部上传(程序化上传主入口)

官方对外承诺的公开上传端点,API Key 认证,multipart 字段名固定为 image:

KEY='sk_1684d3d29b75e244db13a76a312318a650e31b6532b46b40fa0bd60561a5992e'
curl -s -X POST http://localhost:8130/api/external/upload \
  -H "Authorization: Bearer $KEY" \
  -H "Origin: http://localhost:8130" \
  -F "image=@test-blue.png" -w '\nHTTP=%{http_code}\n'
{"url":"i\/KOSzunpg","thumbnailUrl":"i\/Xtpi7XTe","id":"ad765b7f-a8b3-4e3e-b423-31ee86e84578"}
HTTP=201

响应字段:url 为图片短码路径(拼在站点域名后即可访问观看页,如 http://localhost:8130/i/KOSzunpg)、thumbnailUrl 为缩略图路径、id 为图片 UUID(也是存储文件名,对应磁盘 images/ad765b7f-a8b3-4e3e-b423-31ee86e84578.png)。图片默认为私有可见性。

两个必测错误分支(实测回显):

curl -s -X POST http://localhost:8130/api/external/upload \
  -H "Authorization: Bearer $KEY" -F "image=@test-blue.png" -w ' [%{http_code}]\n'
Cross-site POST form submissions are forbidden
HTTP=403

缺 Origin 头即 403(第七章 Q1 详述)。

curl -s -X POST http://localhost:8130/api/external/upload \
  -H "Authorization: Bearer sk_invalid" -H "Origin: http://localhost:8130" \
  -F "image=@test-blue.png" -w ' [%{http_code}]\n'
{"error":{"message":"Authentication failed","code":"INVALID_API_KEY"}} [401]

无效 Key 返回 401,错误码 INVALID_API_KEY——脚本可用它区分「凭证失效」与「网络问题」。

5.3 ShareX 集成配置生成

为截图工具 ShareX 生成开箱即用的上传配置(.sxcu)。注意:官方文档标注为 GET,实测该端点只接受 POST,且参数必须放 JSON 请求体(放查询串会 422)。先看参数位置错误的实测:

curl -s -X POST "http://localhost:8130/api/user/sharex-config?baseUrl=http://localhost:8130&apiKey=$KEY" \
  -H "Authorization: Bearer $TOK" -w '\n[%{http_code}]\n'
{"error":{"title":"Symfony.Component.HttpKernel.Exception.HttpException","message":"Validation Error","violations":[{"property":"apiKey","message":"API key is required. Create one first from your profile page."}]}}
[422]

正确姿势(JSON 请求体 + 同源头):

curl -s -X POST http://localhost:8130/api/user/sharex-config \
  -H "Authorization: Bearer $TOK" -H "Origin: http://localhost:8130" \
  -H 'Content-Type: application/json' \
  -d "{\"baseUrl\":\"http://localhost:8130\",\"apiKey\":\"$KEY\"}" -w '\n[%{http_code}]\n'
{"Version":"14.1.0","Name":"Slink Image Upload","DestinationType":"ImageUploader","RequestMethod":"POST","RequestURL":"http:\/\/localhost:8130\/api\/external\/upload","Headers":{"Authorization":"Bearer sk_1684d3d29b75e244db13a76a312318a650e31b6532b46b40fa0bd60561a5992e","Origin":"http:\/\/localhost:8130"},"Body":"MultipartFormData","FileFormName":"image","URL":"http:\/\/localhost:8130\/{json:url}","ThumbnailURL":"http:\/\/localhost:8130\/{json:thumbnailUrl}"}
[200]

生成物把 Authorization、Origin、multipart 字段名、响应取值({json:url})全部编排好——其中自动包含 Origin 头,这正是 ShareX 客户端不会踩 403 坑的原因;自研脚本对照此 JSON 逐项补齐请求要素即可。

5.4 会话上传与图片管理

登录会话下的上传端点是 POST /api/upload,multipart 需同时带 id(客户端生成的 UUID)与 image 两个字段——只传图片会得到 500(UploadImageCommand::$id must not be accessed before initialization),这是实测踩到的真实报错:

NID=$(python3 -c 'import uuid;print(uuid.uuid4())')
curl -s -X POST http://localhost:8130/api/upload \
  -H "Authorization: Bearer $TOK" -H "Origin: http://localhost:8130" \
  -F "id=$NID" -F "image=@test-orange.png" -w '\nHTTP=%{http_code}\n'
{"id":"1bddea84-36dd-4856-a288-35812a456b44"}
HTTP=201

图片管理走单数路径 /api/image/{id},实测支持 PATCH 与 DELETE(对它发 GET 会得到 405 并在响应头披露 Allow: DELETE, PATCH)。改可见性:

curl -s -X PATCH http://localhost:8130/api/image/1bddea84-36dd-4856-a288-35812a456b44 \
  -H "Authorization: Bearer $TOK" -H "Origin: http://localhost:8130" \
  -H 'Content-Type: application/json' \
  -d '{"visibility":"public"}' -w ' [%{http_code}]\n'
 [204]

浏览公开图片池用 GET /api/images(Explore 页数据源,未登录也可访问):

curl -s "http://localhost:8130/api/images" -H "Authorization: Bearer $TOK" -w ' [%{http_code}]\n'
{"meta":{"size":10,"total":0},"data":[]} [200]

注意语义:/api/images 只列公开图片,新建上传默认私有,因此刚上传后此处仍为 total: 0——这是正常行为,不是丢数据;个人上传历史以 Web 界面 History 页为准。

5.5 收藏夹与分享

收藏夹走单数路径 /api/collection(GET 列表则是复数 /api/collections),创建实测:

curl -s -X POST http://localhost:8130/api/collection \
  -H "Authorization: Bearer $TOK" -H "Origin: http://localhost:8130" \
  -H 'Content-Type: application/json' \
  -d '{"name":"docs-col"}' -w '\nHTTP=%{http_code}\n'
{"data":{"id":"ac3145a9-24ce-427d-89a1-1fbbeed8b5f2","name":"docs-col","description":"","createdAt":{"formattedDate":"2026-10-31 11:17:30","timestamp":1793416650},"updatedAt":null,"userId":"e93b7db1-0695-48af-90fa-2a6649b28c40","itemCount":0}}
HTTP=201

图片与收藏夹均可一键生成分享(空 JSON 体即可,密码/过期等属性可在 Web 界面补配):

curl -s -X POST http://localhost:8130/api/image/1bddea84-36dd-4856-a288-35812a456b44/share \
  -H "Authorization: Bearer $TOK" -H "Origin: http://localhost:8130" \
  -H 'Content-Type: application/json' -d '{}' -w '\nHTTP=%{http_code}\n'
{"shareId":"2d16ad3f-66b0-46e4-a58c-f0334830f909","shareUrl":"http:\/\/localhost:8130\/i\/BjoZB03T","type":"image","created":true,"expiresAt":null,"requiresPassword":false}
HTTP=201
curl -s -X POST http://localhost:8130/api/collection/ac3145a9-24ce-427d-89a1-1fbbeed8b5f2/share \
  -H "Authorization: Bearer $TOK" -H "Origin: http://localhost:8130" \
  -H 'Content-Type: application/json' -d '{}' -w '\nHTTP=%{http_code}\n'
{"shareId":"86ab20d5-c152-44f3-b7a8-6906dee01946","shareUrl":"http:\/\/localhost:8130\/c\/dmlmvYuH","type":"collection","created":true,"expiresAt":null,"requiresPassword":false}
HTTP=201

图片分享落在 /i/<短码>、收藏夹分享落在 /c/<短码>;shareUrl 为完整可分发链接。分享清单与收藏夹清单分别用 GET /api/shares、GET /api/collections 读取(实测返回 meta/data 结构,初始为空列表)。

5.6 平台配置读取

管理员可程序化读取全量运行时配置(内容样例与字段解读见 《Slink 1.14.5 配置参数详解》第三章):

curl -s http://localhost:8130/api/settings -H "Authorization: Bearer $TOK" -o /dev/null -w '%{http_code}\n'
curl -s http://localhost:8130/api/settings -o /dev/null -w '%{http_code}\n'
200
401

带管理员 JWT 返回 200 与完整配置 JSON;不带凭证返回 401。该接口适合做部署后的配置漂移巡检。

六、分页/过滤/排序通用约定

列表接口统一返回 {"meta":{"size":<每页条数>,"total":<总数>},"data":[...]}:size 是页大小而非本页实际条数,data 才是数据,判断「有没有更多」用 len(data) == size。页码参数实测支持 ?page=1 形式;非法排序参数服务端会拒绝而非静默忽略(v1.13.1 Release Notes 明确此行为)。外部上传响应不含分页信息(单对象返回)。

七、常见问题

Q1:写接口一律 403「Cross-site POST form submissions are forbidden」? 缺 Origin 头或与实例 ORIGIN 不一致。Slink 对所有写请求做同源校验:curl 必须显式 -H "Origin: http://localhost:8130"(值与部署时 ORIGIN 逐字一致);Nginx 反代场景确认没有剥离 Origin 头。实测缺失必现 403,补齐即 201。

Q2:登录返回 400 还是 401? 口令错误返回 400(InvalidCredentialsException,见 4.1 回显);不带凭证访问受保护接口才是 401(如无 JWT 读 /api/settings);API Key 无效在外部上传返回 401 + code: INVALID_API_KEY。三类错误码分开处理。

Q3:422 Validation Error 怎么定位? 读 violations[]:property 指明出错字段。实测两例——登录用 email 字段名 → property: username;sharex-config 把参数放查询串 → property: apiKey。按字段名改请求即消错。

Q4:某个路径返回 405 Method Not Allowed? 这是合法探测手段:响应头 Allow: 列出该路径全部支持方法(实测 GET /api/user/profile → Allow: PATCH、GET /api/image/{id} → Allow: DELETE, PATCH),按 Allow 改方法即可,不必翻文档。

Q5:API Key 创建后忘了保存? 明文 Key 只在创建响应(或 Web 创建弹窗)出现一次,之后列表接口只回元数据。丢了就 DELETE 旧 Key 重建;lastUsedAt 字段可辅助确认旧 Key 是否已被他人使用过。

Q6:上传成功但 /api/images 里看不到? 该端点只列公开图片池(Explore 数据源),新建上传默认私有。要公开需 PATCH /api/image/{id} 改可见性(5.4 实测),或在 Web 界面操作。

八、参考链接

Slink 1.14.5 API 使用详解 - Quzuo