Changeset 0.20.0 (#85)
This commit is contained in:
146
server/notifications/sources.go
Normal file
146
server/notifications/sources.go
Normal file
@@ -0,0 +1,146 @@
|
||||
package notifications
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"git.gobha.me/xcaliber/chat-switchboard/events"
|
||||
"git.gobha.me/xcaliber/chat-switchboard/models"
|
||||
"git.gobha.me/xcaliber/chat-switchboard/store"
|
||||
)
|
||||
|
||||
// ── Role Fallback ───────────────────────────
|
||||
// Subscribes to the existing "role.fallback" EventBus event (emitted by
|
||||
// capabilities/resolver.go since v0.17.0) and creates notifications for
|
||||
// all admin users.
|
||||
|
||||
// roleFallbackPayload matches the payload emitted by the role resolver.
|
||||
type roleFallbackPayload struct {
|
||||
Role string `json:"role"`
|
||||
Primary string `json:"primary_model"`
|
||||
Fallback string `json:"fallback_model"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// RoleFallbackHandler returns an events.Handler that creates admin
|
||||
// notifications when a model role falls back.
|
||||
func RoleFallbackHandler(svc *Service, stores store.Stores) events.Handler {
|
||||
return func(e events.Event) {
|
||||
var p roleFallbackPayload
|
||||
if err := json.Unmarshal(e.Payload, &p); err != nil {
|
||||
log.Printf("[notifications] failed to parse role.fallback payload: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
title := fmt.Sprintf("Model fallback: %s → %s", p.Primary, p.Fallback)
|
||||
if p.Primary == "" {
|
||||
title = fmt.Sprintf("Role '%s' fell back to %s", p.Role, p.Fallback)
|
||||
}
|
||||
|
||||
body := ""
|
||||
if p.Error != "" {
|
||||
body = fmt.Sprintf("Primary model error: %s", p.Error)
|
||||
}
|
||||
|
||||
// Notify all admin users
|
||||
ctx := context.Background()
|
||||
admins, _, err := stores.Users.List(ctx, store.ListOptions{Limit: 500})
|
||||
if err != nil {
|
||||
log.Printf("[notifications] failed to list users for role.fallback: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, u := range admins {
|
||||
if u.Role != "admin" {
|
||||
continue
|
||||
}
|
||||
n := models.Notification{
|
||||
UserID: u.ID,
|
||||
Type: models.NotifTypeRoleFallback,
|
||||
Title: title,
|
||||
Body: body,
|
||||
}
|
||||
if err := svc.Notify(ctx, &n); err != nil {
|
||||
log.Printf("[notifications] failed to create role.fallback notification for %s: %v", u.ID, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Knowledge Base ──────────────────────────
|
||||
// Called directly from the KB handler after indexing completes or fails.
|
||||
// Not a bus subscriber — the KB handler calls these functions.
|
||||
|
||||
// NotifyKBReady creates a notification when a knowledge base finishes indexing.
|
||||
func NotifyKBReady(svc *Service, userID, kbID, kbName string, chunkCount int) {
|
||||
if svc == nil {
|
||||
return
|
||||
}
|
||||
n := models.Notification{
|
||||
UserID: userID,
|
||||
Type: models.NotifTypeKBReady,
|
||||
Title: fmt.Sprintf("Knowledge base \"%s\" ready (%d chunks)", kbName, chunkCount),
|
||||
ResourceType: models.ResourceTypeKnowledgeBase,
|
||||
ResourceID: kbID,
|
||||
}
|
||||
if err := svc.Notify(context.Background(), &n); err != nil {
|
||||
log.Printf("[notifications] kb.ready failed for user %s: %v", userID, err)
|
||||
}
|
||||
}
|
||||
|
||||
// NotifyKBError creates a notification when KB indexing fails.
|
||||
func NotifyKBError(svc *Service, userID, kbID, kbName, errMsg string) {
|
||||
if svc == nil {
|
||||
return
|
||||
}
|
||||
n := models.Notification{
|
||||
UserID: userID,
|
||||
Type: models.NotifTypeKBError,
|
||||
Title: fmt.Sprintf("Knowledge base \"%s\" indexing failed", kbName),
|
||||
Body: errMsg,
|
||||
ResourceType: models.ResourceTypeKnowledgeBase,
|
||||
ResourceID: kbID,
|
||||
}
|
||||
if err := svc.Notify(context.Background(), &n); err != nil {
|
||||
log.Printf("[notifications] kb.error failed for user %s: %v", userID, err)
|
||||
}
|
||||
}
|
||||
|
||||
// ── Group Membership ────────────────────────
|
||||
// Called directly from the group handler on add/remove.
|
||||
|
||||
// NotifyGroupMemberAdded creates a notification when a user is added to a group.
|
||||
func NotifyGroupMemberAdded(svc *Service, userID, groupID, groupName string) {
|
||||
if svc == nil {
|
||||
return
|
||||
}
|
||||
n := models.Notification{
|
||||
UserID: userID,
|
||||
Type: models.NotifTypeGrantChanged,
|
||||
Title: fmt.Sprintf("You were added to group \"%s\"", groupName),
|
||||
ResourceType: models.ResourceTypeGroup,
|
||||
ResourceID: groupID,
|
||||
}
|
||||
if err := svc.Notify(context.Background(), &n); err != nil {
|
||||
log.Printf("[notifications] grant.changed (add) failed for user %s: %v", userID, err)
|
||||
}
|
||||
}
|
||||
|
||||
// NotifyGroupMemberRemoved creates a notification when a user is removed from a group.
|
||||
func NotifyGroupMemberRemoved(svc *Service, userID, groupID, groupName string) {
|
||||
if svc == nil {
|
||||
return
|
||||
}
|
||||
n := models.Notification{
|
||||
UserID: userID,
|
||||
Type: models.NotifTypeGrantChanged,
|
||||
Title: fmt.Sprintf("You were removed from group \"%s\"", groupName),
|
||||
ResourceType: models.ResourceTypeGroup,
|
||||
ResourceID: groupID,
|
||||
}
|
||||
if err := svc.Notify(context.Background(), &n); err != nil {
|
||||
log.Printf("[notifications] grant.changed (remove) failed for user %s: %v", userID, err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user