PufferPanel 3.0.9 API 使用详解

PufferPanel 3.0.9 REST API 实战:认证与会话、端点全景、用户/服务器/模板/节点四域实测、请求响应约定与踩坑 FAQ,全示例实测于 Ubuntu 22.04。

PufferPanel自托管2026/10/24

PufferPanel 3.0.9 API 使用详解

目标:面向需要用脚本/程序化管理面板的运维与集成开发者,讲清 PufferPanel 3.0.9 REST API 的认证方式、端点全景、请求/响应约定与高频调用实测。文中每个 curl 命令、响应体、状态码均来自 Ubuntu 22.04.5 + PufferPanel 3.0.9 实测环境(实测日期 2026-10-24),示例统一使用管理员账号 admin(密码按安全规范以 ${PASSWORD} 占位展示,实测时为真实值)。

一、环境信息

  • 操作系统:Ubuntu 22.04.5 LTS(x86_64)
  • 面板版本:PufferPanel 3.0.9(pufferpanel version → PufferPanel 3.0.9 (94ded99f2d691762c09c6df076a2f76c475b481e))
  • API 基地址:http://127.0.0.1:8080(生产环境替换为 https://<域名>,建议反代终结 TLS)
  • 客户端:curl 7.81(Ubuntu 22.04 自带),认证方式为会话 Cookie
  • 测试对象:真实创建的管理员用户、测试用户 gameop、以 echotest 模板创建的服务器实例 echo1(占位进程,见《安装指南》篇)

二、前置条件

  1. 运行中的面板:3.0.9 服务 active(systemctl is-active pufferpanel → active),Web 端口可达(默认 8080)。
  2. 管理员账号:pufferpanel user add --admin 创建;管理员持有全部权限域,可调用全部 API。
  3. 工具:curl(或任意 HTTP 客户端);WebSocket 场景(控制台实时流)需支持 WS 的客户端,本文聚焦 REST。
  4. 认证认知:面板登录走 POST /auth/login(不是 /api/auth/login,404 踩坑见 §七),成功后以 puffer_auth 会话 Cookie 访问 /api/*。除会话外,面板还支持 OAuth2 客户端、Passkey、免密登录与 TOTP 双因素(来源:Web 前端路由实测枚举 /api/self/oauth2、/api/self/otp、/api/self/passkey、/api/self/passwordless/*)。

三、API 总览

面板 REST API 与 Web UI 共用 8080 端口,API 前缀 /api。域全景如下(端点清单经实测与前端资源核对,查询日期 2026-10-24):

域前缀说明实测状态
认证POST /auth/login、/auth/logout登录换会话 Cookie / 登出✓ 200 / 200
当前用户GET /api/self当前登录者信息与权限域✓ 200
用户管理/api/users、/api/users/:id用户 CRUD、权限域管理✓ 创建/列表 200
服务器/api/servers、/api/servers/:id服务器 CRUD、电源、控制台、文件、备份、任务✓ 全链路 200/202/204
节点/api/nodes、/api/nodes/:id面板所辖节点(含本机 LocalNode)与远程节点注册✓ 200
模板/api/templates/:repoId/:name模板仓库(0=Local 本地,1=community 社区源)与模板读写✓ 保存 204/读取 200
面板配置GET /api/config面板可见配置(品牌/注册开关/主题等)✓ 200
系统设置/api/settings(含 /settings/test/email)系统级设置与邮件连通性测试端点实测枚举
用户偏好/api/userSettings当前用户界面偏好端点实测枚举

另有面板↔守护进程内部调用路径 POST /daemon/server/<id>/start|stop|kill|install(实测日志捕获),供理解架构,外部集成请走 /api/* 公开接口。

3.1 权限域(Scopes)概念

API 鉴权围绕权限域展开:管理员账号登录响应直接携带 "scopes":["admin"];普通用户按授权范围持有 server.view、server.console.send、server.files.edit、server.sftp 等约 30 个作用域(创建用户实测请求体即传入 "scopes":["server.view"])。用低权限会话调用未授权 API 会收到权限错误而非静默放行,集成侧应按最小权限原则为集成账号只授予所需作用域。

四、认证与首次调用

4.1 登录并保存会话

curl -s -X POST http://127.0.0.1:8080/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"${PASSWORD}"}' \
  -c /root/ck.txt -i | head -8

实测回显:

HTTP/1.1 200 OK
Content-Type: application/json
Set-Cookie: puffer_auth=<uuid>; Path=/; Expires=...; Max-Age=3600; HttpOnly
Date: Fri, 24 Oct 2026 03:21:14 GMT
Content-Length: 45

{"scopes":["admin"],"needsSecondFactor":false}

要点:会话 Cookie puffer_auth 为 HttpOnly,有效期 3600 秒(1 小时),过期需重新登录;needsSecondFactor 在账号启用 TOTP 后返回 true,需要第二因素(本例账号未启用为 false)。后续所有请求带 -b /root/ck.txt 复用该会话。

4.2 首个业务调用:当前用户

curl -s -b /root/ck.txt http://127.0.0.1:8080/api/self -w "\n[HTTP %{http_code}]\n"

实测回显(节选):

{"id":1,"username":"admin","email":"admin@example.com","scopes":{"global":["admin"],"servers":null},"servers":null}
[HTTP 200]

scopes.global 即权限域,管理员为 ["admin"]。未认证直接调用返回 401(权限拒绝,JSON 错误体,见 §六)。

五、API 域详解(实测)

5.1 用户管理:/api/users

创建用户(用户名/邮箱/密码/权限域,实测创建 gameop):

curl -s -b /root/ck.txt -X POST http://127.0.0.1:8080/api/users \
  -H "Content-Type: application/json" \
  -d '{"username":"gameop","email":"gameop@example.com","password":"${PASSWORD}","scopes":["server.view"]}' \
  -w "\n[HTTP %{http_code}]\n"

实测回显:

{"data":null,"error":null,"scopeMap":null,"user":{"id":2,"username":"gameop","email":"gameop@example.com","scopes":{"global":["server.view"],"servers":null},"servers":null}}
[HTTP 200]

用户列表:

curl -s -b /root/ck.txt http://127.0.0.1:8080/api/users

实测回显(节选,paging 为分页元数据,data 为用户数组):

{"paging":{"offset":0,"size":100,"total":2,"sortField":"","sortOrder":"asc"},"error":null,"data":[{...admin...},{...gameop...}],"scopeMap":null,"user":null}

💡 技术拓展:响应包络统一为 {paging, error, data, scopeMap, user} 五段。scopeMap 用于带出权限域字典,user 在涉及单用户资源时携带主体。分页经查询参数控制偏移与条数(paging 段回显当前值),列表接口均可按此遍历。

5.2 模板管理:/api/templates

模板分仓库存储:/api/templates/0 为 Local(本地模板,可在面板内自建),/api/templates/1 为内置 community 仓库源(GitHub pufferpanel/templates v3 分支,实测列表返回该源元数据)。保存本地模板用 PUT(POST 是 404):

curl -s -b /root/ck.txt -X PUT http://127.0.0.1:8080/api/templates/0/echotest \
  -H "Content-Type: application/json" \
  -d '{"type":"echo","display":"Echo Test","install":[],"run":{"stop":"","pre":[],"post":[],"command":"sleep 600","environmentVars":{}},"environment":{"type":"standard"},"data":{}}' \
  -w "\n[HTTP %{http_code}]\n"

实测回显:

[HTTP 204]

204 无响应体即保存成功。读取确认(实测回显为存储后的全量结构,系统补齐了 autostart/autorestart/stats/query/keepAlive 等默认字段):

curl -s -b /root/ck.txt http://127.0.0.1:8080/api/templates/0/echotest
{"name":"echotest","display":"Echo Test","install":[],"uninstall":[],"run":{"stop":"","pre":[],"post":[],"command":"sleep 600","environmentVars":{},"stdin":{"type":"stdin"},"workingDirectory":""},"environment":{"type":"standard"},"data":{},"autostart":false,"autorecover":false,"autorestart":false,"requirements":{},"stats":{"cpuUsage":0,"memoryUsage":0},"query":{"port":0,"ip":""},"keepAlive":false}

Schema 易错点(实测踩坑沉淀):install 必须是数组(每项为安装处理器/操作,可带 condition 条件);type 为模板类型标识。传错结构会收到精确的 Go 校验错误,如 json: cannot unmarshal object into Go struct field Server.install of type []pufferpanel.ConditionalMetadataType——按错误信息反推字段类型即可。

5.3 服务器管理:/api/servers

创建服务器:PUT 到以服务器 ID 命名的路径(幂等语义),完整请求体(实测成功版本):

curl -s -b /root/ck.txt -X PUT http://127.0.0.1:8080/api/servers/echo1 \
  -H "Content-Type: application/json" \
  -d '{"name":"echo1","node":0,"users":["admin"],"type":"echotest","data":{},"environment":{"type":"standard"},"install":[],"run":{"command":"sleep 600","stop":"","pre":[],"post":[],"environmentVars":{}},"requirements":{}}' \
  -w "\n[HTTP %{http_code}]\n"

实测回显:

{"id":"echo1"}
[HTTP 200]

创建请求体关键约定(均为实测踩坑后确认):

  • node: 0 表示本机 LocalNode(整型,不是字符串);
  • users: 字符串数组(用户名列表),传对象数组会报 json: cannot unmarshal object into Go struct field ServerCreation.users of type string;
  • type: 顶层字符串,值为模板名("echotest"),嵌套对象会报 ServerCreation.Server.Type.type of type string 校验错误;
  • data: 模板变量覆盖(端口/内存等,此处留空使用模板默认)。

查询与权限:

curl -s -b /root/ck.txt "http://127.0.0.1:8080/api/servers/echo1?perms"

实测回显(节选):服务器对象含 id/name/node/users/permissions 与权限映射,?perms 参数令响应额外携带权限表,便于集成侧渲染控制项。

电源操作(均为异步,202 Accepted 即受理):

curl -s -b /root/ck.txt -X POST http://127.0.0.1:8080/api/servers/echo1/install -w "\n[%{http_code}]\n"   # 安装钩子
curl -s -b /root/ck.txt -X POST http://127.0.0.1:8080/api/servers/echo1/start   -w "\n[%{http_code}]\n"   # 启动
curl -s -b /root/ck.txt -X POST http://127.0.0.1:8080/api/servers/echo1/stop    -w "\n[%{http_code}]\n"   # 优雅停止
curl -s -b /root/ck.txt -X POST http://127.0.0.1:8080/api/servers/echo1/kill    -w "\n[%{http_code}]\n"   # 强杀

实测回显:install/start/stop 返回 [202],kill 返回 [204]。状态需另行查询:

curl -s -b /root/ck.txt http://127.0.0.1:8080/api/servers/echo1/status
{"running":true,"installing":false}

控制台读取:GET /api/servers/echo1/console 返回 base64 编码的最近日志(daemon.console.buffer 控制容量),实测解码后:

[DAEMON] Installing server
[DAEMON] Server installed
[DAEMON] Starting server
[DAEMON]  Starting process: sleep 600

发送命令走 POST /api/servers/echo1/console(body {"command":"..."});实时推流由 WebSocket 提供,本篇以 REST 轮询为准。

资源统计:

curl -s -b /root/ck.txt http://127.0.0.1:8080/api/servers/echo1/stats
{"cpu":0,"memory":1986560}

memory 单位字节(约 1.9MB,符合占位进程预期)。其余子资源(/data 文件、/file/* 归档解压、/backup 备份、/tasks 任务、/flags 运行标志、/query 查询状态)与文件管理器/备份/任务 UI 一一对应,均遵循同一认证与会话约定(来源:3.0.9 源码路由核对 + 前端调用核对,查询日期 2026-10-24)。

5.4 节点与配置:/api/nodes、/api/config

curl -s -b /root/ck.txt http://127.0.0.1:8080/api/nodes

实测回显(节选):返回 LocalNode 对象,含 id: 0、公网地址与端口(按 masterUrl 自动探测生成,Docker 部署实测为 publicHost:<宿主IP>, publicPort:8139)、面向远程节点的 clientId/clientSecret 凭据字段。

curl -s -b /root/ck.txt http://127.0.0.1:8080/api/config

实测回显(节选):

{"general":{"registrationEnabled":false,...},"branding":{"name":"Quzuo Game Panel",...}}

该端点为面板可见配置的权威视图,与《配置参数详解》篇 §五.2 的改值实验互相印证。

六、通用约定

  • 方法语义:创建与更新大量使用 PUT(对资源路径幂等写,如 PUT /api/servers/<id>、PUT /api/templates/0/<name>);触发型动作(电源、安装)用 POST;删除为 DELETE。GET 传错方法直接 404,先确认方法再排查路径。
  • 状态码:200 成功;204 成功无内容;202 已受理异步执行(电源/安装类,需轮询 status);401 未认证;404 路径不存在;校验失败返回 400/500 级错误,响应体为 {error: <原因>} 结构。
  • 认证:puffer_auth HttpOnly 会话 Cookie(1 小时有效期),登出 POST /auth/logout;集成账号建议单独创建并只授予所需权限域,避免复用管理员会话。
  • 分页:列表响应 paging 段含 offset/size/total/sortField/sortOrder,遍历按 offset 递增取页。
  • 幂等与并发:PUT 幂等语义使重试安全;电源操作为异步受理,勿以 202 判定执行结果,以 status 查询为准。

七、常见问题(FAQ)

Q1:/api/auth/login 返回 404? 登录端点在 /auth/login(不在 /api 前缀下),实测复现 404 后修正。认证类端点(登录/登出)不带 /api 前缀,其余管理域均在 /api/* 下。

Q2:创建服务器报 users 类型错误? users 是用户名字符串数组:"users":["admin"]。传对象数组([{"username":"admin"}])报 json: cannot unmarshal object into Go struct field ServerCreation.users of type string(实测复现)。

Q3:创建服务器报 Type: required? 顶层 type 必须是模板名字符串:"type":"echotest"。传成 {"type":{"type":"echotest"}} 会触发 ServerCreation.Server.Type.type of type string 校验错误(实测复现)。

Q4:模板保存 POST 404? 本地模板保存用 PUT:PUT /api/templates/0/<名称>(实测 POST 404、PUT 204)。

Q5:stop 之后 running 仍是 true? stop 走模板定义的优雅停机命令,run.stop 为空时对无响应进程无效(实测 5 秒后仍 {"running":true});强杀用 POST /api/servers/echo1/kill(实测 204,状态立即翻转)。模板层修复见《安装指南》篇 Q6。

Q6:会话突然 401? 会话 1 小时过期(Set-Cookie Max-Age=3600 实测),重新登录换新 Cookie;启用了 TOTP 的账号还需第二因素流程。

Q7:想在日志里看到游戏服务器输出? 开 daemon.console.forward 后服务器输出进入 journald(journalctl -u pufferpanel,实测 [echo1] 前缀输出),REST 侧仍以 /console 轮询为主。

八、参考链接