Rebrand CommGate to gsmnode and adopt new design system

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>
This commit is contained in:
tajniak81
2026-07-06 08:53:14 +02:00
commit d6956395c8
166 changed files with 12605 additions and 0 deletions
+96
View File
@@ -0,0 +1,96 @@
package api
import (
"net/http"
"strings"
"time"
"smsgateway/apiserver/internal/pb"
)
type inboxDTO struct {
ID string `json:"id"`
DeviceID string `json:"device_id"`
PhoneNumber string `json:"phone_number"`
Message string `json:"message"`
ReceivedAt string `json:"received_at"`
}
func recordToInbox(rec pb.Record) inboxDTO {
return inboxDTO{
ID: asString(rec["id"]),
DeviceID: asString(rec["device"]),
PhoneNumber: asString(rec["phone_number"]),
Message: asString(rec["message"]),
ReceivedAt: asString(rec["received_at"]),
}
}
// handleListInbox lists received SMS for the authenticated user.
func (s *Server) handleListInbox(w http.ResponseWriter, r *http.Request) {
uid, _ := userFromCtx(r.Context())
res, err := s.pb.List(r.Context(), colInbox, pb.ListOptions{
Filter: "owner = " + pbQuote(uid),
Sort: "-received_at",
Page: queryInt(r, "page", 1),
PerPage: clampPerPage(queryInt(r, "per_page", 50)),
})
if err != nil {
writeUpstreamError(w, err)
return
}
out := make([]inboxDTO, 0, len(res.Items))
for _, rec := range res.Items {
out = append(out, recordToInbox(rec))
}
writeJSON(w, http.StatusOK, map[string]any{
"items": out, "total": res.TotalItems, "page": res.Page,
})
}
type receiveSMSRequest struct {
PhoneNumber string `json:"phone_number"`
Message string `json:"message"`
ReceivedAt string `json:"received_at"`
}
// handleReceiveSMS records an incoming SMS reported by a device and fires
// sms:received webhooks.
func (s *Server) handleReceiveSMS(w http.ResponseWriter, r *http.Request) {
device := deviceFromCtx(r.Context())
var req receiveSMSRequest
if err := decodeJSON(r, &req); err != nil {
writeError(w, http.StatusBadRequest, "invalid request body")
return
}
if strings.TrimSpace(req.PhoneNumber) == "" {
writeError(w, http.StatusBadRequest, "phone_number is required")
return
}
receivedAt := req.ReceivedAt
if receivedAt == "" {
receivedAt = time.Now().UTC().Format("2006-01-02 15:04:05.000Z")
}
rec, err := s.pb.Create(r.Context(), colInbox, pb.Record{
"device": asString(device["id"]),
"owner": asString(device["owner"]),
"phone_number": req.PhoneNumber,
"message": req.Message,
"received_at": receivedAt,
})
if err != nil {
writeUpstreamError(w, err)
return
}
s.dispatchWebhooks(asString(device["owner"]), asString(device["id"]), "sms:received", map[string]any{
"inbox_id": asString(rec["id"]),
"phone_number": req.PhoneNumber,
"message": req.Message,
"received_at": receivedAt,
})
writeJSON(w, http.StatusCreated, recordToInbox(rec))
}