Raw
 1package web
 2
 3import (
 4	"bytes"
 5	"encoding/binary"
 6	"fmt"
 7	"html/template"
 8	"log/slog"
 9	"net/http"
10	"strings"
11	"time"
12
13	"tug/internal/transport/web/text"
14)
15
16const (
17	flashCookie = "__Host-authsvc_flash"
18	flashTTL    = 60 * time.Second
19)
20
21func (k *Kit) SetFlash(w http.ResponseWriter, key text.Key, v Ident) error {
22	if !key.IsFlash() {
23		return fmt.Errorf("web: %q is not a flash key", key)
24	}
25	if _, err := lookup(key); err != nil {
26		return err
27	}
28	setCookie(w, flashCookie, k.sealFlash(key, v, k.now()), int(flashTTL/time.Second))
29	return nil
30}
31
32func (k *Kit) sealFlash(key text.Key, v Ident, now time.Time) string {
33	payload := binary.BigEndian.AppendUint64(nil, uint64(now.Add(flashTTL).Unix()))
34	payload = append(payload, key...)
35	payload = append(payload, 0)
36	payload = append(payload, v...)
37	return seal(k.keys.flash, payload)
38}
39
40func (k *Kit) takeFlash(w http.ResponseWriter, r *http.Request) template.HTML {
41	c, err := r.Cookie(flashCookie)
42	if err != nil {
43		return ""
44	}
45	setCookie(w, flashCookie, "", -1)
46	payload, err := unseal(k.keys.flash, c.Value)
47	if err != nil || len(payload) < 9 || k.now().Unix() > int64(binary.BigEndian.Uint64(payload[:8])) {
48		return ""
49	}
50	key, v, _ := bytes.Cut(payload[8:], []byte{0})
51	fk := text.Key(key)
52	if !fk.IsFlash() {
53		return ""
54	}
55	s, err := lookup(fk)
56	if err != nil {
57		return ""
58	}
59	if !strings.Contains(s, identMarker) {
60		return template.HTML(template.HTMLEscapeString(s))
61	}
62	out, err := translate(fk, Ident(v))
63	if err != nil || len(v) == 0 {
64		slog.Warn("flash not shown", "key", string(fk))
65		return ""
66	}
67	return out
68}