- 可观测性
- 后端
- 前端
- 运维
【免费下载链接】kener
Stunning status pages, batteries included!
本文围绕 Kener v4.0.16 版本更新日志(v4.0.16.md/docs/content/v4/changelogs/v4.0.16.md))展开,逐项解析该版本引入的四项新特性——gRPC 服务监控、状态页监控项排序、基于 Origin 的 CSRF 防护、Zeabur 一键部署模板——以及 API 修复与繁中本地化改进。读完本文,你将能够:为 gRPC 服务配置基于标准健康检查协议的监控项、通过position机制管理状态页展示顺序、理解 Kener 服务端跨站表单请求的拦截逻辑,并基于仓库内的部署模板快速理解各环境变量配置。
版本更新总览
v4.0.16 的更新可分为四类:
| 类别 | 内容 |
|---|---|
| New features | gRPC 监控(grpc.health.v1)、状态页监控项排序、Origin 校验 CSRF 防护、Zeabur 部署模板 |
| Bug fixes | 监控 API 部分更新时monitor_settings_json的 JSON 合并丢失问题 |
| Improvements | 繁体中文(zh-TW)完整 UI 翻译 |
以下逐节展开,并结合仓库源码说明每一项的实际实现。
gRPC 监控:基于标准健康检查协议的监控接入
协议原理
Kener v4.0.16 起支持通过标准 gRPC Health Check Protocol(grpc.health.v1)监控 gRPC 服务(对应 issue #505)。与其他监控类型(HTTP API、TCP、DNS 等)不同,gRPC 监控不检查“服务是否可达”这么简单一层,而是直接调用目标服务的Health/Check方法,依据服务端返回的ServingStatus枚举判断业务级健康状态——这在微服务架构下尤其有价值:即使端口能连通,内部某个依赖尚未就绪的服务也可以报告NOT_SERVING。
配置字段
| 字段 | 必填 | 说明 |
|---|---|---|
host | 是 | gRPC 服务的主机名或 IP |
port | 是 | gRPC 服务端口(默认50051) |
service | 否 | 完全限定的服务名(如my.package.ServiceName);留空则检查整体服务器健康 |
tls | 否 | 启用 TLS/SSL 加密连接 |
insecure | 否 | 跳过 TLS 证书校验(仅在tls开启时生效) |
timeout | 否 | 请求超时时间,单位毫秒(默认10000) |
类型定义见 GrpcMonitorTypeData。管理界面中这些字段有对应的默认值填充逻辑,见 monitor-grpc.svelte/manage/app/monitors/[tag]/types/monitor-grpc.svelte#L9-L15)。
通过 API 创建监控项时的配置示例(完整示例与排查建议见 gRPC 监控文档/docs/content/v4/monitors/grpc.md)):
{ "monitor_type": "GRPC", "type_data": { "host": "grpc.example.com", "port": 50051, "service": "my.package.MyService", "tls": true, "timeout": 5000 } }自签名证书场景(等效于grpcurl -insecure):
{ "monitor_type": "GRPC", "type_data": { "host": "grpc.example.com", "port": 50051, "tls": true, "insecure": true, "timeout": 5000 } }状态映射
| gRPC 健康状态 | Kener 状态 |
|---|---|
SERVING | UP |
NOT_SERVING | DOWN |
超时(DEADLINE_EXCEEDED) | DOWN(类型标记为 TIMEOUT) |
UNKNOWN/SERVICE_UNKNOWN | DEGRADED |
| 连接错误 | DOWN |
源码实现解析
gRPC 监控的执行逻辑集中在 grpcCall.ts:
- 内嵌 proto 定义:为避免打包后的生产构建中 proto 文件路径失效,
grpc.health.v1的 proto 定义以字符串形式内嵌在源码中(grpcCall.ts#L10-L33)。首次调用时写入临时目录,经@grpc/proto-loader加载为包定义后删除临时文件,并将解析结果缓存在模块级变量cachedProto中,后续探测复用同一份定义(grpcCall.ts#L36-L54)。 - 凭据选择:
tls与insecure组合决定凭据类型——两者同开时创建rejectUnauthorized: false的 SSL 凭据;仅tls开启时创建标准 SSL 凭据;否则使用不安全凭据(grpcCall.ts#L75-L83)。 - 超时控制:以
new Date(Date.now() + timeoutMs)作为 gRPC 调用的 deadline 传入,未设置timeout时默认 10000ms(grpcCall.ts#L65-L91)。 - 状态判定与错误归类:响应
status === "SERVING"返回 UP;NOT_SERVING返回 DOWN;其余(UNKNOWN、SERVICE_UNKNOWN)返回 DEGRADED(grpcCall.ts#L105-L125)。异常分支中,grpc.status.DEADLINE_EXCEEDED被识别为超时并以 TIMEOUT 类型返回 DOWN,其余 gRPC 错误以 ERROR 类型返回 DOWN,错误信息优先取details字段(grpcCall.ts#L126-L144)。
监控类型的分发入口在 service.ts:当monitor_type为GRPC时实例化GrpcCall并执行。该类型的枚举定义位于 monitor.ts。
页面监控项排序:position 列与前后端链路
v4.0.16 之前,状态页上的监控项按创建顺序展示;现在可以通过页面编辑器中的上/下箭头调整顺序,由数据库新增的position列控制显示序列。
数据库迁移:20260313030304_add_position_page_monitors.ts 在pages_monitors表上添加position整型列(unsigned、非空、默认 0),迁移前通过hasColumn做了幂等保护。
后端逻辑(pagesController.ts):
AddMonitorToPage:若未显式指定position,自动取当前页面已有监控项的最大 position + 1,即新加入的监控项追加到末尾(pagesController.ts#L124-L136);ReorderPageMonitors:接收完整的monitor_tags数组,按数组下标生成position并整体写回(pagesController.ts#L148-L153)。
前端调用链:管理端页面编辑器(pages/[page_id]/+page.svelte)在拖拽/箭头操作后将选中的 tag 数组通过reorderPageMonitorsaction 提交到 manage/api/+server.ts/manage/api/+server.ts#L432-L434),由服务端完成排序持久化。
权限模型:reorderPageMonitors归属pages.write权限,见 allPerms.ts。这意味着多角色(RBAC)环境下,只有被授予页面写权限的用户才能调整监控项顺序。
CSRF 防护:Origin 头校验 + SameSite=Lax 双保险
v4.0.16 为所有状态变更型表单提交(POST、PUT、PATCH、DELETE)增加了基于 Origin 的 CSRF 防护(对应 issue #570)。完整实现位于 hooks.server.ts,作为 SvelteKit 请求管线中的第一个 handler(sequence(csrfHandle, apiAuthHandle),见 hooks.server.ts#L216)。其判定逻辑可以拆解为两层:
- 拦截范围:
Content-Type属于表单类型(application/x-www-form-urlencoded、multipart/form-data、text/plain,hooks.server.ts#L67-L73)且方法为 POST/PUT/PATCH/DELETE 时才进入校验。JSON 请求(管理端 API 与 REST API)不在拦截范围内。 - Origin 比对:若请求携带
Origin头且其值不是字符串"null",则解析出 host 与当前请求的 host 比较;不一致时直接返回403 Cross-site {METHOD} form submissions are forbidden(hooks.server.ts#L78-L96)。
源码注释明确说明了设计取舍:当Origin头缺失时(例如Referrer-Policy: no-referrer的场景),防护依赖 Cookie 的SameSite=Lax属性——跨站 POST 不会携带认证 Cookie,从而天然阻断经典 CSRF 攻击链。这与登录 Cookie 的设置相互印证:会话 Cookie 统一配置为sameSite: "lax"、httpOnly: true、有效期一年,见 commonController.ts。
这一改动对自托管用户的实际意义:攻击者通过恶意站点向 Kener 构造跨站表单提交(例如伪造“创建事件”“修改页面”请求)时,只要请求带Origin头就会被 403 拒绝;不带时,Lax Cookie 策略又保证了认证 Cookie 不会随请求发出。
Zeabur 一键部署模板
仓库根目录的 zeabur-template.yaml 提供了 Zeabur 平台的一键部署模板,预先编排了 Kener + PostgreSQL + Redis 三个服务。核心编排内容:
- Kener 主服务:使用预构建镜像
rajnandan1/kener:latest,对外暴露 3000 端口的 HTTP 服务,声明了对postgresql与redis两个服务的依赖(zeabur-template.yaml#L25-L48)。环境变量映射为:DATABASE_URL← PostgreSQL 连接串(POSTGRES_CONNECTION_STRING)REDIS_URL← Redis 连接串(REDIS_CONNECTION_STRING)KENER_SECRET_KEY← 平台密码变量ORIGIN←https://${ZEABUR_WEB_DOMAIN}
- PostgreSQL:
postgres:18镜像,数据目录挂载于/var/lib/postgresql/data卷,默认库名kener,并导出POSTGRES_CONNECTION_STRING等变量供主服务引用(zeabur-template.yaml#L49-L105)。 - Redis:
redis/redis-stack-server:latest镜像,通过--requirepass启用密码认证,导出REDIS_CONNECTION_STRING(zeabur-template.yaml#L106-L156)。
部署后的使用流程:在浏览器打开分配的PUBLIC_DOMAIN,完成初始设置向导创建管理员账号,再添加第一个监控项。该模板与本仓库 docker-compose.yml、Dockerfile 属于同一部署体系的云上变体,镜像与环境变量要求保持一致。
Bug 修复:监控 API 的 JSON 设置合并
v4.0.16 修复了部分更新监控项时monitor_settings_json未正确合并的问题(PR #654):当数据库中的既有值已经是解析后的对象而非 JSON 字符串时,旧逻辑在合并后直接存储对象,导致写入的数据类型不符合预期,部分更新场景下造成数据丢失。修复后的策略是合并后始终先JSON.stringify再存储。
该修复后的逻辑现在可见于监控项部分更新端点的实现(changelog 中记录为 PUT/api/v4/monitors/{tag},当前仓库中该部分更新逻辑由 PATCH handler 实现):
if (body.monitor_settings_json !== undefined) { if (body.monitor_settings_json === null) { updateData.monitor_settings_json = null; } else { // Parse existing monitor_settings_json if it exists let existingSettings = {}; if (existingMonitor.monitor_settings_json) { try { existingSettings = existingMonitor.monitor_settings_json; } catch { existingSettings = {}; } } // Merge existing with new data const mergedSettings = { ...existingSettings, ...body.monitor_settings_json }; updateData.monitor_settings_json = JSON.stringify(mergedSettings); } } else { updateData.monitor_settings_json = JSON.stringify(existingMonitor.monitor_settings_json); }(见 api/v4/monitors/[monitor_tag]/+server.ts#L118-L137)
几个值得注意的细节:
- 显式传
null表示清空设置,而非保持原值; - 旧值解析失败时回退为空对象,避免脏数据阻断更新;
- 未传该字段时也会把既有值重新 stringify 一次写回,保证库中始终是字符串形态;
- 同一段代码对
type_data字段采用了完全一致的合并策略(+server.ts#L96-L116/api/v4/monitors/[monitor_tag]/+server.ts#L96-L116)),两者属于同一类 JSON 合并缺陷的修复范围。
对应测试位于 server.test.ts/api/v4/monitors/[monitor_tag]/server.test.ts),覆盖了monitor_settings_json在既有值为对象形态时的 PATCH 更新场景。
改进:繁体中文(zh-TW)本地化
本版本新增完整的繁体中文(zh-TW)UI 翻译。翻译文件维护在 zh-TW.json,采用name+mappings结构,将界面文案键(如"All Systems Operational")映射为繁体译文(如"所有系統運行正常"),与仓库内 cs.json、en.json 等二十余种语言文件同目录、同结构,由前端语言选择器(LanguageSelector.svelte)按用户语言加载。changelog 原文注明该翻译覆盖了整个 UI 的键集,自托管多语言团队可在部署后直接在界面中切换验证。
小结
v4.0.16 的更新虽以 changelog 形式发布,但每一项都能在仓库中找到清晰的落点:gRPC 监控的完整执行链在 grpcCall.ts,页面排序的列、控制器、前端调用链贯穿 migrations、pagesController.ts 与管理端路由,CSRF 防护固化在 hooks.server.ts 的请求管线首位,Zeabur 模板提供了云上部署的最小服务编排,而monitor_settings_json的合并修复则保障了 API 部分更新的幂等语义。如果你正在评估或升级 Kener,这一版本在“监控覆盖面(gRPC)”与“安全基线(CSRF)”两方面都是值得纳入升级计划的功能点。
- 可观测性
- 后端
- 前端
- 运维
【免费下载链接】kener
Stunning status pages, batteries included!
相关推荐
Kener Gamedig 监控器实战指南:用状态页监控游戏与游戏服务器服务
Kener Gamedig 监控器实战指南:用状态页监控游戏与游戏服务器服务 Kener 的 Gamedig 监控器( GAMEDIG )基于 node gam
可观测性后端前端运维Prometheus Consul Exporter Helm Chart 实战指南:部署、配置与监控接入
Prometheus Consul Exporter Helm Chart 实战指南:部署、配置与监控接入 本指南以 prometheus community/
Kener 状态页监控嵌入完全指南:JavaScript 脚本与 iframe 两种方案的实战手册
Kener 状态页监控嵌入完全指南:JavaScript 脚本与 iframe 两种方案的实战手册 Kener 状态页提供了开箱即用的监控嵌入(Embed Mo
可观测性后端前端运维
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考