d6956395c8
Rename the whole project from CommGate to gsmnode and re-skin every surface onto the new signal-green + ink design system (Space Grotesk / IBM Plex Sans / JetBrains Mono, flat surfaces, two-arrow routing mark, lowercase gsm+node wordmark). - Web App + API panel: rewrite token layer, gn-* utilities, data-gsm-theme, new logo/favicon assets; both builds verified. - Phone App (Flutter): green theme, new mark/wordmark widgets, adaptive launcher icons; rename Android applicationId/package to app.gsmnode.phone; bump AGP to 8.6.0 and google_fonts to 8.1.0; debug APK build verified. - API Server (Go): panel routes, User-Agent, health service id, branding. - Home Assistant Plugin: rename integration domain sms_gateway to gsmnode (folder, DOMAIN, services, entity ids, classes); py_compile verified. - Update all READMEs; add .gitignore (excludes Design/, build artifacts). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
66 lines
1.7 KiB
Go
66 lines
1.7 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
|
|
"smsgateway/apiserver/internal/pb"
|
|
)
|
|
|
|
// writeJSON writes v as a JSON response with the given status code.
|
|
func writeJSON(w http.ResponseWriter, status int, v any) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(status)
|
|
if v == nil {
|
|
return
|
|
}
|
|
if err := json.NewEncoder(w).Encode(v); err != nil {
|
|
log.Printf("writeJSON: %v", err)
|
|
}
|
|
}
|
|
|
|
// errorBody is the standard error envelope.
|
|
type errorBody struct {
|
|
Error string `json:"error"`
|
|
}
|
|
|
|
// writeError writes a JSON error response.
|
|
func writeError(w http.ResponseWriter, status int, msg string) {
|
|
writeJSON(w, status, errorBody{Error: msg})
|
|
}
|
|
|
|
// writeUpstreamError maps a PocketBase error onto an appropriate HTTP status.
|
|
func writeUpstreamError(w http.ResponseWriter, err error) {
|
|
var apiErr *pb.APIError
|
|
if errors.As(err, &apiErr) {
|
|
switch apiErr.Status {
|
|
case http.StatusNotFound:
|
|
writeError(w, http.StatusNotFound, "not found")
|
|
case http.StatusBadRequest:
|
|
writeError(w, http.StatusBadRequest, apiErr.Message)
|
|
case http.StatusForbidden:
|
|
writeError(w, http.StatusForbidden, "forbidden")
|
|
default:
|
|
log.Printf("upstream error: %v (%s)", apiErr, apiErr.Body)
|
|
writeError(w, http.StatusBadGateway, "upstream error")
|
|
}
|
|
return
|
|
}
|
|
log.Printf("internal error: %v", err)
|
|
writeError(w, http.StatusInternalServerError, "internal error")
|
|
}
|
|
|
|
// decodeJSON reads and decodes a JSON request body into v.
|
|
func decodeJSON(r *http.Request, v any) error {
|
|
defer io.Copy(io.Discard, r.Body)
|
|
dec := json.NewDecoder(http.MaxBytesReader(nil, r.Body, 1<<20))
|
|
dec.DisallowUnknownFields()
|
|
if err := dec.Decode(v); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|