fix(logs): 「所有人」策略保存被拒 + 快捷 LogQL namespace 空且不认默认策略

=== 一、保存策略组报「用户ID无效或重复」===
validatePolicyGroup 里 `if id == 0 || seen[id]` 直接把「所有人」哨兵当非法。
修复:
  - 新增常量 AnyoneUserID = 0(与 policyGroupAccess、前端 ANYONE 对齐)
  - 0 跳过用户存在性校验,但必须独占:与具体用户混选时报
    「「所有人」不能与具体用户同时选择」
  - 重复检查独立出来,报错带具体 ID

=== 二、快捷 LogQL 的 namespace 下拉总是空的 ===
两个叠加原因:
  1. 用的是原生 <datalist>,只在输入时才提示、样式弱,看着像"下拉是空的"
  2. 候选只来自 Loki 实际值 —— 而默认策略配的 default 命名空间里没有业务 Pod,
     Loki 里根本没有这个 label 值,所以永远不出现;前端也不会拿策略值当默认选中

修复:
  后端新增 GET /api/v1/logs/my-scope?sourceId= → {labels, unrestricted}
    (services.UserAccessLabels 暴露当前用户在数据源上的授权 labels;
      admin 返回 unrestricted=true 但仍带上策略 labels 供 UI 做默认值)
  前端:
    - logsApi.myScope()
    - QuickQueryTemplates 用策略里的 namespace 作为初始选中值
    - namespaceOptions = 策略值 ∪ Loki 实际值(去重,策略值优先)
    - namespace/app/pod 三个 <datalist> 全部换成 SearchableValueSelect
      (点击即展开、可搜索,placeholder 显示「请选择(共 N 个)」)

验证:
  PUT /logs/policy-groups/1 {userIds:[0]}         → 200 ✅
  POST userIds:[0,2](混选)                       → 400「「所有人」不能与具体用户同时选择」✅
  GET /logs/my-scope?sourceId=1                   → {"labels":{"namespace":"default"},"unrestricted":true} ✅
  bundle: my-scope 1 处、datalist 残留 0 处 ✅

镜像:lghost/lokistack:latest + v0.1 = sha256:ad659f564665
This commit is contained in:
opsx committed 2026-10-27 02:59:33 +00:00
1 parent 4dda6e5ba8
commit cf54be3f7d
6 files changed
+131 -16

No files matched your search

+14 -2
View File
@@ -25,6 +25,10 @@ func ListLogPolicyGroups() []models.LogPolicyGroup {
return groups
}
// AnyoneUserID 是 userIds 里的哨兵值,表示「所有人」(anyone / *)。
// 与 policyGroupAccess 的匹配逻辑、前端 ANYONE 常量保持一致。
const AnyoneUserID uint = 0
func validatePolicyGroup(db *repository.JsonDB, group models.LogPolicyGroup, excludeID uint) error {
group.Name = strings.TrimSpace(group.Name)
if group.Name == "" {
@@ -53,10 +57,18 @@ func validatePolicyGroup(db *repository.JsonDB, group models.LogPolicyGroup, exc
}
seen := map[uint]bool{}
for _, id := range group.UserIDs {
if id == 0 || seen[id] {
return fmt.Errorf("用户 ID 无效或重复")
if seen[id] {
return fmt.Errorf("用户 ID 重复: %d", id)
}
seen[id] = true
// AnyoneUserID(0) 是「所有人」哨兵,不对应真实用户记录,
// 跳过存在性校验;但它必须独占,不能与其他具体用户混选。
if id == AnyoneUserID {
if len(group.UserIDs) > 1 {
return fmt.Errorf("「所有人」不能与具体用户同时选择")
}
continue
}
found := false
for _, user := range db.Users {
if user.ID == id && user.Role != "admin" {