Skip to content

主题开发 ​

本页供主题开发者使用。Lite 主题是一个包含静态站点和清单文件的 ZIP 包,只替换公共监控页面;管理员后台、首次安装、远程终端和文件管理不受主题影响。API 字段见 API 与 RPC2。

与上游主题的关系

Lite 优先使用 Lite-theme.json,并继续兼容旧主题的 komari-theme.json 和基础公共接口约定。新主题应使用新清单名;未标注差异的基础打包与接入方式仍可按兼容约定使用。

最小目录 ​

text
my-theme.zip
├── Lite-theme.json
├── preview.png
└── dist/
    ├── index.html
    ├── favicon.ico
    └── assets/
        ├── app-a1b2c3.js
        └── app-a1b2c3.css

ZIP 根目录必须直接包含 Lite-theme.json。兼容旧包时也接受 komari-theme.json,但同一包同时存在两个清单时以 Lite-theme.json 为准。不要再套一层仓库目录,也不要上传 GitHub 自动生成的源码压缩包。

主题可正常使用的必要条件:

  • 根目录存在有效的 Lite-theme.json(旧主题可使用 komari-theme.json)。
  • dist/index.html 是可直接运行的生产构建。
  • HTML 引用的所有 JS、CSS、字体和图片都包含在包内或来自明确允许的 HTTPS 地址。
  • 自定义路由不占用 /admin 和 /terminal。

清单文件 ​

json
{
  "name": {
    "zh-CN": "Lite 示例主题",
    "en": "Lite Example Theme"
  },
  "short": "example-theme",
  "description": {
    "zh-CN": "面向 Lite 的响应式主题",
    "en": "A responsive theme for Lite"
  },
  "version": "1.0.0",
  "author": {
    "zh-CN": "示例作者",
    "en": "Example Author"
  },
  "url": "https://github.com/example/lite-theme",
  "preview": "preview.png",
  "navigation": {
    "server_detail": "/server/{uuid}",
    "server_network": "/server/{uuid}?view=network",
    "ping_task_parameter": "ping_task"
  },
  "configuration": {
    "type": "managed",
    "name": {
      "zh-CN": "示例主题设置",
      "en": "Example Theme Settings"
    },
    "data": []
  }
}

顶层字段 ​

字段类型必需说明
namestring | object是完整主题名称,可为字符串或多语言对象
shortstring是唯一目录名
descriptionstring | object建议主题说明,可为字符串或多语言对象
versionstring建议建议使用语义化版本
authorstring | object建议作者或团队,可为字符串或多语言对象
urlstring否项目主页
previewstring否相对主题根目录的预览图
navigationobject否仪表盘跳转到主题服务器页面的路由声明
configurationobject否后台动态配置入口

short 只能包含 ASCII 字母、数字、下划线和连字符。自定义主题不能使用 default,也不能包含空格、斜杠、反斜杠或 ..。

仪表盘导航 ​

navigation 让后台排行按当前启用主题的真实路由生成链接:

字段说明
server_detail服务器详情路径模板,必须包含 {uuid}
server_network服务器网络总览路径模板,必须包含 {uuid};不填时回退到详情页
ping_task_parameterPing 任务 ID 的查询参数名,例如 ping_task

路径必须是站内绝对路径,不能包含协议、域名、反斜杠或越级片段。服务端会对 UUID 和查询参数进行编码。平均时延、延迟抖动和近 15 分钟丢包排行均使用 server_detail,并通过 ping_task_parameter 附带该条目对应的任务 ID。

未提供 navigation 的旧主题仍可安装,服务端会使用兼容回退地址。新主题应显式声明这三个字段,使不同 Lite 版本和第三方主题切换时都能保持正确跳转。

服务端上传限制:

  • 压缩后的主题 ZIP 最多 128 MiB。
  • 最多 10,000 个文件。
  • 单文件解压后最多 128 MiB。
  • 全包解压后最多 512 MiB。
  • Lite-theme.json(或旧兼容清单 komari-theme.json)最多 1 MiB。

压缩包限制在直接上传、更新已安装主题、GitHub 安装和主题市场安装中一致。这些是安全上限,不是推荐体积;实际主题应尽量保持在数 MiB 内,并压缩图片和字体。

网络安装只接受 HTTP/HTTPS 地址,并会检查 DNS 解析结果和每次重定向。主题仓库、清单、预览图和 ZIP 必须位于可公开访问的地址;指向本机、私网或保留地址的下载会被拒绝。

配置入口 ​

configuration.type 支持:

类型data后台行为
managed配置项数组由内置后台生成表单
raw非空 HTML 字符串在后台内容区域用沙箱 iframe 显示,仅允许脚本运行
redirect站内相对路径跳转到主题自己的设置页面

未写 type 的旧主题在部分界面会按 managed 识别,但服务端默认值合并要求显式 type: "managed"。新主题不要省略该字段。

configuration 字段 ​

字段类型说明
typestringmanaged、raw 或 redirect
iconstring后台菜单图标或图片 URL,可选
namestring | object后台入口名称,可多语言
dataarray | string由 type 决定

managed 配置 ​

json
{
  "configuration": {
    "type": "managed",
    "icon": "/themes/example-theme/dist/assets/settings.svg",
    "name": {
      "zh-CN": "主题设置",
      "en": "Theme Settings"
    },
    "data": [
      {
        "type": "title",
        "name": { "zh-CN": "外观", "en": "Appearance" }
      },
      {
        "key": "compactCards",
        "type": "switch",
        "name": "紧凑卡片",
        "default": true,
        "help": "减少移动端卡片留白"
      },
      {
        "key": "accentColor",
        "type": "select",
        "name": "强调色",
        "options": "green,blue,red",
        "default": "green"
      },
      {
        "key": "maxWidth",
        "type": "number",
        "name": "内容最大宽度",
        "default": 1440
      },
      {
        "key": "backgroundUrl",
        "type": "string",
        "name": "背景图片 URL",
        "default": ""
      },
      {
        "key": "footerHtml",
        "type": "richtext",
        "name": "页脚 HTML",
        "default": ""
      },
      {
        "key": "featuredNodes",
        "type": "nodes",
        "name": {
          "zh-CN": "重点节点",
          "en": "Featured nodes"
        },
        "help": {
          "zh-CN": "选择公开页面优先展示的服务器",
          "en": "Servers highlighted on the public page"
        },
        "default": []
      },
      {
        "key": "latencyTasks",
        "type": "pingtasks",
        "name": {
          "zh-CN": "延迟任务",
          "en": "Latency tasks"
        },
        "default": []
      },
      {
        "key": "HomeProbeTasks",
        "type": "serverpingtasks",
        "name": "首页探测任务",
        "default": {},
        "help": "为各节点选择和排序首页显示的探测任务,每台最多 4 个"
      }
    ]
  }
}

配置项字段:

字段适用范围说明
type全部title、switch、select、number、string、richtext、nodes、pingtasks、serverpingtasks
name全部字符串或多语言对象
key标题项除外保存到 theme_settings 的唯一键
required文本类是否必填
optionsselect逗号分隔选项
default标题项除外默认值
help标题项除外帮助文本,可多语言

默认值合并规则:

  • 管理员保存值优先。
  • select 没有默认值时使用第一个选项。
  • number 默认 0。
  • switch 默认 false。
  • string、richtext 默认空字符串。
  • nodes 默认空数组,保存当前实例中的服务器 UUID。
  • pingtasks 默认空数组,保存延迟监测任务 ID。
  • serverpingtasks 使用空对象作为默认值,按节点 UUID 保存延迟监测任务 ID 数组。

nodes 和 pingtasks 由后台提供选择器,主题只需读取保存后的 ID 列表。不要在主题配置里硬编码服务器名称或任务名称。

公开页面通过 /api/public 的 data.theme_settings 获取最终值:

js
const response = await fetch("/api/public");
const { data } = await response.json();
const compact = data.theme_settings?.compactCards ?? true;

theme_settings 是公开数据。不要声明密码、Token、Webhook 密钥或仅管理员可见的信息。

按节点配置探测任务 ​

serverpingtasks 由后台生成按服务器选择任务的表单,只列出已分配给对应节点的延迟监测任务。每台最多选择 4 个,可拖动排序;保存后的数组顺序就是主题应使用的显示顺序。

上例的 HomeProbeTasks 会在 /api/public 的 data.theme_settings 中返回:

json
{
  "HomeProbeTasks": {
    "节点-UUID-A": [12, 3, 8],
    "节点-UUID-B": [5, 2]
  }
}
数据类型说明
HomeProbeTasksobject配置项的键名,由主题清单定义
节点 UUIDstring对象中的键,标识配置适用的服务器
任务 ID 列表number[]最多 4 个已选任务,顺序为展示顺序

清空选择或恢复默认会删除该节点的覆盖配置,保存主题设置后生效。主题应在节点未配置时沿用默认任务顺序,并过滤已经删除或取消分配的任务;全部已选任务都失效时也可回退到默认顺序。该配置不改变监测任务本身的顺序。

多语言文本 ​

name、description、author、configuration.name、配置项 name 和 help 可写成对象:

json
{
  "zh-CN": "背景图片",
  "zh-TW": "背景圖片",
  "en": "Background image",
  "ja": "背景画像"
}

后台优先匹配完整语言代码,再匹配基础语言,然后尝试 en,最后回退到对象中的首个非空值。仍应提供一个可读的默认语言,不要依赖对象键顺序表达业务含义。字符串形式的旧清单仍然有效。

raw 配置 ​

json
{
  "configuration": {
    "type": "raw",
    "icon": "Code",
    "name": "高级主题设置",
    "data": "<!doctype html><html><body><main id=\"app\"></main></body></html>"
  }
}

raw HTML 来自主题包,在后台通过沙箱 iframe 显示。沙箱仅启用 allow-scripts,允许运行 JavaScript,但不授予与后台相同的来源权限:

  • 不能直接访问父页面 DOM、后台 Cookie 或后台的 localStorage、sessionStorage,也不能依赖沙箱内的这些存储接口。
  • 不允许原生表单提交、弹出新窗口、脚本对话框或跳转顶层页面。
  • 请求面板接口时不再按后台同源请求处理,不能依赖继承后台登录状态来读取或保存设置。

已有 raw 配置页如果依赖上述能力,需要适配。需要由后台保存的常规配置,优先使用 managed,由内置表单完成编辑和保存。只安装可信主题,主题脚本仍应避免发起未经用户确认的操作。

redirect 配置 ​

json
{
  "configuration": {
    "type": "redirect",
    "icon": "Settings",
    "name": "主题设置",
    "data": "settings/theme"
  }
}

目标必须是站内路径。允许 query 和 hash;绝对 URL、//、反斜杠和路径中间的 .. 会被拒绝。路径会按站点根目录规范化,部署在子路径时必须实测最终地址。

不要跳转到 /admin 或 /terminal,这两个路径由内置应用接管。

index.html ​

推荐基础结构:

html
<!doctype html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="description" content="A simple server monitor tool." />
    <title>Lite Monitor</title>
    <script type="module" src="/assets/app-a1b2c3.js"></script>
    <link rel="stylesheet" href="/assets/app-a1b2c3.css" />
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>

服务端注入 ​

当前 Lite 行为:

  • 现有 <title> 会被站点名称替换;没有 title 时会自动插入。
  • 服务端会持续同步 document.title,主题运行时不要反复覆盖它。
  • 字面量 A simple server monitor tool. 会替换为站点描述。
  • 小写 </head> 前插入管理员 custom_head。
  • 小写 </body> 前插入管理员 custom_body 和主题切换刷新脚本。

因此必须保留规范的小写 </head>、</body>,并建议保留上面的 description 占位文本。这是明确的 Lite 差异:与上游旧说明不同,title 不再要求严格等于某个固定标签,但写标准占位符仍最兼容。

管理员自定义 HTML 不可信。主题不应把该内容再拼进脚本字符串、属性或 innerHTML 之外的危险上下文。

viewport 与移动端 ​

必须包含:

html
<meta name="viewport" content="width=device-width, initial-scale=1" />

移动端输入框字体至少 16px,可避免 iOS 聚焦时自动放大。列表应在窄屏切换为卡片或可读的分组布局,不应只缩小桌面表格。

资源路径 ​

启用主题后,dist 下资源可从站点根路径访问:

text
dist/assets/app-a1b2c3.js -> /assets/app-a1b2c3.js
dist/logo.png            -> /logo.png

也可以显式访问主题根目录:

text
/themes/example-theme/dist/assets/settings.svg

该前缀适合 manifest 的 icon、预览辅助资源或需要引用未启用主题的管理界面。公共主题页面本身优先使用构建工具生成的根路径。

Favicon 优先级:

  1. 管理员上传的 data/favicon.ico。
  2. 当前主题的 dist/favicon.ico。
  3. 内置默认资源。

SPA 路由 ​

服务端支持 History API 路由。GET 请求没有匹配到主题文件时会返回 dist/index.html,因此 Vue Router、React Router 等 history 模式可直接使用。

限制:

  • /admin 和 /terminal 始终加载内置应用。
  • 不存在的 JS/CSS 文件也可能落到 SPA index,浏览器会报 MIME 或模块加载错误;发布前必须检查所有资源路径。
  • 非 GET 请求不会进入主题 SPA fallback。
  • 不要在公共主题中重做后台管理入口。

缓存策略 ​

  • index.html 不缓存,保证站点名称、描述和主题切换及时生效。
  • sw.js、service-worker.js、registersw.js、manifest.webmanifest 不缓存。
  • /assets/ 下带哈希样式文件名可被长期 immutable 缓存。

构建产物应使用内容哈希,例如 app-a1b2c3.js。更新主题时不要复用相同文件名却改变内容,否则 CDN 和浏览器可能继续使用旧资源。

Service Worker 容易跨主题保留旧资源。除非确实需要离线能力,否则主题不要注册全站 Service Worker;必须使用时,应设计版本升级和注销流程。

公共数据接入 ​

推荐加载顺序:

  1. /api/public 获取站点和主题配置。
  2. /api/nodes 或 common:getNodes 获取节点资料。
  3. /api/clients WebSocket 获取实时状态。
  4. public:queryMetrics 获取图表历史。

只请求当前页面需要的节点和时间范围;长时间序列应使用服务端降采样。参数完全相同的结果可以在主题内短时复用。

common:getNodes 只返回主题展示所需字段,即使管理员已经登录,也不会返回 Agent 版本、私有备注、部署状态、远程控制状态和节点时间戳。完整字段边界见兼容与公共接口。

节点剩余价值可读取 common:getNodes 的 remaining_value 和 remaining_value_currency;登录后管理员头像可读取 public:getMe 的 avatar_url。字段类型、缺省值和币种说明见 API 与 RPC2。

私有站点 ​

私有站点登录前仍可能加载当前主题,以保持访问提示与主题风格一致。此时应先读取公开站点信息和当前登录状态;确认未登录后只显示访问提示,不要创建实时 RPC 连接。

RPC 返回未认证或私有站点拒绝时,应关闭连接并暂停重试,等用户登录或主动刷新后再恢复。持续重连 /api/rpc2 只会产生重复 401,也会增加服务端和浏览器负担。

带宽 ​

大屏要显示线路带宽时,从节点资料读取 bandwidth,不要从 WebSocket 实时状态或公开备注里的旧套餐字段拼。

js
const nodes = await fetch("/api/nodes").then((r) => r.json());
const bandwidth = String(nodes.data?.find((n) => n.uuid === uuid)?.bandwidth || "").trim();
if (bandwidth) {
  // 直接显示,例如 "100 Mbps"
}
  • 值为管理员已整理过的文案,主题不必再换算单位。
  • 空字符串表示未设置,不要显示「未知」或 0。
  • 实时网速仍看状态里的 net_in_speed / net_out_speed。

本地存储 ​

默认主题使用:

键类型说明
appearancelight | dark | system外观模式
languagestring语言代码,例如 zh-CN

第三方主题可复用这些键以保持切换体验。新增键应加主题前缀,避免与其他主题冲突。

打包与验收 ​

构建后从产物目录创建 ZIP,确保 manifest 位于根目录:

text
Lite-theme.json
preview.png
dist/index.html
dist/assets/...

发布前至少验证:

  1. ZIP 根目录和 short 合法。
  2. 首页、节点详情和自定义 SPA 路由可直接刷新。
  3. PC 列表和手机卡片均无横向溢出。
  4. 360px 宽度下按钮、长名称和数值不重叠。
  5. 浅色、深色和系统模式可读。
  6. 实时 WebSocket 断线后可重连。
  7. 24/48 小时历史查询启用服务端降采样。
  8. 私有站点、隐藏节点和临时分享不会泄露数据。
  9. 切换主题后不残留旧 Service Worker 或缓存资源。
  10. /admin、/terminal 和 /favicon.ico 行为正常。

与默认前端的关系 ​

自定义主题不需要复制后台代码,也不应依赖默认前端的私有组件路径。把 API 适配、状态管理和视觉组件分开,后续协议升级时只需调整适配层。

基于 MIT 许可证发布