Files

153 lines
4.7 KiB
Go

package http
import (
"net/http"
"github.com/google/uuid"
"github.com/nextlevelbuilder/goclaw/internal/bus"
"github.com/nextlevelbuilder/goclaw/internal/i18n"
"github.com/nextlevelbuilder/goclaw/internal/skills"
"github.com/nextlevelbuilder/goclaw/internal/store"
)
type skillAccessResponse struct {
Skill skillRef `json:"skill"`
Visibility string `json:"visibility"`
AgentGrants []store.SkillAgentGrantInfo `json:"agent_grants"`
UserGrants []store.SkillUserGrantInfo `json:"user_grants"`
}
type skillEffectiveAccessResponse struct {
Skill skillRef `json:"skill"`
Accessible bool `json:"accessible"`
Reason string `json:"reason"`
CanManage bool `json:"can_manage"`
PinnedVersion *int `json:"pinned_version,omitempty"`
}
func (h *SkillsHandler) handleGetSkillAccess(w http.ResponseWriter, r *http.Request) {
sk, ok := h.lifecycleSkill(w, r, false)
if !ok {
return
}
agentGrants, err := h.skills.ListAgentGrantsForSkill(r.Context(), mustParseUUID(sk.ID))
if err != nil {
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
userGrants, err := h.skills.ListUserGrantsForSkill(r.Context(), mustParseUUID(sk.ID))
if err != nil {
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
writeJSON(w, http.StatusOK, skillAccessResponse{
Skill: refForSkill(sk),
Visibility: sk.Visibility,
AgentGrants: agentGrants,
UserGrants: userGrants,
})
}
func (h *SkillsHandler) handlePatchSkillAccess(w http.ResponseWriter, r *http.Request) {
locale := store.LocaleFromContext(r.Context())
sk, ok := h.lifecycleSkill(w, r, false)
if !ok {
return
}
if sk.IsSystem && !h.requireMasterTenant(w, r) {
return
}
var req struct {
Mode string `json:"mode"`
Visibility string `json:"visibility"`
}
if !bindJSON(w, r, locale, &req) {
return
}
visibility := req.Visibility
if visibility == "" {
visibility = req.Mode
}
if err := skills.ValidateVisibility(visibility); err != nil {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": i18n.T(locale, i18n.MsgInvalidVisibility, visibility)})
return
}
visibility = skills.NormalizeVisibility(visibility)
id := mustParseUUID(sk.ID)
updateCtx := r.Context()
if sk.IsSystem {
updateCtx = store.WithCrossTenant(store.WithTenantID(updateCtx, store.MasterTenantID))
}
if err := h.skills.UpdateSkill(updateCtx, id, map[string]any{"visibility": visibility}); err != nil {
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
h.skills.BumpVersion()
h.emitCacheInvalidate(bus.CacheKindSkills, sk.ID, uuid.Nil)
emitAudit(h.msgBus, r, "skill.access_updated", "skill", sk.ID)
writeJSON(w, http.StatusOK, map[string]any{"ok": true, "visibility": visibility})
}
func (h *SkillsHandler) handleGetSkillEffectiveAccess(w http.ResponseWriter, r *http.Request) {
sk, ok := h.lifecycleSkill(w, r, false)
if !ok {
return
}
agentID, userID, ok := parseEffectiveAccessParams(w, r)
if !ok {
return
}
if !h.requireTenantUser(w, r, userID) {
return
}
idx, err := h.buildEffectiveAccessIndex(r, agentID, userID)
if err != nil {
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
resp := effectiveAccessForSkill(r.Context(), sk, idx, userID)
writeJSON(w, http.StatusOK, resp)
}
func (h *SkillsHandler) handleListEffectiveAccess(w http.ResponseWriter, r *http.Request) {
agentID, userID, ok := parseEffectiveAccessParams(w, r)
if !ok {
return
}
if !h.requireTenantUser(w, r, userID) {
return
}
idx, err := h.buildEffectiveAccessIndex(r, agentID, userID)
if err != nil {
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
skills := h.skills.ListAllSkills(r.Context())
out := make([]skillEffectiveAccessResponse, 0, len(skills))
for _, sk := range skills {
out = append(out, effectiveAccessForSkill(r.Context(), sk, idx, userID))
}
writeJSON(w, http.StatusOK, map[string]any{"skills": out})
}
func parseEffectiveAccessParams(w http.ResponseWriter, r *http.Request) (uuid.UUID, string, bool) {
locale := store.LocaleFromContext(r.Context())
agentID, err := uuid.Parse(r.URL.Query().Get("agent_id"))
if err != nil {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": i18n.T(locale, i18n.MsgInvalidID, "agent")})
return uuid.Nil, "", false
}
userID := r.URL.Query().Get("user_id")
if err := store.ValidateUserID(userID); err != nil {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return uuid.Nil, "", false
}
return agentID, userID, true
}
func mustParseUUID(raw string) uuid.UUID {
id, _ := uuid.Parse(raw)
return id
}