init
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
package httpserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/example/notify-gateway/internal/gateway"
|
||||
"github.com/example/notify-gateway/internal/mailingress"
|
||||
"github.com/example/notify-gateway/internal/outbox"
|
||||
)
|
||||
|
||||
func (s *Server) UseQueue(q *gateway.Queue, p *mailingress.Poller) { s.queue = q; s.mail = p }
|
||||
func (s *Server) ready(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
|
||||
defer cancel()
|
||||
if s.queue == nil || s.queue.Store.Ping(ctx) != nil {
|
||||
writeJSON(w, 503, map[string]any{"ok": false})
|
||||
return
|
||||
}
|
||||
writeJSON(w, 200, map[string]any{"ok": true})
|
||||
}
|
||||
func (s *Server) deliveries(w http.ResponseWriter, r *http.Request) {
|
||||
if s.queue == nil {
|
||||
http.Error(w, "outbox unavailable", 503)
|
||||
return
|
||||
}
|
||||
offset, _ := strconv.Atoi(r.URL.Query().Get("offset"))
|
||||
jobs, err := s.queue.Store.List(r.Context(), 50, offset)
|
||||
if err != nil {
|
||||
http.Error(w, "outbox unavailable", 503)
|
||||
return
|
||||
}
|
||||
counts, err := s.queue.Store.Counts(r.Context())
|
||||
if err != nil {
|
||||
http.Error(w, "outbox unavailable", 503)
|
||||
return
|
||||
}
|
||||
statuses := []mailingress.Status{}
|
||||
if s.mail != nil {
|
||||
statuses = s.mail.Statuses()
|
||||
}
|
||||
writeJSON(w, 200, map[string]any{"deliveries": jobs, "counts": counts, "mail": statuses})
|
||||
}
|
||||
func (s *Server) deliveryHistory(w http.ResponseWriter, r *http.Request) {
|
||||
if s.queue == nil {
|
||||
http.Error(w, "outbox unavailable", 503)
|
||||
return
|
||||
}
|
||||
rows, err := s.queue.Store.History(r.Context(), r.PathValue("id"))
|
||||
if err != nil {
|
||||
http.Error(w, "outbox unavailable", 503)
|
||||
return
|
||||
}
|
||||
writeJSON(w, 200, rows)
|
||||
}
|
||||
func (s *Server) retryDelivery(w http.ResponseWriter, r *http.Request) {
|
||||
if s.queue == nil {
|
||||
http.Error(w, "outbox unavailable", 503)
|
||||
return
|
||||
}
|
||||
err := s.queue.Store.Retry(r.Context(), r.PathValue("id"))
|
||||
if errors.Is(err, outbox.ErrNotRetryable) {
|
||||
http.Error(w, err.Error(), 409)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
http.Error(w, "outbox unavailable", 503)
|
||||
return
|
||||
}
|
||||
writeJSON(w, 200, map[string]any{"ok": true})
|
||||
}
|
||||
func (s *Server) metrics(w http.ResponseWriter, r *http.Request) {
|
||||
token := os.Getenv("GATEWAY_METRICS_TOKEN")
|
||||
if token == "" {
|
||||
s.requireAdmin(s.writeMetrics)(w, r)
|
||||
return
|
||||
}
|
||||
got := r.Header.Get("Authorization")
|
||||
if subtle.ConstantTimeCompare([]byte(got), []byte("Bearer "+token)) != 1 {
|
||||
http.Error(w, "unauthorized", 401)
|
||||
return
|
||||
}
|
||||
s.writeMetrics(w, r)
|
||||
}
|
||||
func (s *Server) writeMetrics(w http.ResponseWriter, r *http.Request) {
|
||||
if s.queue == nil {
|
||||
http.Error(w, "outbox unavailable", 503)
|
||||
return
|
||||
}
|
||||
counts, err := s.queue.Store.Counts(r.Context())
|
||||
if err != nil {
|
||||
http.Error(w, "outbox unavailable", 503)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/plain; version=0.0.4")
|
||||
fmt.Fprintln(w, "# HELP notify_gateway_deliveries Persisted deliveries by current state.\n# TYPE notify_gateway_deliveries gauge")
|
||||
for _, state := range []string{"pending", "sending", "succeeded", "dry_run", "dead"} {
|
||||
fmt.Fprintf(w, "notify_gateway_deliveries{state=%q} %d\n", state, counts[state])
|
||||
}
|
||||
}
|
||||
func csrfToken(secret, session string) string {
|
||||
mac := hmac.New(sha256.New, []byte(secret))
|
||||
mac.Write([]byte("csrf:" + session))
|
||||
return hex.EncodeToString(mac.Sum(nil))
|
||||
}
|
||||
func (s *Server) csrf(w http.ResponseWriter, r *http.Request) {
|
||||
c, _ := r.Cookie("ng_session")
|
||||
writeJSON(w, 200, map[string]string{"token": csrfToken(s.store.Get().Server.SessionSecret, c.Value)})
|
||||
}
|
||||
Reference in New Issue
Block a user