mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-13 04:14:56 +00:00
153 lines
4.7 KiB
Go
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
|
|
}
|