eleven / messenger Public

Clone
git clone https://amadan.net/eleven/messenger

Plain git — no account needed to clone.

Download

Download this file

1package main
2
3// The public price book (#1374). The setup conversation's plan step runs on
4// the SPACE origin, which holds no home session — so the book must be
5// readable without auth. Prices are public information (the same figures
6// render on this page's own pickers); the payload carries nothing else, and
7// an empty book simply means the step doesn't happen. Cached so a burst of
8// setups never fans out into the provisioner, and served stale on refresh
9// errors so a provisioner blip can't blank a conversation mid-flight.
10
11import (
12 "net/http"
13 "sync"
14 "time"
15)
16
17const pricebookTTL = 10 * time.Minute
18
19type pricebookCache struct {
20 mu sync.Mutex
21 tiers []any
22 fetchedAt time.Time
23}
24
25// pricebookTiers returns the cached book, refreshing from the default
26// region's provisioner (any region as fallback) when stale. Never errors:
27// no cloud, billing off, or an unreachable provisioner with no prior copy
28// all come back as an empty book.
29func (s *Server) pricebookTiers() []any {
30 c := &s.pricebook
31 c.mu.Lock()
32 defer c.mu.Unlock()
33 if time.Since(c.fetchedAt) < pricebookTTL {
34 return c.tiers
35 }
36 // Default region first — it's the book's source of truth (the same
37 // preference the spaces listing applies to its billing key).
38 clouds := s.allClouds()
39 if s.cloud != nil {
40 ordered := []*cloudProvisioner{s.cloud}
41 for _, cl := range clouds {
42 if cl != s.cloud {
43 ordered = append(ordered, cl)
44 }
45 }
46 clouds = ordered
47 }
48 for _, cl := range clouds {
49 status, body, err := cl.do("GET", "/api/pricebook", nil)
50 if err != nil || status != http.StatusOK {
51 continue
52 }
53 tiers := []any{}
54 if b, ok := body["billing"].(map[string]any); ok {
55 if arr, ok := b["tiers"].([]any); ok {
56 tiers = arr
57 }
58 }
59 c.tiers, c.fetchedAt = tiers, time.Now()
60 return c.tiers
61 }
62 if c.fetchedAt.IsZero() {
63 return []any{} // nothing cached and nothing reachable — an empty book, not an error
64 }
65 return c.tiers // stale copy beats a blanked conversation step
66}
67
68// pricebookHandler: GET /api/pricebook — public and CORS-open (the caller is
69// a space origin's browser JS). {tiers:[…]}; empty = billing off.
70func (s *Server) pricebookHandler(w http.ResponseWriter, r *http.Request) {
71 w.Header().Set("Access-Control-Allow-Origin", "*")
72 w.Header().Set("Cache-Control", "public, max-age=300")
73 writeJSON(w, http.StatusOK, map[string]any{"tiers": s.pricebookTiers()})
74}
75