Nightingale 业务组(BusiGroup)API 指南:基于 Skill Gateway 的只读查询与 bgid/gid 溯源
2026/9/15 11:59:02 网站建设 项目流程

Nightingale 业务组(BusiGroup)API 指南:基于 Skill Gateway 的只读查询与 bgid/gid 溯源

【免费下载链接】nightingaleNightingale is to monitoring and alerting what Grafana is to visualization.项目地址: https://gitcode.com/GitHub_Trending/ni/nightingale

业务组(Business Group,简称 BG)是 Nightingale(n9e)中一切监控资源的归属与权限单元:告警规则、目标(targets)、仪表盘、静默、订阅、录屏规则等都必须归属于某个业务组,而用户对资源的访问权限也是按业务组授予的。本文围绕aiagent/skill/embedded/builtin/skill-creator/api/busi-groups.md这一份 Skill 内置 API 参考文档,完整讲解通过 Skill Gateway 查询业务组的三个只读接口、全部查询参数与响应字段,并结合仓库源码(models/busi_group.gocenter/router/router_busi_group.gomodels/user.go)深入解析其 RBAC 行为与底层实现。读完本文,你将能正确构造bgid/gid/gids参数去调用其它 n9e 只读接口,并能安全地在自己的 Skill 脚本中完成"按业务组发现与筛选资源"的流程。

一、业务组:n9e 的 RBAC 与资源归属单元

在 Nightingale 中,每个告警规则(alert rule)、目标(target)、仪表盘(board)、静默(mute)、订阅(subscribe)、录屏规则(recording rule)等资源都恰好归属于一个业务组,用户对这些资源的访问权限则通过团队(user-group / teams)按组授予。

这里有一个贯穿全篇、必须牢记的关键约定:业务组的id正是其它接口中所说的bgidgidgids。例如:

  • /busi-group/<id>/alert-rules路径中的<id>
  • /busi-groups/alert-rules/targets/alert-his-events/list等接口上的gids/bgid查询参数。

因此,本文介绍的三个接口是整个查询体系的第一站:先从这里发现"当前用户可见哪些业务组、它们的 id 是多少",再把拿到的 id 传给其它接口做资源过滤。

在源码层面,这一模型的落点是 models/busi_group.go 中的BusiGroup结构体(数据表busi_group)与 models/busi_group_member.go 中的BusiGroupMember结构体(数据表busi_group_member,保存"业务组-团队"的关联关系及perm_flag权限标志)。业务组与团队是多对多关系,一个业务组可以由多个团队共同管理,一个团队也可以管理多个业务组。

二、调用前提:Skill Gateway 协议速览

本文档描述的接口属于Skill Gateway 调用,是只读 GET 请求。正式动手前,先了解 n9e-api.md 规定的通用协议(写任何脚本前都应先读该索引文件):

  • Socket 路径来自环境变量N9E_SKILL_GATEWAY,通过它发送换行分隔的 JSON(newline-delimited JSON);
  • 路径必须包含/api/n9e前缀,例如请求{"method":"GET","path":"/api/n9e/busi-groups",...}
  • query中的所有值必须是字符串,例如{"limit":"300","all":"true"},而不是数字或布尔值(这一点只针对querymap;POSTbody内使用原生 JSON 类型);
  • 响应信封{"ok":true,"status":200,"data":{"dat":<payload>,"err":""}}——一定要读取data["dat"];若err非空则说明 n9e API 报错;
  • 列表的两种形态:Pattern A 为dat = {"list":[...],"total":N}(分页,用于高频事件/目标接口);Pattern B 为dat = [...]裸数组(无 total,一次性返回,用于配置对象列表)。本文的业务组接口属于Pattern B
  • 路径不能凭空猜测:错误路径不会返回 404,n9e 会把 SPA 的index.html(HTML 字符串)放进data,导致静默失败。脚本必须校验ok为 true、data是 dict 且data["err"]为空后再使用data["dat"]
  • Deny-list/datasource*/notify-channel*/users、SSO/IdP 等携带密钥或涉及写操作的路径会被网关以ok:false拒绝,不要试图绕过(完整清单见 n9e-api.md 末尾的 Blocked 一节)。

三、三个核心端点一览

busi-groups.md定义了三个端点,覆盖了"列表 → 详情 → 标签"的完整发现链路:

Path用途dat形态
/busi-groups当前用户可见的业务组(管理员:全部;其它用户:其团队所拥有的组)Pattern B——BusiGroup裸数组
/busi-group/:id单个业务组,附带其所属团队user_groups被填充),:id位于路径中单个BusiGroup对象
/busi-groups/tags跨业务组去重后的目标标签集合字符串裸数组

三条端点分别解决三个问题:/busi-groups用于枚举和发现 id;/busi-group/:id用于查看某个组的完整归属信息(谁在管理、读写权限如何);/busi-groups/tags用于"我要按标签筛选目标"场景下的标签字典获取。

四、/busi-groups:列表查询与查询参数

4.1 查询参数

参数类型必填默认值含义
querystring""对组name的不区分大小写子串匹配。(隐藏回退:对管理员,若按名字无匹配,则会把它当作目标ident重试一次,以找到该主机所属的业务组。)
limitint(字符串)300返回的最大行数。
allbool(字符串)falsetrue= 列出系统中的每一个业务组(管理员无论此标志如何都始终看到全部);否则仅列出当前用户团队所拥有的组。

注意:通过网关传参时所有值都是字符串,例如{"limit":"300","all":"true"}

4.2 可见性规则与隐藏的 ident 回退

/busi-groups的可见性并非"全量返回",其 RBAC 逻辑在 models/user.go 的User.BusiGroups方法中体现得淋漓尽致:

  • 管理员(u.IsAdmin())或传了all=true:直接按name like "%query%"查询全部业务组;
  • 普通用户:先通过MyGroupIds拿到用户所属的团队 id,再经BusiGroupIds(见 models/busi_group_member.go)反查出这些团队有权限的业务组 id 集合,最后限定id in ?过滤——用户永远只能看到自己团队所拥有的组
  • 隐藏的 ident 回退:当query按名字查不到任何组时,代码会把query当作目标ident(主机标识)再查一次TargetGet(ctx, "ident=?", query),若命中则返回该主机所属的业务组;普通用户还会额外校验该主机的组是否与busiGroupIds有交集(t.MatchGroupId(busiGroupIds...))。源码注释戏称这是"一般不告诉别人的隐藏功能",但它确实是一个实用的逆向查找入口。

4.3 列表返回的注意事项

  • /busi-groups会填充update_by_nickname(计算字段),但不会填充user_groups(保持为空/null);
  • 当你需要"所属团队及其权限标志"时,请改用/busi-group/:id

update_by_nickname的填充由 models/user.go 的FillUpdateByNicknames泛型函数完成:它通过反射读取每个元素的UpdateBy字段,批量查用户名→昵称映射(UserNicknameMap),再写回UpdateByNickname字段,避免了对数据库的 N 次查询。路由层在 center/router/router_busi_group.go 的busiGroupGets中调用它。

五、/busi-group/:id:单个业务组与其所属团队

当需要获取某个业务组的完整归属信息时,使用/busi-group/:idid必须放在路径中(例如/api/n9e/busi-group/2)。

该接口与列表接口的关键差异在于user_groups字段被填充。其实现对应 center/router/router_busi_group.go 的busiGroupGet:先通过BusiGroup中间件按路径参数拿到目标业务组,再调用bg.FillUserGroups(rt.Ctx)。而FillUserGroups的实现在 models/busi_group.go:

  1. 通过BusiGroupMemberGetsByBusiGroupId查出该业务组的所有成员关系(busi_group_member表);
  2. 对每条成员关系,用UserGroupGetById取回完整的团队对象(UserGroup);
  3. 组装成UserGroupWithPermFlag(定义于 models/busi_group.go),其中PermFlag直接取自成员记录的perm_flag字段。

perm_flag只有两个取值:rw(读写)与ro(只读)。一个业务组通常至少要有一个rw的团队来承担管理职责——路由层busiGroupAdd(center/router/router_busi_group.go)在创建业务组时校验:members不能为空,且必须至少有一个团队是rw权限,否则直接报 400。同样,DelMembers(models/busi_group.go)在删除成员时会确保业务组至少保留一个团队,否则返回 "the business group must retain at least one team"。

六、/busi-groups/tags:跨组目标标签字典

GET /api/n9e/busi-groups/tags?gids=1,2,3
  • 返回从所选业务组的目标(targets)上收集到的去重标签字符串数组([]string),例如["env=prod","region=cn-east-1"]
  • 接受可选的gids查询参数(逗号分隔的业务组 id;为空表示当前用户的所有业务组)。

其实现链路在 center/router/router_busi_group.go 的busiGroupsGetTags:先用TargetIndentsGetByBgids(models/target_busi_group.go)根据业务组 id 集合查出所有目标 ident,再调用TargetGetTags(models/target.go)聚合并去重出标签。典型用法是:拿到标签字典后,与/targets接口配合,用目标标签做进一步的筛选与分组统计。

七、响应对象:BusiGroup全字段解析

/busi-groups返回裸数组(Pattern B),数组中的每个元素是一个BusiGroup对象;/busi-group/:id则直接返回单个对象。字段定义与源码BusiGroup结构体(models/busi_group.go)一一对应:

字段(json)类型含义
idint64业务组 id——即其它接口中使用的bgid/gid/gids的值
namestring业务组显示名(全局唯一)
label_enableint1= 该业务组还会向目标的指标注入一个标签;0= 关闭
label_valuestringlabel_enable=1时注入的标签值(否则为空)
create_atint64创建时间,unix 秒
create_bystring创建者用户名
update_atint64最后更新时间,unix 秒
update_bystring最后更新者用户名
update_by_nicknamestring(计算字段)update_by解析出的显示昵称
user_groupsarray(计算字段)所属团队 + 权限标志。仅由/busi-group/:id填充;在/busi-groups列表中为空/null。每个元素形如{"user_group": <UserGroup>, "perm_flag": "ro"\|"rw"}

关于user_groups中内嵌的UserGroup对象,它携带idnamenotecreate_atcreate_byupdate_atupdate_byupdate_by_nickname,以及(在此处通常为空的)users/busi_groups字段。

7.1 关于 label_enable / label_value 的补充

label_enablelabel_value是业务组上比较容易忽略但影响深远的字段。从 models/busi_group.go 的UpdateBusiGroupAdd(models/busi_group.go)可以看出其约束:

  • name全局唯一,创建/更新时会做BusiGroupExists校验;
  • label_enable=1时,label_value全局唯一——即不允许两个业务组注入相同的标签值,否则会报 "BusiGroup already exists";
  • label_enable=0时,label_value会被强制清空为""

这意味着业务组具备"向归属目标的指标注入固定标签"的能力,可用于在查询指标时区分数据来源归属(源码中以bgLabelKey参数贯穿TargetGetTags等目标处理逻辑)。

八、完整请求 / 响应示例

8.1 列表:/busi-groups

请求:

{"method":"GET","path":"/api/n9e/busi-groups","query":{"limit":"300"}}

响应(已裁剪):

{ "ok": true, "status": 200, "data": { "dat": [ { "id": 2, "name": "default-busi-group", "label_enable": 0, "label_value": "", "create_at": 1700000000, "create_by": "root", "update_at": 1700000000, "update_by": "root", "update_by_nickname": "Administrator", "user_groups": null } ], "err": "" } }

8.2 详情:/busi-group/2(附带所属团队)

请求:

{"method":"GET","path":"/api/n9e/busi-group/2","query":{}}

响应:

{ "ok": true, "status": 200, "data": { "dat": { "id": 2, "name": "default-busi-group", "label_enable": 0, "label_value": "", "create_at": 1700000000, "create_by": "root", "update_at": 1700000000, "update_by": "root", "update_by_nickname": "Administrator", "user_groups": [ {"user_group": {"id": 1, "name": "admins", "note": ""}, "perm_flag": "rw"} ] }, "err": "" } }

对比两份响应可以直观看到差异:列表接口中user_groupsnull,而详情接口中它被填充为一个包含{"user_group": {...}, "perm_flag": "rw"}的数组。示例中id=2name=default-busi-group是安装初始化时自动创建的默认业务组,由名为admins的团队以rw(读写)权限管理。

九、脚本中的实战用法:响应校验与 id 溯源

9.1 响应校验模板

由于错误路径会静默返回 HTML,脚本中应始终先做三段式校验再取数(模板见 n9e-api.md 的 "Validate every response" 一节):

import json, os, socket def call(path, query=None): req = {"method": "GET", "path": "/api/n9e/" + path.lstrip("/"), "query": query or {}} s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) s.connect(os.environ["N9E_SKILL_GATEWAY"]) s.sendall((json.dumps(req) + "\n").encode()) buf = b"" while True: chunk = s.recv(65536) if not chunk: break buf += chunk if b"\n" in buf: break resp = json.loads(buf.split(b"\n")[0].decode()) if not resp.get("ok") or not isinstance(resp.get("data"), dict): raise RuntimeError(f"gateway call failed: {str(resp)[:200]}") env = resp["data"] if env.get("err"): raise RuntimeError(f"n9e api error: {env['err']}") return env["dat"]

9.2 典型流程:发现 → 溯源 → 过滤

一个典型的业务组驱动查询流程如下:

  1. 发现/busi-groups?limit=300拿到全部可见组的id/name映射;
  2. 溯源:对某个不确定归属的资源(如告警事件返回的bgid),可用/busi-group/<id>反查组名与管理团队;
  3. 过滤:把选定的gids(逗号分隔字符串)传给/busi-groups/alert-rules/targets/alert-his-events/list等资源端点,实现"只看某几个业务组"的查询;
  4. 按标签分组/busi-groups/tags?gids=...获取标签字典后,再结合目标标签做汇总统计。

注意业务组相关接口的两种作用域惯用法(见 n9e-api.md 的 "Business-group scoping idiom"):跨组资源用/busi-groups/<resource>?gids=...(空gids= 当前用户 RBAC 允许的所有组),单组资源用/busi-group/<id>/<resource><id>必填于路径)。

9.3 权限边界:只读与 Deny-list

通过 Skill Gateway 调用时,业务组接口仅为只读 GET。网关会以发起对话的用户的身份执行请求,n9e 自身的路由中间件会做常规 RBAC 与业务组权限校验;而携带密钥或写操作的路径(如/datasource*/notify-channel*/users、SSO 相关等)会被网关以ok:false拒绝。业务组的创建、更新、成员管理等写操作不在网关能力范围内,应在 n9e Web 控制台的"业务组"管理界面完成。

十、延伸阅读

  • 调用协议、响应信封、列表形态与 Deny-list 总览:n9e-api.md
  • 业务组数据模型与增删改查、删除级联检查:models/busi_group.go、models/busi_group_member.go
  • 业务组 HTTP 路由与参数解析(列表/详情/标签/成员管理):center/router/router_busi_group.go
  • 业务组可见性的 RBAC 核心逻辑(admin/all/ident 回退):models/user.go
  • 配套资源接口(按组过滤的实际应用场景):api/alert-rules.md、api/targets.md、api/alert-events.md、api/user-groups.md
  • 编写调用这些接口的 Skill 脚本的完整规范:SKILL.md

【免费下载链接】nightingaleNightingale is to monitoring and alerting what Grafana is to visualization.项目地址: https://gitcode.com/GitHub_Trending/ni/nightingale

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询