Files
silo/internal/api/macro_handlers.go
Forbes 6e6c9c2c75 feat(api): macro indexing from .kc files and read-only API
- Add MacroFile type to internal/kc and extract silo/macros/* files
  from .kc ZIP archives on commit
- Create ItemMacroRepository with ReplaceForItem, ListByItem, and
  GetByFilename methods
- Add GET /{partNumber}/macros (list) and
  GET /{partNumber}/macros/{filename} (source content) endpoints
- Index macros in extractKCMetadata with SSE broadcast
- List endpoint omits content for lightweight responses

Closes #144
2026-02-18 19:03:44 -06:00

96 lines
2.7 KiB
Go

package api
import (
"net/http"
"github.com/go-chi/chi/v5"
)
// MacroListItem is the JSON representation for GET /macros list entries.
type MacroListItem struct {
Filename string `json:"filename"`
Trigger string `json:"trigger"`
RevisionNumber int `json:"revision_number"`
}
// MacroResponse is the JSON representation for GET /macros/{filename}.
type MacroResponse struct {
Filename string `json:"filename"`
Trigger string `json:"trigger"`
Content string `json:"content"`
RevisionNumber int `json:"revision_number"`
}
// HandleGetMacros returns the list of registered macros for an item.
// GET /api/items/{partNumber}/macros
func (s *Server) HandleGetMacros(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
partNumber := chi.URLParam(r, "partNumber")
item, err := s.items.GetByPartNumber(ctx, partNumber)
if err != nil {
s.logger.Error().Err(err).Msg("failed to get item")
writeError(w, http.StatusInternalServerError, "internal_error", "Failed to get item")
return
}
if item == nil {
writeError(w, http.StatusNotFound, "not_found", "Item not found")
return
}
macros, err := s.macros.ListByItem(ctx, item.ID)
if err != nil {
s.logger.Error().Err(err).Msg("failed to list macros")
writeError(w, http.StatusInternalServerError, "internal_error", "Failed to list macros")
return
}
resp := make([]MacroListItem, len(macros))
for i, m := range macros {
resp[i] = MacroListItem{
Filename: m.Filename,
Trigger: m.Trigger,
RevisionNumber: m.RevisionNumber,
}
}
writeJSON(w, http.StatusOK, resp)
}
// HandleGetMacro returns a single macro's source content.
// GET /api/items/{partNumber}/macros/{filename}
func (s *Server) HandleGetMacro(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
partNumber := chi.URLParam(r, "partNumber")
filename := chi.URLParam(r, "filename")
item, err := s.items.GetByPartNumber(ctx, partNumber)
if err != nil {
s.logger.Error().Err(err).Msg("failed to get item")
writeError(w, http.StatusInternalServerError, "internal_error", "Failed to get item")
return
}
if item == nil {
writeError(w, http.StatusNotFound, "not_found", "Item not found")
return
}
macro, err := s.macros.GetByFilename(ctx, item.ID, filename)
if err != nil {
s.logger.Error().Err(err).Msg("failed to get macro")
writeError(w, http.StatusInternalServerError, "internal_error", "Failed to get macro")
return
}
if macro == nil {
writeError(w, http.StatusNotFound, "not_found", "Macro not found")
return
}
writeJSON(w, http.StatusOK, MacroResponse{
Filename: macro.Filename,
Trigger: macro.Trigger,
Content: macro.Content,
RevisionNumber: macro.RevisionNumber,
})
}