fix: address review issues in skills system

- Fix authorization bypass in DeleteSkillFile (missing workspace/role check)
- Extract skills page into features/skills/ module (thin route shell)
- Fix skill files disappearing after save (use API return values + merge in refreshSkills)
- Fix silently swallowed DB errors in ListAgents/GetAgent skill queries

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jiayuan Zhang 2026-03-25 15:37:08 +08:00
parent fb709ff143
commit 42aef6e13e
7 changed files with 599 additions and 543 deletions

View file

@ -130,7 +130,11 @@ func (h *Handler) ListAgents(w http.ResponseWriter, r *http.Request) {
}
// Batch-load skills for all agents to avoid N+1.
skillRows, _ := h.Queries.ListAgentSkillsByWorkspace(r.Context(), parseUUID(workspaceID))
skillRows, err := h.Queries.ListAgentSkillsByWorkspace(r.Context(), parseUUID(workspaceID))
if err != nil {
writeError(w, http.StatusInternalServerError, "failed to load agent skills")
return
}
skillMap := map[string][]SkillResponse{}
for _, row := range skillRows {
agentID := uuidToString(row.AgentID)
@ -159,7 +163,11 @@ func (h *Handler) GetAgent(w http.ResponseWriter, r *http.Request) {
return
}
resp := agentToResponse(agent)
skills, _ := h.Queries.ListAgentSkills(r.Context(), agent.ID)
skills, err := h.Queries.ListAgentSkills(r.Context(), agent.ID)
if err != nil {
writeError(w, http.StatusInternalServerError, "failed to load agent skills")
return
}
if len(skills) > 0 {
resp.Skills = make([]SkillResponse, len(skills))
for i, s := range skills {

View file

@ -440,6 +440,15 @@ func (h *Handler) UpsertSkillFile(w http.ResponseWriter, r *http.Request) {
}
func (h *Handler) DeleteSkillFile(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id")
skill, ok := h.loadSkillForUser(w, r, id)
if !ok {
return
}
if _, ok := h.requireWorkspaceRole(w, r, uuidToString(skill.WorkspaceID), "skill not found", "owner", "admin"); !ok {
return
}
fileID := chi.URLParam(r, "fileId")
if err := h.Queries.DeleteSkillFile(r.Context(), parseUUID(fileID)); err != nil {
writeError(w, http.StatusInternalServerError, "failed to delete skill file")