package web import ( "crypto/sha256" "encoding/hex" "encoding/json" "net/http" "strings" "sync" "time" ) type publicSnapshot struct { data []byte etag string generatedAt time.Time lastModified time.Time } type publicSnapshotEntry struct { snapshot publicSnapshot expiresAt time.Time } type publicSnapshotService struct { mu sync.Mutex ttl time.Duration entries map[string]publicSnapshotEntry } func newPublicSnapshotService(ttl time.Duration) *publicSnapshotService { return &publicSnapshotService{ttl: ttl, entries: map[string]publicSnapshotEntry{}} } func (s *publicSnapshotService) Get(key string, build func(generatedAt time.Time) any) publicSnapshot { now := time.Now().UTC() s.mu.Lock() defer s.mu.Unlock() if entry, ok := s.entries[key]; ok && now.Before(entry.expiresAt) { return entry.snapshot } payload := build(now) data, err := json.Marshal(payload) if err != nil { data = []byte(`{"ok":false,"error":"SNAPSHOT_FAILED"}`) } hash := sha256.Sum256(data) snapshot := publicSnapshot{ data: append(data, '\n'), etag: `"` + hex.EncodeToString(hash[:]) + `"`, generatedAt: now, lastModified: now.Truncate(time.Second), } s.entries[key] = publicSnapshotEntry{snapshot: snapshot, expiresAt: now.Add(s.ttl)} return snapshot } func (s *publicSnapshotService) Invalidate() { s.mu.Lock() s.entries = map[string]publicSnapshotEntry{} s.mu.Unlock() } func writePublicSnapshot(w http.ResponseWriter, req *http.Request, snapshot publicSnapshot) { w.Header().Set("Content-Type", "application/json; charset=utf-8") w.Header().Set("ETag", snapshot.etag) w.Header().Set("Last-Modified", snapshot.lastModified.Format(http.TimeFormat)) w.Header().Set("Cache-Control", "public, max-age=60, stale-while-revalidate=300") if etagMatches(req.Header.Get("If-None-Match"), snapshot.etag) { w.WriteHeader(http.StatusNotModified) return } if modifiedSince := req.Header.Get("If-Modified-Since"); modifiedSince != "" { if parsed, err := http.ParseTime(modifiedSince); err == nil && !snapshot.lastModified.After(parsed) { w.WriteHeader(http.StatusNotModified) return } } w.WriteHeader(http.StatusOK) _, _ = w.Write(snapshot.data) } func etagMatches(header, etag string) bool { for _, candidate := range strings.Split(header, ",") { candidate = strings.TrimSpace(candidate) if candidate == "*" || candidate == etag || strings.TrimPrefix(candidate, "W/") == etag { return true } } return false }