package api import ( "bytes" "encoding/json" "net/http" "sort" "git.golem15.com/golem15/summercms/modules/wire" ) // writeJSON answers like Laravel's response()->json behind WinterCMS's // json.response middleware: the JSON body with Cache-Control // "no-cache, private". The plugin keeps its own writers because a plugin in // its own repository cannot import an application's controllers. func writeJSON(w http.ResponseWriter, status int, v any) { w.Header().Set("Cache-Control", "no-cache, private") wire.WriteJSON(w, status, v) } // writeError is the widget routes' {"error":true,"message":...} envelope. func writeError(w http.ResponseWriter, status int, message string) { writeJSON(w, status, errorBody{Error: true, Message: message}) } type errorBody struct { Error bool `json:"error"` Message string `json:"message"` } // writeOpaque500 is the house opaque 500 for a failure that is not the // client's (no database, a failed write). func writeOpaque500(w http.ResponseWriter) { wire.WriteOpaque500(w) } // orderedErrors marshals a Laravel message bag with its keys in rule order // (lagoon.ErrorKeys), the remaining keys sorted after them. type orderedErrors struct { errs map[string][]string order []string } func (o orderedErrors) MarshalJSON() ([]byte, error) { var buf bytes.Buffer buf.WriteByte('{') seen := map[string]bool{} first := true write := func(k string) error { msgs, ok := o.errs[k] if !ok || seen[k] { return nil } seen[k] = true if !first { buf.WriteByte(',') } first = false kb, err := marshalNoEscape(k) if err != nil { return err } vb, err := marshalNoEscape(msgs) if err != nil { return err } buf.Write(kb) buf.WriteByte(':') buf.Write(vb) return nil } for _, k := range o.order { if err := write(k); err != nil { return nil, err } } rest := make([]string, 0, len(o.errs)) for k := range o.errs { rest = append(rest, k) } sort.Strings(rest) for _, k := range rest { if err := write(k); err != nil { return nil, err } } buf.WriteByte('}') return buf.Bytes(), nil } func marshalNoEscape(v any) ([]byte, error) { var buf bytes.Buffer enc := json.NewEncoder(&buf) enc.SetEscapeHTML(false) if err := enc.Encode(v); err != nil { return nil, err } return bytes.TrimRight(buf.Bytes(), "\n"), nil }