Co-authored-by: gobha <jasafpro@gmail.com> Co-committed-by: gobha <jasafpro@gmail.com>
118 lines
3.5 KiB
Go
118 lines
3.5 KiB
Go
package handlers
|
|
|
|
// profile_bootstrap.go — Single-call boot payload for the SDK.
|
|
//
|
|
// GET /api/v1/profile/bootstrap
|
|
//
|
|
// Collapses what previously required 3-4 sequential requests
|
|
// (profile, permissions, teams/mine, settings) into one call.
|
|
// The SDK calls this at startup and on token refresh.
|
|
//
|
|
// v0.37.15
|
|
|
|
import (
|
|
"net/http"
|
|
"sort"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"chat-switchboard/auth"
|
|
"chat-switchboard/store"
|
|
)
|
|
|
|
// ProfileBootstrapHandler serves the combined boot payload.
|
|
type ProfileBootstrapHandler struct {
|
|
stores store.Stores
|
|
}
|
|
|
|
func NewProfileBootstrapHandler(s store.Stores) *ProfileBootstrapHandler {
|
|
return &ProfileBootstrapHandler{stores: s}
|
|
}
|
|
|
|
// GetBootstrap returns everything the shell needs at startup.
|
|
// GET /api/v1/profile/bootstrap
|
|
func (h *ProfileBootstrapHandler) GetBootstrap(c *gin.Context) {
|
|
userID := getUserID(c)
|
|
role, _ := c.Get("role")
|
|
ctx := c.Request.Context()
|
|
|
|
// ── User profile ────────────────────────
|
|
user, err := h.stores.Users.GetByID(ctx, userID)
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
|
|
return
|
|
}
|
|
|
|
userPayload := gin.H{
|
|
"id": user.ID,
|
|
"username": user.Username,
|
|
"display_name": user.DisplayName,
|
|
"email": user.Email,
|
|
"role": user.Role,
|
|
}
|
|
if user.AvatarURL != "" {
|
|
userPayload["avatar"] = user.AvatarURL
|
|
}
|
|
|
|
// ── Permissions ─────────────────────────
|
|
var permList []string
|
|
if role == "admin" {
|
|
permList = make([]string, len(auth.AllPermissions))
|
|
copy(permList, auth.AllPermissions)
|
|
} else {
|
|
perms, err := auth.ResolvePermissions(ctx, h.stores, userID)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to resolve permissions"})
|
|
return
|
|
}
|
|
permList = make([]string, 0, len(perms))
|
|
for p := range perms {
|
|
permList = append(permList, p)
|
|
}
|
|
}
|
|
sort.Strings(permList)
|
|
|
|
// ── Groups ──────────────────────────────
|
|
groupIDs, _ := h.stores.Groups.GetUserGroupIDs(ctx, userID)
|
|
if groupIDs == nil {
|
|
groupIDs = []string{}
|
|
}
|
|
groupIDs = append(groupIDs, auth.EveryoneGroupID)
|
|
|
|
// ── Teams ───────────────────────────────
|
|
teams, _ := h.stores.Teams.ListForUser(ctx, userID)
|
|
teamData := make([]gin.H, 0, len(teams))
|
|
for _, t := range teams {
|
|
teamData = append(teamData, gin.H{
|
|
"id": t.ID,
|
|
"name": t.Name,
|
|
"my_role": t.MyRole,
|
|
})
|
|
}
|
|
|
|
// ── Policies ────────────────────────────
|
|
policies := make(map[string]bool)
|
|
if ps := h.stores.Policies; ps != nil {
|
|
policies["allow_user_byok"], _ = ps.GetBool(ctx, "allow_user_byok")
|
|
policies["allow_user_personas"], _ = ps.GetBool(ctx, "allow_user_personas")
|
|
policies["allow_raw_model_access"], _ = ps.GetBool(ctx, "allow_raw_model_access")
|
|
policies["kb_direct_access"], _ = ps.GetBool(ctx, "kb_direct_access")
|
|
}
|
|
|
|
// ── Settings ────────────────────────────
|
|
settings := make(map[string]interface{})
|
|
if user.Settings != nil {
|
|
settings = user.Settings
|
|
}
|
|
|
|
// ── Response ────────────────────────────
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"user": userPayload,
|
|
"permissions": permList,
|
|
"groups": groupIDs,
|
|
"teams": teamData,
|
|
"policies": policies,
|
|
"settings": settings,
|
|
})
|
|
}
|