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}