Files
LokiStack/backend/services/log_policy.go
T
logan 2d73661dd1 feat: complete Loki query, insights, settings and deployment workflows
Add the Loki query UI, access policies, ingress insights, alert and registry settings, and their backend APIs. Include the related Kubernetes deployment and collector updates required by the integrated application.

Validated with go test ./..., npm run build, and npm run lint.
2026-10-25 21:28:32 +08:00

176 lines
4.3 KiB
Go

package services
import (
"fmt"
"strings"
"time"
"opsxloki/models"
"opsxloki/repository"
)
func ListLogPolicyGroups() []models.LogPolicyGroup {
db := repository.GetDB()
db.RLock()
defer db.RUnlock()
groups := make([]models.LogPolicyGroup, len(db.LogPolicyGroups))
for i, group := range db.LogPolicyGroups {
groups[i] = group
groups[i].UserIDs = append([]uint(nil), group.UserIDs...)
groups[i].Labels = make(map[string]string, len(group.Labels))
for key, value := range group.Labels {
groups[i].Labels[key] = value
}
}
return groups
}
func validatePolicyGroup(db *repository.JsonDB, group models.LogPolicyGroup, excludeID uint) error {
group.Name = strings.TrimSpace(group.Name)
if group.Name == "" {
return fmt.Errorf("策略组名称必填")
}
if group.SourceID == 0 {
return fmt.Errorf("请选择数据源")
}
foundSource := false
for _, source := range db.LogSources {
if source.ID == group.SourceID {
foundSource = true
break
}
}
if !foundSource {
return fmt.Errorf("数据源不存在")
}
if len(group.UserIDs) == 0 {
return fmt.Errorf("请选择至少一个用户")
}
for key := range group.Labels {
if !ValidLogLabelName(key) {
return fmt.Errorf("无效标签名: %s", key)
}
}
seen := map[uint]bool{}
for _, id := range group.UserIDs {
if id == 0 || seen[id] {
return fmt.Errorf("用户 ID 无效或重复")
}
seen[id] = true
found := false
for _, user := range db.Users {
if user.ID == id && user.Role != "admin" {
found = true
break
}
}
if !found {
return fmt.Errorf("用户 %d 不存在或不是普通用户", id)
}
}
for _, other := range db.LogPolicyGroups {
if other.ID == excludeID || other.SourceID != group.SourceID {
continue
}
if other.Name == group.Name {
return fmt.Errorf("此数据源已有同名策略组")
}
for _, id := range other.UserIDs {
if seen[id] {
return fmt.Errorf("用户 %d 已属于此数据源的另一个策略组", id)
}
}
}
return nil
}
// Group membership replaces legacy per-user grants for the same source.
func removeDirectGrants(db *repository.JsonDB, sourceID uint, userIDs []uint) {
members := map[uint]bool{}
for _, id := range userIDs {
members[id] = true
}
for i := range db.LogSources {
if db.LogSources[i].ID != sourceID {
continue
}
kept := db.LogSources[i].Access[:0]
for _, rule := range db.LogSources[i].Access {
if !members[rule.UserID] {
kept = append(kept, rule)
}
}
db.LogSources[i].Access = kept
return
}
}
func CreateLogPolicyGroup(group models.LogPolicyGroup) (*models.LogPolicyGroup, error) {
db := repository.GetDB()
id := db.NextID("logPolicyGroups")
db.Lock()
defer db.Unlock()
if err := validatePolicyGroup(db, group, 0); err != nil {
return nil, err
}
group.ID = id
group.Name = strings.TrimSpace(group.Name)
group.CreatedAt = time.Now()
group.UpdatedAt = group.CreatedAt
removeDirectGrants(db, group.SourceID, group.UserIDs)
db.LogPolicyGroups = append(db.LogPolicyGroups, group)
db.RequestSave()
return &group, nil
}
func UpdateLogPolicyGroup(id uint, group models.LogPolicyGroup) (*models.LogPolicyGroup, error) {
db := repository.GetDB()
db.Lock()
defer db.Unlock()
for i := range db.LogPolicyGroups {
if db.LogPolicyGroups[i].ID != id {
continue
}
if err := validatePolicyGroup(db, group, id); err != nil {
return nil, err
}
group.ID = id
group.Name = strings.TrimSpace(group.Name)
group.CreatedAt = db.LogPolicyGroups[i].CreatedAt
group.UpdatedAt = time.Now()
removeDirectGrants(db, group.SourceID, group.UserIDs)
db.LogPolicyGroups[i] = group
db.RequestSave()
return &group, nil
}
return nil, fmt.Errorf("策略组不存在")
}
func DeleteLogPolicyGroup(id uint) error {
db := repository.GetDB()
db.Lock()
defer db.Unlock()
for i := range db.LogPolicyGroups {
if db.LogPolicyGroups[i].ID == id {
db.LogPolicyGroups = append(db.LogPolicyGroups[:i], db.LogPolicyGroups[i+1:]...)
db.RequestSave()
return nil
}
}
return fmt.Errorf("策略组不存在")
}
func policyGroupAccess(groups []models.LogPolicyGroup, sourceID, userID uint) *models.LogSourceAccess {
for _, group := range groups {
if group.SourceID != sourceID {
continue
}
for _, id := range group.UserIDs {
if id == userID {
return &models.LogSourceAccess{UserID: userID, Labels: group.Labels}
}
}
}
return nil
}