chore: establish local platform baseline

This commit is contained in:
Codex Local
2026-08-26 06:41:54 +08:00
commit 29bdf4900f
575 changed files with 95644 additions and 0 deletions
+148
View File
@@ -0,0 +1,148 @@
package mockalertreceiver
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"io"
"log/slog"
"net/http"
"strconv"
"strings"
"sync"
"time"
)
const maxBodyBytes = 64 * 1024
type Options struct {
Secret string
ClockSkew time.Duration
}
type Event struct {
EventID string `json:"event_id"`
EventType string `json:"event_type"`
TestRef string `json:"test_ref,omitempty"`
Data json.RawMessage `json:"data"`
ReceivedAt time.Time `json:"received_at"`
}
type envelope struct {
EventID string `json:"event_id"`
EventType string `json:"event_type"`
Data json.RawMessage `json:"data"`
}
type Server struct {
secret []byte
clockSkew time.Duration
logger *slog.Logger
now func() time.Time
mu sync.RWMutex
events []Event
}
func New(options Options, logger *slog.Logger) (*Server, error) {
options.Secret = strings.TrimSpace(options.Secret)
if len(options.Secret) < 32 {
return nil, errors.New("mock alert receiver secret must contain at least 32 characters")
}
if options.ClockSkew <= 0 {
return nil, errors.New("mock alert receiver clock skew must be positive")
}
if logger == nil {
return nil, errors.New("mock alert receiver logger is required")
}
return &Server{secret: []byte(options.Secret), clockSkew: options.ClockSkew, logger: logger, now: time.Now, events: []Event{}}, nil
}
func (s *Server) Handler() http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("GET /health/live", func(w http.ResponseWriter, _ *http.Request) {
writeJSON(w, http.StatusOK, map[string]string{"status": "alive", "service": "mock-alert-receiver"})
})
mux.HandleFunc("POST /webhooks/alerts", s.receive)
mux.HandleFunc("GET /events", s.list)
return mux
}
func (s *Server) receive(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(io.LimitReader(r.Body, maxBodyBytes+1))
if err != nil || len(body) > maxBodyBytes {
http.Error(w, "invalid body", http.StatusBadRequest)
return
}
timestamp := strings.TrimSpace(r.Header.Get("X-Voice-Timestamp"))
eventID := strings.TrimSpace(r.Header.Get("X-Voice-Event-ID"))
unixSeconds, err := strconv.ParseInt(timestamp, 10, 64)
if err != nil || eventID == "" || s.now().UTC().Sub(time.Unix(unixSeconds, 0).UTC()).Abs() > s.clockSkew || !validSignature(s.secret, timestamp, body, r.Header.Get("X-Voice-Signature")) {
http.Error(w, "invalid webhook authentication", http.StatusUnauthorized)
return
}
var incoming envelope
if err = json.Unmarshal(body, &incoming); err != nil || incoming.EventID != eventID || incoming.EventType == "" || !json.Valid(incoming.Data) {
http.Error(w, "invalid webhook envelope", http.StatusBadRequest)
return
}
var metadata struct {
TestRef string `json:"test_ref"`
}
_ = json.Unmarshal(incoming.Data, &metadata)
received := Event{EventID: incoming.EventID, EventType: incoming.EventType, TestRef: metadata.TestRef, Data: incoming.Data, ReceivedAt: s.now().UTC()}
s.mu.Lock()
found := false
for index := range s.events {
if s.events[index].EventID == received.EventID {
s.events[index] = received
found = true
break
}
}
if !found {
s.events = append([]Event{received}, s.events...)
if len(s.events) > 100 {
s.events = s.events[:100]
}
}
s.mu.Unlock()
s.logger.InfoContext(r.Context(), "mock alert webhook received", "event_id", received.EventID, "event_type", received.EventType, "test_ref", received.TestRef)
w.WriteHeader(http.StatusNoContent)
}
func (s *Server) list(w http.ResponseWriter, r *http.Request) {
testRef := strings.TrimSpace(r.URL.Query().Get("test_ref"))
s.mu.RLock()
items := make([]Event, 0, len(s.events))
for _, item := range s.events {
if testRef == "" || item.TestRef == testRef {
items = append(items, item)
}
}
s.mu.RUnlock()
w.Header().Set("Cache-Control", "no-store")
writeJSON(w, http.StatusOK, map[string]any{"items": items})
}
func validSignature(secret []byte, timestamp string, body []byte, provided string) bool {
mac := hmac.New(sha256.New, secret)
_, _ = mac.Write([]byte(timestamp))
_, _ = mac.Write([]byte("."))
_, _ = mac.Write(body)
want := mac.Sum(nil)
provided = strings.TrimSpace(provided)
if !strings.HasPrefix(provided, "v1=") {
return false
}
provided = strings.TrimPrefix(provided, "v1=")
got, err := hex.DecodeString(provided)
return err == nil && hmac.Equal(want, got)
}
func writeJSON(w http.ResponseWriter, status int, value any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
_ = json.NewEncoder(w).Encode(value)
}
+74
View File
@@ -0,0 +1,74 @@
package mockalertreceiver
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"io"
"log/slog"
"net/http"
"net/http/httptest"
"strconv"
"testing"
"time"
)
func TestReceiverVerifiesAndRecordsSignedDelivery(t *testing.T) {
t.Parallel()
secret := "test-alert-receiver-secret-at-least-32-bytes"
now := time.Unix(1_800_000_000, 0).UTC()
server, err := New(Options{Secret: secret, ClockSkew: 5 * time.Minute}, slog.New(slog.NewTextHandler(io.Discard, nil)))
if err != nil {
t.Fatal(err)
}
server.now = func() time.Time { return now }
httpServer := httptest.NewServer(server.Handler())
defer httpServer.Close()
body, _ := json.Marshal(envelope{EventID: "10000000-0000-0000-0000-000000000001", EventType: "alert.webhook_test_requested", Data: json.RawMessage(`{"test_ref":"10000000-0000-0000-0000-000000000099"}`)})
timestamp := strconv.FormatInt(now.Unix(), 10)
request, _ := http.NewRequest(http.MethodPost, httpServer.URL+"/webhooks/alerts", bytes.NewReader(body))
request.Header.Set("X-Voice-Event-ID", "10000000-0000-0000-0000-000000000001")
request.Header.Set("X-Voice-Timestamp", timestamp)
request.Header.Set("X-Voice-Signature", testSignature([]byte(secret), timestamp, body))
response, err := http.DefaultClient.Do(request)
if err != nil {
t.Fatal(err)
}
response.Body.Close()
if response.StatusCode != http.StatusNoContent {
t.Fatalf("delivery status=%d", response.StatusCode)
}
response, err = http.Get(httpServer.URL + "/events?test_ref=10000000-0000-0000-0000-000000000099")
if err != nil {
t.Fatal(err)
}
defer response.Body.Close()
var result struct {
Items []Event `json:"items"`
}
if err = json.NewDecoder(response.Body).Decode(&result); err != nil || len(result.Items) != 1 || result.Items[0].EventType != "alert.webhook_test_requested" {
t.Fatalf("events=%+v err=%v", result.Items, err)
}
invalid, _ := http.NewRequest(http.MethodPost, httpServer.URL+"/webhooks/alerts", bytes.NewReader(body))
invalid.Header.Set("X-Voice-Event-ID", "10000000-0000-0000-0000-000000000001")
invalid.Header.Set("X-Voice-Timestamp", timestamp)
invalid.Header.Set("X-Voice-Signature", "v1=00")
response, err = http.DefaultClient.Do(invalid)
if err != nil {
t.Fatal(err)
}
response.Body.Close()
if response.StatusCode != http.StatusUnauthorized {
t.Fatalf("invalid signature status=%d", response.StatusCode)
}
}
func testSignature(secret []byte, timestamp string, body []byte) string {
mac := hmac.New(sha256.New, secret)
_, _ = mac.Write([]byte(timestamp + "."))
_, _ = mac.Write(body)
return "v1=" + hex.EncodeToString(mac.Sum(nil))
}