back interdiff of round #1 and #0

refactor: move computed data code to function to simplify visuals #21

merged
opened by brookjeynes.dev targeting master from push-lokxzmtxxkxo
REVERTED
go.mod
··· 9 github.com/bluesky-social/indigo v0.0.0-20251003000214-3259b215110e 10 github.com/bluesky-social/jetstream v0.0.0-20250414024304-d17bd81a945e 11 github.com/carlmjohnson/versioninfo v0.22.5 12 - github.com/charmbracelet/log v0.4.2 13 github.com/go-chi/chi/v5 v5.2.1 14 github.com/gorilla/sessions v1.4.0 15 github.com/ipfs/go-cid v0.4.1 ··· 29 require ( 30 github.com/a-h/parse v0.0.0-20250122154542-74294addb73e // indirect 31 github.com/andybalholm/brotli v1.1.0 // indirect 32 - github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect 33 github.com/beorn7/perks v1.0.1 // indirect 34 github.com/cenkalti/backoff/v4 v4.3.0 // indirect 35 github.com/cespare/xxhash/v2 v2.3.0 // indirect 36 - github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect 37 - github.com/charmbracelet/lipgloss v1.1.0 // indirect 38 - github.com/charmbracelet/x/ansi v0.8.0 // indirect 39 - github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect 40 - github.com/charmbracelet/x/term v0.2.1 // indirect 41 github.com/cli/browser v1.3.0 // indirect 42 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect 43 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect 44 github.com/fatih/color v1.16.0 // indirect 45 github.com/felixge/httpsnoop v1.0.4 // indirect 46 github.com/fsnotify/fsnotify v1.7.0 // indirect 47 - github.com/go-logfmt/logfmt v0.6.0 // indirect 48 github.com/go-logr/logr v1.4.2 // indirect 49 github.com/go-logr/stdr v1.2.2 // indirect 50 github.com/goccy/go-json v0.10.2 // indirect ··· 77 github.com/lestrrat-go/httprc v1.0.4 // indirect 78 github.com/lestrrat-go/iter v1.0.2 // indirect 79 github.com/lestrrat-go/option v1.0.1 // indirect 80 - github.com/lucasb-eyer/go-colorful v1.2.0 // indirect 81 github.com/mattn/go-colorable v0.1.13 // indirect 82 github.com/mattn/go-isatty v0.0.20 // indirect 83 - github.com/mattn/go-runewidth v0.0.16 // indirect 84 github.com/minio/sha256-simd v1.0.1 // indirect 85 github.com/mr-tron/base58 v1.2.0 // indirect 86 - github.com/muesli/termenv v0.16.0 // indirect 87 github.com/multiformats/go-base32 v0.1.0 // indirect 88 github.com/multiformats/go-base36 v0.2.0 // indirect 89 github.com/multiformats/go-multibase v0.2.0 // indirect ··· 96 github.com/prometheus/client_model v0.6.1 // indirect 97 github.com/prometheus/common v0.54.0 // indirect 98 github.com/prometheus/procfs v0.15.1 // indirect 99 - github.com/rivo/uniseg v0.4.7 // indirect 100 github.com/segmentio/asm v1.2.0 // indirect 101 github.com/spaolacci/murmur3 v1.1.0 // indirect 102 - github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect 103 gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b // indirect 104 gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 // indirect 105 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect ··· 110 go.uber.org/multierr v1.11.0 // indirect 111 go.uber.org/zap v1.26.0 // indirect 112 golang.org/x/crypto v0.40.0 // indirect 113 - golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 // indirect 114 golang.org/x/mod v0.26.0 // indirect 115 golang.org/x/sys v0.34.0 // indirect 116 golang.org/x/time v0.8.0 // indirect
··· 9 github.com/bluesky-social/indigo v0.0.0-20251003000214-3259b215110e 10 github.com/bluesky-social/jetstream v0.0.0-20250414024304-d17bd81a945e 11 github.com/carlmjohnson/versioninfo v0.22.5 12 github.com/go-chi/chi/v5 v5.2.1 13 github.com/gorilla/sessions v1.4.0 14 github.com/ipfs/go-cid v0.4.1 ··· 28 require ( 29 github.com/a-h/parse v0.0.0-20250122154542-74294addb73e // indirect 30 github.com/andybalholm/brotli v1.1.0 // indirect 31 github.com/beorn7/perks v1.0.1 // indirect 32 github.com/cenkalti/backoff/v4 v4.3.0 // indirect 33 github.com/cespare/xxhash/v2 v2.3.0 // indirect 34 github.com/cli/browser v1.3.0 // indirect 35 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect 36 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect 37 github.com/fatih/color v1.16.0 // indirect 38 github.com/felixge/httpsnoop v1.0.4 // indirect 39 github.com/fsnotify/fsnotify v1.7.0 // indirect 40 github.com/go-logr/logr v1.4.2 // indirect 41 github.com/go-logr/stdr v1.2.2 // indirect 42 github.com/goccy/go-json v0.10.2 // indirect ··· 69 github.com/lestrrat-go/httprc v1.0.4 // indirect 70 github.com/lestrrat-go/iter v1.0.2 // indirect 71 github.com/lestrrat-go/option v1.0.1 // indirect 72 github.com/mattn/go-colorable v0.1.13 // indirect 73 github.com/mattn/go-isatty v0.0.20 // indirect 74 github.com/minio/sha256-simd v1.0.1 // indirect 75 github.com/mr-tron/base58 v1.2.0 // indirect 76 github.com/multiformats/go-base32 v0.1.0 // indirect 77 github.com/multiformats/go-base36 v0.2.0 // indirect 78 github.com/multiformats/go-multibase v0.2.0 // indirect ··· 85 github.com/prometheus/client_model v0.6.1 // indirect 86 github.com/prometheus/common v0.54.0 // indirect 87 github.com/prometheus/procfs v0.15.1 // indirect 88 github.com/segmentio/asm v1.2.0 // indirect 89 github.com/spaolacci/murmur3 v1.1.0 // indirect 90 gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b // indirect 91 gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 // indirect 92 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect ··· 97 go.uber.org/multierr v1.11.0 // indirect 98 go.uber.org/zap v1.26.0 // indirect 99 golang.org/x/crypto v0.40.0 // indirect 100 golang.org/x/mod v0.26.0 // indirect 101 golang.org/x/sys v0.34.0 // indirect 102 golang.org/x/time v0.8.0 // indirect
REVERTED
go.sum
··· 5 github.com/a-h/templ v0.3.898/go.mod h1:oLBbZVQ6//Q6zpvSMPTuBK0F3qOtBdFBcGRspcT+VNQ= 6 github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= 7 github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= 8 - github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= 9 - github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= 10 github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 11 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 12 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= ··· 24 github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= 25 github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= 26 github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 27 - github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs= 28 - github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= 29 - github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= 30 - github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= 31 - github.com/charmbracelet/log v0.4.2 h1:hYt8Qj6a8yLnvR+h7MwsJv/XvmBJXiueUcI3cIxsyig= 32 - github.com/charmbracelet/log v0.4.2/go.mod h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw= 33 - github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE= 34 - github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q= 35 - github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8= 36 - github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= 37 - github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= 38 - github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= 39 github.com/cli/browser v1.3.0 h1:LejqCrpWr+1pRqmEPDGnTZOjsMe7sehifLynZJuqJpo= 40 github.com/cli/browser v1.3.0/go.mod h1:HH8s+fOAxjhQoBUAsKuPCbqUuxZDhQ2/aD+SzsEfBTk= 41 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= ··· 56 github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= 57 github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8= 58 github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= 59 - github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= 60 - github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= 61 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 62 github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= 63 github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= ··· 164 github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= 165 github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= 166 github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= 167 - github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= 168 - github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= 169 github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 170 github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 171 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 172 github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 173 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 174 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 175 - github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= 176 - github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 177 github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= 178 github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= 179 github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= 180 github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= 181 github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= 182 github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= 183 - github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= 184 - github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= 185 github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= 186 github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= 187 github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= ··· 218 github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= 219 github.com/redis/go-redis/v9 v9.14.0 h1:u4tNCjXOyzfgeLN+vAZaW1xUooqWDqVEsZN0U01jfAE= 220 github.com/redis/go-redis/v9 v9.14.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= 221 - github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 222 - github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= 223 - github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 224 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 225 github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= 226 github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= ··· 256 github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11/go.mod h1:Wlo/SzPmxVp6vXpGt/zaXhHH0fn4IxgqZc82aKg6bpQ= 257 github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e h1:28X54ciEwwUxyHn9yrZfl5ojgF4CBNLWX7LR0rvBkf4= 258 github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so= 259 - github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= 260 - github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= 261 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 262 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 263 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
··· 5 github.com/a-h/templ v0.3.898/go.mod h1:oLBbZVQ6//Q6zpvSMPTuBK0F3qOtBdFBcGRspcT+VNQ= 6 github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= 7 github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= 8 github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 9 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 10 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= ··· 22 github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= 23 github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= 24 github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 25 github.com/cli/browser v1.3.0 h1:LejqCrpWr+1pRqmEPDGnTZOjsMe7sehifLynZJuqJpo= 26 github.com/cli/browser v1.3.0/go.mod h1:HH8s+fOAxjhQoBUAsKuPCbqUuxZDhQ2/aD+SzsEfBTk= 27 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= ··· 42 github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= 43 github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8= 44 github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= 45 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 46 github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= 47 github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= ··· 148 github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= 149 github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= 150 github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= 151 github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 152 github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 153 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 154 github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 155 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 156 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 157 github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= 158 github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= 159 github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= 160 github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= 161 github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= 162 github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= 163 github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= 164 github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= 165 github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= ··· 196 github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= 197 github.com/redis/go-redis/v9 v9.14.0 h1:u4tNCjXOyzfgeLN+vAZaW1xUooqWDqVEsZN0U01jfAE= 198 github.com/redis/go-redis/v9 v9.14.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= 199 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 200 github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= 201 github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= ··· 231 github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11/go.mod h1:Wlo/SzPmxVp6vXpGt/zaXhHH0fn4IxgqZc82aKg6bpQ= 232 github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e h1:28X54ciEwwUxyHn9yrZfl5ojgF4CBNLWX7LR0rvBkf4= 233 github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so= 234 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 235 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 236 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
REVERTED
internal/server/log/log.go
··· 4 "context" 5 "log/slog" 6 "os" 7 - 8 - "github.com/charmbracelet/log" 9 ) 10 11 func NewHandler(name string) slog.Handler { 12 - return log.NewWithOptions(os.Stderr, log.Options{ 13 - ReportTimestamp: true, 14 - Prefix: name, 15 - Level: log.DebugLevel, 16 - }) 17 } 18 19 func New(name string) *slog.Logger { ··· 45 46 return slog.Default() 47 } 48 - 49 - // Sublogger derives a new logger from an existing one by appending a suffix to 50 - // its prefix. 51 - func SubLogger(base *slog.Logger, suffix string) *slog.Logger { 52 - // Try to get the underlying charmbracelet logger. 53 - if cl, ok := base.Handler().(*log.Logger); ok { 54 - prefix := cl.GetPrefix() 55 - if prefix != "" { 56 - prefix = prefix + "/" + suffix 57 - } else { 58 - prefix = suffix 59 - } 60 - return slog.New(NewHandler(prefix)) 61 - } 62 - 63 - // Fallback to no known handler type. 64 - return slog.New(NewHandler(suffix)) 65 - }
··· 4 "context" 5 "log/slog" 6 "os" 7 ) 8 9 + // NewHandler sets up a new slog.Handler with the service name as an attribute 10 func NewHandler(name string) slog.Handler { 11 + handler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{}) 12 + 13 + var attrs []slog.Attr 14 + attrs = append(attrs, slog.Attr{Key: "service", Value: slog.StringValue(name)}) 15 + handler.WithAttrs(attrs) 16 + return handler 17 } 18 19 func New(name string) *slog.Logger { ··· 45 46 return slog.Default() 47 }
REVERTED
internal/db/db.go
··· 4 "context" 5 "database/sql" 6 "fmt" 7 - "log/slog" 8 "strings" 9 10 _ "github.com/mattn/go-sqlite3" ··· 12 13 type DB struct { 14 *sql.DB 15 - logger *slog.Logger 16 } 17 18 type Execer interface { ··· 26 PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) 27 } 28 29 - func Make(ctx context.Context, dbPath string, logger *slog.Logger) (*DB, error) { 30 opts := []string{ 31 "_foreign_keys=1", 32 "_journal_mode=WAL", ··· 39 return nil, fmt.Errorf("failed to open db: %w", err) 40 } 41 42 conn, err := db.Conn(ctx) 43 if err != nil { 44 return nil, err ··· 231 return nil, fmt.Errorf("failed to execute db create statement: %w", err) 232 } 233 234 - return &DB{ 235 - db, 236 - logger, 237 - }, nil 238 }
··· 4 "context" 5 "database/sql" 6 "fmt" 7 "strings" 8 9 _ "github.com/mattn/go-sqlite3" ··· 11 12 type DB struct { 13 *sql.DB 14 } 15 16 type Execer interface { ··· 24 PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) 25 } 26 27 + func Make(dbPath string) (*DB, error) { 28 opts := []string{ 29 "_foreign_keys=1", 30 "_journal_mode=WAL", ··· 37 return nil, fmt.Errorf("failed to open db: %w", err) 38 } 39 40 + ctx := context.Background() 41 + 42 conn, err := db.Conn(ctx) 43 if err != nil { 44 return nil, err ··· 231 return nil, fmt.Errorf("failed to execute db create statement: %w", err) 232 } 233 234 + return &DB{db}, nil 235 }
REVERTED
internal/server/handlers/study-session.go
··· 3 import ( 4 "errors" 5 "fmt" 6 "net/http" 7 "strconv" 8 "time" ··· 116 } 117 118 func (h *Handler) HandleStudySessionFeed(w http.ResponseWriter, r *http.Request) { 119 - l := h.Logger.With("handler", "HandleStudySessionFeed") 120 - 121 isFriends, err := strconv.ParseBool(r.URL.Query().Get("friends")) 122 if err != nil { 123 - l.Error("failed to parse friends value", "err", err) 124 isFriends = false 125 } 126 ··· 130 } 131 page, err := strconv.ParseInt(pageStr, 10, 64) 132 if err != nil { 133 - l.Error("failed to parse page value", "err", err) 134 page = 1 135 } 136 if page == 0 { ··· 149 if !isFriends { 150 feed, err = db.GetStudySessionFeed(h.Db, pageSize+1, int(offset)) 151 if err != nil { 152 - l.Error("failed to get global feed", "err", err) 153 htmx.HxError(w, http.StatusInternalServerError, "Failed to get global study session feed, try again later.") 154 return 155 } 156 err = h.GetBskyProfileHydratedSessionFeed(feed) 157 if err != nil { 158 - l.Error("failed to hydrate bsky profiles", "err", err) 159 htmx.HxError(w, http.StatusInternalServerError, "Failed to get global study session feed, try again later.") 160 return 161 } 162 } else { 163 if fetchUserError != nil { 164 - l.Error("failed to get logged-in user", "err", err) 165 htmx.HxRedirect(w, "/login") 166 return 167 } 168 169 feed, err = db.GetFriendsStudySessionFeed(h.Db, user.Did, pageSize+1, int(offset)) 170 if err != nil { 171 - l.Error("failed to get global feed", "err", err) 172 htmx.HxError(w, http.StatusInternalServerError, "Failed to get global study session feed, try again later.") 173 return 174 } 175 err = h.GetBskyProfileHydratedSessionFeed(feed) 176 if err != nil { 177 - l.Error("failed to hydrate bsky profiles", "err", err) 178 htmx.HxError(w, http.StatusInternalServerError, "Failed to get global study session feed, try again later.") 179 return 180 } ··· 182 183 feed, err = ApplyPendingChanges(h, w, r, feed, PendingStudySessionCreation, PendingStudySessionUpdates, PendingStudySessionDeletion) 184 if err != nil { 185 - l.Error("failed to save yoten-session after processing pending changes", "err", err) 186 } 187 188 nextPage := 0 ··· 201 } 202 203 func (h *Handler) HandleEditStudySessionPage(w http.ResponseWriter, r *http.Request) { 204 - l := h.Logger.With("handler", "HandleEditStudySessionPage") 205 - 206 client, err := h.Oauth.AuthorizedClient(r) 207 if err != nil { 208 - l.Error("failed to get authorized client", "err", err) 209 htmx.HxRedirect(w, "/login") 210 return 211 } 212 213 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 214 if err != nil { 215 - l.Error("failed to get logged-in user", "err", err) 216 htmx.HxRedirect(w, "/login") 217 return 218 } ··· 220 rkey := chi.URLParam(r, "rkey") 221 studySession, err := db.GetStudySessionByRkey(h.Db, user.Did, rkey) 222 if err != nil { 223 - l.Error("failed to get study session from db", "err", err) 224 htmx.HxError(w, http.StatusInternalServerError, "Failed to update study session, try again later.") 225 return 226 } 227 228 if user.Did != studySession.Did { 229 - l.Error("user does not own record", "did", user.Did, "sessionDid", studySession.Did) 230 htmx.HxError(w, http.StatusUnauthorized, "You do not have permissions to edit this study session.") 231 return 232 } ··· 235 case http.MethodGet: 236 userDefinedActivities, err := db.GetActivitiesByDid(h.Db, user.Did) 237 if err != nil { 238 - l.Error("failed to get user-defined activities", "err", err) 239 } 240 241 resources, err := db.GetResourcesByDid(h.Db, user.Did) 242 if err != nil { 243 - l.Error("failed to get user-defined resources", "err", err) 244 } 245 246 preDefinedActivities, err := db.GetPredefinedActivities(h.Db) 247 if err != nil { 248 - l.Error("failed to get pre-defined activities", "err", err) 249 } 250 251 currentResource := studySession.Resource ··· 270 271 languages, err := db.GetProfileLanguages(h.Db, user.Did) 272 if err != nil { 273 - l.Error("failed to fetch profile languages", "err", err) 274 } 275 276 views.EditStudySessionPage(views.EditStudySessionPageParams{ ··· 283 case http.MethodPost: 284 updatedStudySession, err := h.parseStudySessionForm(r) 285 if err != nil { 286 - l.Error("invalid study session form", "err", err) 287 htmx.HxError(w, http.StatusBadRequest, "Failed to update study session, ensure all data is valid.") 288 return 289 } ··· 292 updatedStudySession.CreatedAt = studySession.CreatedAt 293 294 if err := db.ValidateStudySession(updatedStudySession); err != nil { 295 - l.Error("invalid study session", "err", err) 296 switch { 297 case errors.Is(err, db.ErrSessionDescriptionTooLong): 298 htmx.HxError(w, http.StatusBadRequest, "Study session description cannot be more than 256 characters.") ··· 361 SwapRecord: cid, 362 }) 363 if err != nil { 364 - l.Error("failed to update study session record", "err", err) 365 htmx.HxError(w, http.StatusInternalServerError, "Failed to update study session, try again later.") 366 return 367 } 368 369 err = SavePendingUpdate(h, w, r, PendingStudySessionUpdates, updatedStudySession) 370 if err != nil { 371 - l.Error("failed to save yoten-session to add pending study session updates", "err", err) 372 } 373 374 if !h.Config.Core.Dev { ··· 380 Set("rkey", rkey), 381 }) 382 if err != nil { 383 - l.Error("failed to enqueue posthog event", "err", err) 384 } 385 } 386 ··· 389 } 390 391 func (h *Handler) HandleNewStudySessionPage(w http.ResponseWriter, r *http.Request) { 392 - l := h.Logger.With("handler", "HandleNewStudySessionPage") 393 - 394 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 395 if err != nil { 396 - l.Error("failed to get logged-in user", "err", err) 397 htmx.HxRedirect(w, "/login") 398 return 399 } 400 401 client, err := h.Oauth.AuthorizedClient(r) 402 if err != nil { 403 - l.Error("failed to get authorized client", "err", err) 404 htmx.HxRedirect(w, "/login") 405 return 406 } ··· 409 case http.MethodGet: 410 profile, err := db.GetProfile(h.Db, user.Did) 411 if err != nil { 412 - l.Error("failed to find user in db", "did", user.Did, "err", err) 413 htmx.HxError(w, http.StatusNotFound, "Failed to find user.") 414 return 415 } 416 417 userDefinedActivities, err := db.GetActivitiesByDid(h.Db, user.Did) 418 if err != nil { 419 - l.Error("failed to get user-defined activities", "err", err) 420 } 421 preDefinedActivities, err := db.GetPredefinedActivities(h.Db) 422 if err != nil { 423 - l.Error("failed to get pre-defined activities", "err", err) 424 } 425 activeActivities := utils.Filter(userDefinedActivities, func(activity db.Activity) bool { 426 return activity.Status != db.Deleted ··· 429 430 resources, err := db.GetResourcesByDid(h.Db, user.Did) 431 if err != nil { 432 - l.Error("failed to get user-defined resources", "err", err) 433 } 434 activeResources := utils.Filter(resources, func(resource db.Resource) bool { 435 return resource.Status != db.Deleted ··· 445 case http.MethodPost: 446 newStudySession, err := h.parseStudySessionForm(r) 447 if err != nil { 448 - l.Error("invalid study session form", "err", err) 449 htmx.HxError(w, http.StatusBadRequest, "Failed to update study session, ensure all data is valid.") 450 return 451 } ··· 464 } 465 466 if err := db.ValidateStudySession(newStudySession); err != nil { 467 - l.Error("invalid study session", "err", err) 468 switch { 469 case errors.Is(err, db.ErrSessionDescriptionTooLong): 470 htmx.HxError(w, http.StatusBadRequest, "Study session description cannot be more than 256 characters.") ··· 526 }, 527 }) 528 if err != nil { 529 - l.Error("failed to create study session record", "err", err) 530 htmx.HxError(w, http.StatusInternalServerError, "Failed to create study session, try again later.") 531 return 532 } 533 534 err = SavePendingCreate(h, w, r, PendingStudySessionCreation, newStudySession) 535 if err != nil { 536 - l.Error("failed to save yoten-session to add pending study session creation", "err", err) 537 } 538 539 if !h.Config.Core.Dev { ··· 550 Set("date_is_today", newStudySession.Date.Truncate(24*time.Hour).Equal(time.Now().UTC().In(loc).Truncate(24*time.Hour))), 551 }) 552 if err != nil { 553 - l.Error("failed to enqueue posthog event", "err", err) 554 } 555 } 556 ··· 559 } 560 561 func (h *Handler) HandleDeleteStudySession(w http.ResponseWriter, r *http.Request) { 562 - l := h.Logger.With("handler", "HandleDeleteStudySession") 563 - 564 user := h.Oauth.GetUser(r) 565 if user == nil { 566 - l.Error("failed to get logged-in user") 567 htmx.HxRedirect(w, "/login") 568 return 569 } 570 571 client, err := h.Oauth.AuthorizedClient(r) 572 if err != nil { 573 - l.Error("failed to get authorized client", "err", err) 574 htmx.HxError(w, http.StatusUnauthorized, "Failed to delete study session, try again later.") 575 return 576 } ··· 579 case http.MethodDelete: 580 err := r.ParseForm() 581 if err != nil { 582 - l.Error("failed to parse study session delete form", "err", err) 583 htmx.HxError(w, http.StatusBadRequest, "Failed to delete study session, try again later.") 584 return 585 } ··· 587 rkey := chi.URLParam(r, "rkey") 588 studySession, err := db.GetStudySessionByRkey(h.Db, user.Did, rkey) 589 if err != nil { 590 - l.Error("failed to get study session from db", "err", err) 591 htmx.HxError(w, http.StatusInternalServerError, "Failed to delete study session, try again later.") 592 return 593 } 594 595 if user.Did != studySession.Did { 596 - l.Error("user does not own record", "did", user.Did, "sessionDid", studySession.Did) 597 htmx.HxError(w, http.StatusUnauthorized, "Failed to delete study session, try again later.") 598 return 599 } ··· 604 Rkey: rkey, 605 }) 606 if err != nil { 607 - l.Error("failed to delete study session from PDS", "err", err) 608 htmx.HxError(w, http.StatusInternalServerError, "Failed to delete study session, try again later.") 609 return 610 } 611 612 err = SavePendingDelete(h, w, r, PendingStudySessionDeletion, studySession) 613 if err != nil { 614 - l.Error("failed to save yoten-session to add pending study session deletion", "err", err) 615 } 616 617 if !h.Config.Core.Dev { ··· 624 Set("session_age_seconds", time.Since(studySession.CreatedAt).Seconds()), 625 }) 626 if err != nil { 627 - l.Error("failed to enqueue posthog event", "err", err) 628 } 629 } 630 ··· 651 } 652 653 func (h *Handler) HandleStudySessionPage(w http.ResponseWriter, r *http.Request) { 654 - l := h.Logger.With("handler", "HandleStudySessionPage") 655 - 656 user, _ := bsky.GetUserWithBskyProfile(h.Oauth, r) 657 didOrHandle := chi.URLParam(r, "user") 658 if didOrHandle == "" { ··· 670 671 studySession, err := db.GetStudySessionByRkey(h.Db, ident.DID.String(), rkey) 672 if err != nil { 673 - l.Error("failed to retrieve study session", "err", err) 674 htmx.HxError(w, http.StatusInternalServerError, "Failed to retrieve study session, try again later.") 675 return 676 } 677 678 bskyProfile, err := bsky.GetBskyProfile(ident.DID.String()) 679 if err != nil { 680 - l.Error("failed to retrieve bsky profile for study session", "err", err) 681 htmx.HxError(w, http.StatusInternalServerError, "Failed to retrieve bsky profile, try again later.") 682 return 683 } 684 685 profile, err := db.GetProfile(h.Db, ident.DID.String()) 686 if err != nil { 687 - l.Error("failed to retrieve profile for study session", "err", err) 688 htmx.HxError(w, http.StatusInternalServerError, "Failed to retrieve profile, try again later.") 689 return 690 } ··· 707 } 708 709 func (h *Handler) HandleStudySessionPageCommentFeed(w http.ResponseWriter, r *http.Request) { 710 - l := h.Logger.With("handler", "HandleStudySessionPageCommentFeed") 711 - 712 user, _ := bsky.GetUserWithBskyProfile(h.Oauth, r) 713 714 didOrHandle := chi.URLParam(r, "user") ··· 732 } 733 page, err := strconv.ParseInt(pageStr, 10, 64) 734 if err != nil { 735 - l.Error("failed to parse page value", "err", err) 736 page = 1 737 } 738 if page == 0 { ··· 744 745 commentFeed, err := db.GetCommentsForSession(h.Db, studySessionUri.String(), pageSize+1, int(offset)) 746 if err != nil { 747 - l.Error("failed to get comment feed", "err", err) 748 htmx.HxError(w, http.StatusInternalServerError, "Failed to get comment feed, try again later.") 749 return 750 } ··· 780 781 populatedCommentFeed, err := h.BuildCommentFeed(finalFeed) 782 if err != nil { 783 - l.Error("failed to populate comment feed", "err", err) 784 htmx.HxError(w, http.StatusInternalServerError, "Failed to get comment feed, try again later.") 785 return 786 }
··· 3 import ( 4 "errors" 5 "fmt" 6 + "log" 7 "net/http" 8 "strconv" 9 "time" ··· 117 } 118 119 func (h *Handler) HandleStudySessionFeed(w http.ResponseWriter, r *http.Request) { 120 isFriends, err := strconv.ParseBool(r.URL.Query().Get("friends")) 121 if err != nil { 122 + log.Println("failed to parse friends value:", err) 123 isFriends = false 124 } 125 ··· 129 } 130 page, err := strconv.ParseInt(pageStr, 10, 64) 131 if err != nil { 132 + log.Println("failed to parse page value:", err) 133 page = 1 134 } 135 if page == 0 { ··· 148 if !isFriends { 149 feed, err = db.GetStudySessionFeed(h.Db, pageSize+1, int(offset)) 150 if err != nil { 151 + log.Println("failed to get global feed:", err) 152 htmx.HxError(w, http.StatusInternalServerError, "Failed to get global study session feed, try again later.") 153 return 154 } 155 err = h.GetBskyProfileHydratedSessionFeed(feed) 156 if err != nil { 157 + log.Println("failed to hydrate bsky profiles:", err) 158 htmx.HxError(w, http.StatusInternalServerError, "Failed to get global study session feed, try again later.") 159 return 160 } 161 } else { 162 if fetchUserError != nil { 163 + log.Println("failed to get logged-in user:", err) 164 htmx.HxRedirect(w, "/login") 165 return 166 } 167 168 feed, err = db.GetFriendsStudySessionFeed(h.Db, user.Did, pageSize+1, int(offset)) 169 if err != nil { 170 + log.Println("failed to get global feed:", err) 171 htmx.HxError(w, http.StatusInternalServerError, "Failed to get global study session feed, try again later.") 172 return 173 } 174 err = h.GetBskyProfileHydratedSessionFeed(feed) 175 if err != nil { 176 + log.Println("failed to hydrate bsky profiles:", err) 177 htmx.HxError(w, http.StatusInternalServerError, "Failed to get global study session feed, try again later.") 178 return 179 } ··· 181 182 feed, err = ApplyPendingChanges(h, w, r, feed, PendingStudySessionCreation, PendingStudySessionUpdates, PendingStudySessionDeletion) 183 if err != nil { 184 + log.Printf("failed to save yoten-session after processing pending changes: %v", err) 185 } 186 187 nextPage := 0 ··· 200 } 201 202 func (h *Handler) HandleEditStudySessionPage(w http.ResponseWriter, r *http.Request) { 203 client, err := h.Oauth.AuthorizedClient(r) 204 if err != nil { 205 + log.Println("failed to get authorized client:", err) 206 htmx.HxRedirect(w, "/login") 207 return 208 } 209 210 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 211 if err != nil { 212 + log.Println("failed to get logged-in user:", err) 213 htmx.HxRedirect(w, "/login") 214 return 215 } ··· 217 rkey := chi.URLParam(r, "rkey") 218 studySession, err := db.GetStudySessionByRkey(h.Db, user.Did, rkey) 219 if err != nil { 220 + log.Println("failed to get study session from db:", err) 221 htmx.HxError(w, http.StatusInternalServerError, "Failed to update study session, try again later.") 222 return 223 } 224 225 if user.Did != studySession.Did { 226 + log.Printf("user '%s' does not own record '%s'", user.Did, studySession.Rkey) 227 htmx.HxError(w, http.StatusUnauthorized, "You do not have permissions to edit this study session.") 228 return 229 } ··· 232 case http.MethodGet: 233 userDefinedActivities, err := db.GetActivitiesByDid(h.Db, user.Did) 234 if err != nil { 235 + log.Println("failed to get user-defined activities:", err) 236 } 237 238 resources, err := db.GetResourcesByDid(h.Db, user.Did) 239 if err != nil { 240 + log.Println("failed to get user-defined resources:", err) 241 } 242 243 preDefinedActivities, err := db.GetPredefinedActivities(h.Db) 244 if err != nil { 245 + log.Println("failed to get pre-defined activities:", err) 246 } 247 248 currentResource := studySession.Resource ··· 267 268 languages, err := db.GetProfileLanguages(h.Db, user.Did) 269 if err != nil { 270 + log.Println("failed to fetch profile languages:", err) 271 } 272 273 views.EditStudySessionPage(views.EditStudySessionPageParams{ ··· 280 case http.MethodPost: 281 updatedStudySession, err := h.parseStudySessionForm(r) 282 if err != nil { 283 + log.Println("invalid study session form:", err) 284 htmx.HxError(w, http.StatusBadRequest, "Failed to update study session, ensure all data is valid.") 285 return 286 } ··· 289 updatedStudySession.CreatedAt = studySession.CreatedAt 290 291 if err := db.ValidateStudySession(updatedStudySession); err != nil { 292 + log.Println("invalid study session:", err) 293 switch { 294 case errors.Is(err, db.ErrSessionDescriptionTooLong): 295 htmx.HxError(w, http.StatusBadRequest, "Study session description cannot be more than 256 characters.") ··· 358 SwapRecord: cid, 359 }) 360 if err != nil { 361 + log.Println("failed to update study session record:", err) 362 htmx.HxError(w, http.StatusInternalServerError, "Failed to update study session, try again later.") 363 return 364 } 365 366 err = SavePendingUpdate(h, w, r, PendingStudySessionUpdates, updatedStudySession) 367 if err != nil { 368 + log.Printf("failed to save yoten-session to add pending study session updates: %v", err) 369 } 370 371 if !h.Config.Core.Dev { ··· 377 Set("rkey", rkey), 378 }) 379 if err != nil { 380 + log.Println("failed to enqueue posthog event:", err) 381 } 382 } 383 ··· 386 } 387 388 func (h *Handler) HandleNewStudySessionPage(w http.ResponseWriter, r *http.Request) { 389 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 390 if err != nil { 391 + log.Println("failed to get logged-in user:", err) 392 htmx.HxRedirect(w, "/login") 393 return 394 } 395 396 client, err := h.Oauth.AuthorizedClient(r) 397 if err != nil { 398 + log.Println("failed to get authorized client:", err) 399 htmx.HxRedirect(w, "/login") 400 return 401 } ··· 404 case http.MethodGet: 405 profile, err := db.GetProfile(h.Db, user.Did) 406 if err != nil { 407 + log.Printf("failed to find %s in db: %s", user.Did, err) 408 htmx.HxError(w, http.StatusNotFound, "Failed to find user.") 409 return 410 } 411 412 userDefinedActivities, err := db.GetActivitiesByDid(h.Db, user.Did) 413 if err != nil { 414 + log.Println("failed to get user-defined activities:", err) 415 } 416 preDefinedActivities, err := db.GetPredefinedActivities(h.Db) 417 if err != nil { 418 + log.Println("failed to get pre-defined activities:", err) 419 } 420 activeActivities := utils.Filter(userDefinedActivities, func(activity db.Activity) bool { 421 return activity.Status != db.Deleted ··· 424 425 resources, err := db.GetResourcesByDid(h.Db, user.Did) 426 if err != nil { 427 + log.Println("failed to get user-defined resources:", err) 428 } 429 activeResources := utils.Filter(resources, func(resource db.Resource) bool { 430 return resource.Status != db.Deleted ··· 440 case http.MethodPost: 441 newStudySession, err := h.parseStudySessionForm(r) 442 if err != nil { 443 + log.Println("invalid study session form:", err) 444 htmx.HxError(w, http.StatusBadRequest, "Failed to update study session, ensure all data is valid.") 445 return 446 } ··· 459 } 460 461 if err := db.ValidateStudySession(newStudySession); err != nil { 462 + log.Println("invalid study session:", err) 463 switch { 464 case errors.Is(err, db.ErrSessionDescriptionTooLong): 465 htmx.HxError(w, http.StatusBadRequest, "Study session description cannot be more than 256 characters.") ··· 521 }, 522 }) 523 if err != nil { 524 + log.Println("failed to create study session record:", err) 525 htmx.HxError(w, http.StatusInternalServerError, "Failed to create study session, try again later.") 526 return 527 } 528 529 err = SavePendingCreate(h, w, r, PendingStudySessionCreation, newStudySession) 530 if err != nil { 531 + log.Printf("failed to save yoten-session to add pending study session creation: %v", err) 532 } 533 534 if !h.Config.Core.Dev { ··· 545 Set("date_is_today", newStudySession.Date.Truncate(24*time.Hour).Equal(time.Now().UTC().In(loc).Truncate(24*time.Hour))), 546 }) 547 if err != nil { 548 + log.Println("failed to enqueue posthog event:", err) 549 } 550 } 551 ··· 554 } 555 556 func (h *Handler) HandleDeleteStudySession(w http.ResponseWriter, r *http.Request) { 557 user := h.Oauth.GetUser(r) 558 if user == nil { 559 + log.Println("failed to get logged-in user") 560 htmx.HxRedirect(w, "/login") 561 return 562 } 563 564 client, err := h.Oauth.AuthorizedClient(r) 565 if err != nil { 566 + log.Println("failed to get authorized client:", err) 567 htmx.HxError(w, http.StatusUnauthorized, "Failed to delete study session, try again later.") 568 return 569 } ··· 572 case http.MethodDelete: 573 err := r.ParseForm() 574 if err != nil { 575 + log.Println("failed to parse study session delete form:", err) 576 htmx.HxError(w, http.StatusBadRequest, "Failed to delete study session, try again later.") 577 return 578 } ··· 580 rkey := chi.URLParam(r, "rkey") 581 studySession, err := db.GetStudySessionByRkey(h.Db, user.Did, rkey) 582 if err != nil { 583 + log.Println("failed to get study session from db:", err) 584 htmx.HxError(w, http.StatusInternalServerError, "Failed to delete study session, try again later.") 585 return 586 } 587 588 if user.Did != studySession.Did { 589 + log.Println("failed to delete study session: user does not own record") 590 htmx.HxError(w, http.StatusUnauthorized, "Failed to delete study session, try again later.") 591 return 592 } ··· 597 Rkey: rkey, 598 }) 599 if err != nil { 600 + log.Println("failed to delete study session from PDS:", err) 601 htmx.HxError(w, http.StatusInternalServerError, "Failed to delete study session, try again later.") 602 return 603 } 604 605 err = SavePendingDelete(h, w, r, PendingStudySessionDeletion, studySession) 606 if err != nil { 607 + log.Printf("failed to save yoten-session to add pending study session deletion: %v", err) 608 } 609 610 if !h.Config.Core.Dev { ··· 617 Set("session_age_seconds", time.Since(studySession.CreatedAt).Seconds()), 618 }) 619 if err != nil { 620 + log.Println("failed to enqueue posthog event:", err) 621 } 622 } 623 ··· 644 } 645 646 func (h *Handler) HandleStudySessionPage(w http.ResponseWriter, r *http.Request) { 647 user, _ := bsky.GetUserWithBskyProfile(h.Oauth, r) 648 didOrHandle := chi.URLParam(r, "user") 649 if didOrHandle == "" { ··· 661 662 studySession, err := db.GetStudySessionByRkey(h.Db, ident.DID.String(), rkey) 663 if err != nil { 664 + log.Println("failed to retrieve study session:", err) 665 htmx.HxError(w, http.StatusInternalServerError, "Failed to retrieve study session, try again later.") 666 return 667 } 668 669 bskyProfile, err := bsky.GetBskyProfile(ident.DID.String()) 670 if err != nil { 671 + log.Println("failed to retrieve bsky profile for study session:", err) 672 htmx.HxError(w, http.StatusInternalServerError, "Failed to retrieve bsky profile, try again later.") 673 return 674 } 675 676 profile, err := db.GetProfile(h.Db, ident.DID.String()) 677 if err != nil { 678 + log.Println("failed to retrieve profile for study session:", err) 679 htmx.HxError(w, http.StatusInternalServerError, "Failed to retrieve profile, try again later.") 680 return 681 } ··· 698 } 699 700 func (h *Handler) HandleStudySessionPageCommentFeed(w http.ResponseWriter, r *http.Request) { 701 user, _ := bsky.GetUserWithBskyProfile(h.Oauth, r) 702 703 didOrHandle := chi.URLParam(r, "user") ··· 721 } 722 page, err := strconv.ParseInt(pageStr, 10, 64) 723 if err != nil { 724 + log.Println("failed to parse page value:", err) 725 page = 1 726 } 727 if page == 0 { ··· 733 734 commentFeed, err := db.GetCommentsForSession(h.Db, studySessionUri.String(), pageSize+1, int(offset)) 735 if err != nil { 736 + log.Println("failed to get comment feed:", err) 737 htmx.HxError(w, http.StatusInternalServerError, "Failed to get comment feed, try again later.") 738 return 739 } ··· 769 770 populatedCommentFeed, err := h.BuildCommentFeed(finalFeed) 771 if err != nil { 772 + log.Println("failed to populate comment feed:", err) 773 htmx.HxError(w, http.StatusInternalServerError, "Failed to get comment feed, try again later.") 774 return 775 }
REVERTED
internal/server/handlers/resource.go
··· 3 import ( 4 "errors" 5 "fmt" 6 "net/http" 7 "time" 8 ··· 62 } 63 64 func (h *Handler) HandleNewResourcePage(w http.ResponseWriter, r *http.Request) { 65 - l := h.Logger.With("handler", "HandleNewResourcePage") 66 - 67 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 68 if err != nil { 69 - l.Error("failed to get logged-in user", "err", err) 70 htmx.HxRedirect(w, "/login") 71 return 72 } ··· 80 case http.MethodPost: 81 client, err := h.Oauth.AuthorizedClient(r) 82 if err != nil { 83 - l.Error("failed to get authorized client", "err", err) 84 htmx.HxRedirect(w, "/login") 85 return 86 } 87 88 newResource, err := parseResourceForm(r) 89 if err != nil { 90 - l.Error("invalid resource form", "err", err) 91 htmx.HxError(w, http.StatusBadRequest, "Failed to create resource, ensure all fields contain valid data.") 92 return 93 } ··· 96 newResource.CreatedAt = time.Now().UTC() 97 98 if err := db.ValidateResource(newResource); err != nil { 99 - l.Error("invalid resource definition", "err", err) 100 switch { 101 case errors.Is(err, db.ErrResourceTitleEmpty): 102 htmx.HxError(w, http.StatusBadRequest, "Resource must have a title.") ··· 119 if newResource.Link != nil { 120 linkCheckResult := google.CheckResourceLinkSafety(*newResource.Link) 121 if linkCheckResult.Err != nil { 122 - l.Error("invalid resource definition", "err", linkCheckResult.Err) 123 switch { 124 case errors.Is(linkCheckResult.Err, google.ErrResourceLinkSketchy): 125 if !h.Config.Core.Dev { ··· 187 }, 188 }) 189 if err != nil { 190 - l.Error("failed to create resource record", "err", err) 191 htmx.HxError(w, http.StatusInternalServerError, "Failed to create resource, try again later.") 192 return 193 } 194 195 err = SavePendingCreate(h, w, r, PendingResourceCreation, newResource) 196 if err != nil { 197 - l.Error("failed to save yoten-session to add pending resource creation", "err", err) 198 } 199 200 if !h.Config.Core.Dev { ··· 210 Set("link_provided", newResource.Link != nil), 211 }) 212 if err != nil { 213 - l.Error("failed to enqueue posthog event", "err", err) 214 } 215 } 216 ··· 219 } 220 221 func (h *Handler) HandleDeleteResource(w http.ResponseWriter, r *http.Request) { 222 - l := h.Logger.With("handler", "HandleDeleteResource") 223 - 224 user := h.Oauth.GetUser(r) 225 if user == nil { 226 - l.Error("failed to get logged-in user") 227 htmx.HxRedirect(w, "/login") 228 return 229 } 230 client, err := h.Oauth.AuthorizedClient(r) 231 if err != nil { 232 - l.Error("failed to get authorized client", "err", err) 233 htmx.HxError(w, http.StatusUnauthorized, "Failed to delete resource, try again later.") 234 return 235 } ··· 239 rkey := chi.URLParam(r, "rkey") 240 resource, err := db.GetResourceByRkey(h.Db, user.Did, rkey) 241 if err != nil { 242 - l.Error("failed to get resource from db", "err", err) 243 htmx.HxError(w, http.StatusInternalServerError, "Failed to delete resource, try again later.") 244 return 245 } 246 247 if user.Did != resource.Did { 248 - l.Error("user does not own record", "did", user.Did, "resourceDid", resource.Did) 249 htmx.HxError(w, http.StatusUnauthorized, "You do not have permissions to delete this resource.") 250 return 251 } ··· 256 Rkey: resource.Rkey, 257 }) 258 if err != nil { 259 - l.Error("failed to delete resource from PDS", "err", err) 260 htmx.HxError(w, http.StatusInternalServerError, "Failed to delete resource, try again later.") 261 return 262 } 263 264 err = SavePendingDelete(h, w, r, PendingResourceDeletion, resource) 265 if err != nil { 266 - l.Error("failed to save yoten-session to add pending resource deletion", "err", err) 267 } 268 269 if !h.Config.Core.Dev { ··· 275 Set("resource_type", resource.Type), 276 }) 277 if err != nil { 278 - l.Error("failed to enqueue posthog event", "err", err) 279 } 280 } 281 ··· 284 } 285 286 func (h *Handler) HandleEditResourcePage(w http.ResponseWriter, r *http.Request) { 287 - l := h.Logger.With("handler", "HandleEditResourcePage") 288 - 289 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 290 if err != nil { 291 - l.Error("failed to get logged-in user", "err", err) 292 htmx.HxRedirect(w, "/login") 293 return 294 } ··· 296 rkey := chi.URLParam(r, "rkey") 297 resource, err := db.GetResourceByRkey(h.Db, user.Did, rkey) 298 if err != nil { 299 - l.Error("failed to get resource from db", "err", err) 300 htmx.HxError(w, http.StatusInternalServerError, "Failed to update resource, try again later.") 301 return 302 } 303 304 if user.Did != resource.Did { 305 - l.Error("user does not own record", "did", user.Did, "resourceDid", resource.Did) 306 htmx.HxError(w, http.StatusUnauthorized, "You do not have permissions to edit this resource.") 307 return 308 } ··· 317 case http.MethodPost: 318 client, err := h.Oauth.AuthorizedClient(r) 319 if err != nil { 320 - l.Error("failed to get authorized client", "err", err) 321 htmx.HxRedirect(w, "/login") 322 return 323 } 324 325 updatedResource, err := parseResourceForm(r) 326 if err != nil { 327 - l.Error("invalid resource form", "err", err) 328 htmx.HxError(w, http.StatusBadRequest, "Failed to create resource, ensure all fields contain valid data.") 329 return 330 } ··· 333 updatedResource.CreatedAt = resource.CreatedAt 334 335 if err := db.ValidateResource(updatedResource); err != nil { 336 - l.Error("invalid resource definition", "err", err) 337 switch { 338 case errors.Is(err, db.ErrResourceTitleEmpty): 339 htmx.HxError(w, http.StatusBadRequest, "Resource must have a title.") ··· 357 if updatedResource.Link != nil && (resource.Link == nil || *updatedResource.Link != *resource.Link) { 358 linkCheckResult := google.CheckResourceLinkSafety(*updatedResource.Link) 359 if linkCheckResult.Err != nil { 360 - l.Error("invalid resource link", "link", resource.Link, "threatType", linkCheckResult.ThreatType, "err", linkCheckResult.Err) 361 switch { 362 case errors.Is(linkCheckResult.Err, google.ErrResourceLinkSketchy): 363 if !h.Config.Core.Dev { ··· 432 SwapRecord: cid, 433 }) 434 if err != nil { 435 - l.Error("failed to update resource record", "err", err) 436 htmx.HxError(w, http.StatusInternalServerError, "Failed to update resource, try again later.") 437 return 438 } 439 440 err = SavePendingUpdate(h, w, r, PendingResourceUpdates, updatedResource) 441 if err != nil { 442 - l.Error("failed to save yoten-session to add pending resource updates", "err", err) 443 } 444 445 if !h.Config.Core.Dev { ··· 455 Set("link_provided", updatedResource.Link != nil), 456 }) 457 if err != nil { 458 - l.Error("failed to enqueue posthog event", "err", err) 459 } 460 } 461
··· 3 import ( 4 "errors" 5 "fmt" 6 + "log" 7 "net/http" 8 "time" 9 ··· 63 } 64 65 func (h *Handler) HandleNewResourcePage(w http.ResponseWriter, r *http.Request) { 66 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 67 if err != nil { 68 + log.Println("failed to get logged-in user:", err) 69 htmx.HxRedirect(w, "/login") 70 return 71 } ··· 79 case http.MethodPost: 80 client, err := h.Oauth.AuthorizedClient(r) 81 if err != nil { 82 + log.Println("failed to get authorized client:", err) 83 htmx.HxRedirect(w, "/login") 84 return 85 } 86 87 newResource, err := parseResourceForm(r) 88 if err != nil { 89 + log.Println("invalid resource form:", err) 90 htmx.HxError(w, http.StatusBadRequest, "Failed to create resource, ensure all fields contain valid data.") 91 return 92 } ··· 95 newResource.CreatedAt = time.Now().UTC() 96 97 if err := db.ValidateResource(newResource); err != nil { 98 + log.Println("invalid resource definition:", err) 99 switch { 100 case errors.Is(err, db.ErrResourceTitleEmpty): 101 htmx.HxError(w, http.StatusBadRequest, "Resource must have a title.") ··· 118 if newResource.Link != nil { 119 linkCheckResult := google.CheckResourceLinkSafety(*newResource.Link) 120 if linkCheckResult.Err != nil { 121 + log.Println("invalid resource definition:", linkCheckResult.Err) 122 switch { 123 case errors.Is(linkCheckResult.Err, google.ErrResourceLinkSketchy): 124 if !h.Config.Core.Dev { ··· 186 }, 187 }) 188 if err != nil { 189 + log.Println("failed to create resource record:", err) 190 htmx.HxError(w, http.StatusInternalServerError, "Failed to create resource, try again later.") 191 return 192 } 193 194 err = SavePendingCreate(h, w, r, PendingResourceCreation, newResource) 195 if err != nil { 196 + log.Printf("failed to save yoten-session to add pending resource creation: %v", err) 197 } 198 199 if !h.Config.Core.Dev { ··· 209 Set("link_provided", newResource.Link != nil), 210 }) 211 if err != nil { 212 + log.Println("failed to enqueue posthog event:", err) 213 } 214 } 215 ··· 218 } 219 220 func (h *Handler) HandleDeleteResource(w http.ResponseWriter, r *http.Request) { 221 user := h.Oauth.GetUser(r) 222 if user == nil { 223 + log.Println("failed to get logged-in user") 224 htmx.HxRedirect(w, "/login") 225 return 226 } 227 client, err := h.Oauth.AuthorizedClient(r) 228 if err != nil { 229 + log.Println("failed to get authorized client:", err) 230 htmx.HxError(w, http.StatusUnauthorized, "Failed to delete resource, try again later.") 231 return 232 } ··· 236 rkey := chi.URLParam(r, "rkey") 237 resource, err := db.GetResourceByRkey(h.Db, user.Did, rkey) 238 if err != nil { 239 + log.Println("failed to get resource from db:", err) 240 htmx.HxError(w, http.StatusInternalServerError, "Failed to delete resource, try again later.") 241 return 242 } 243 244 if user.Did != resource.Did { 245 + log.Printf("user '%s' does not own record '%s'", user.Did, rkey) 246 htmx.HxError(w, http.StatusUnauthorized, "You do not have permissions to delete this resource.") 247 return 248 } ··· 253 Rkey: resource.Rkey, 254 }) 255 if err != nil { 256 + log.Println("failed to delete resource from PDS:", err) 257 htmx.HxError(w, http.StatusInternalServerError, "Failed to delete resource, try again later.") 258 return 259 } 260 261 err = SavePendingDelete(h, w, r, PendingResourceDeletion, resource) 262 if err != nil { 263 + log.Printf("failed to save yoten-session to add pending resource deletion: %v", err) 264 } 265 266 if !h.Config.Core.Dev { ··· 272 Set("resource_type", resource.Type), 273 }) 274 if err != nil { 275 + log.Println("failed to enqueue posthog event:", err) 276 } 277 } 278 ··· 281 } 282 283 func (h *Handler) HandleEditResourcePage(w http.ResponseWriter, r *http.Request) { 284 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 285 if err != nil { 286 + log.Println("failed to get logged-in user:", err) 287 htmx.HxRedirect(w, "/login") 288 return 289 } ··· 291 rkey := chi.URLParam(r, "rkey") 292 resource, err := db.GetResourceByRkey(h.Db, user.Did, rkey) 293 if err != nil { 294 + log.Println("failed to get resource from db:", err) 295 htmx.HxError(w, http.StatusInternalServerError, "Failed to update resource, try again later.") 296 return 297 } 298 299 if user.Did != resource.Did { 300 + log.Printf("user '%s' does not own record '%s'", user.Did, rkey) 301 htmx.HxError(w, http.StatusUnauthorized, "You do not have permissions to edit this resource.") 302 return 303 } ··· 312 case http.MethodPost: 313 client, err := h.Oauth.AuthorizedClient(r) 314 if err != nil { 315 + log.Println("failed to get authorized client:", err) 316 htmx.HxRedirect(w, "/login") 317 return 318 } 319 320 updatedResource, err := parseResourceForm(r) 321 if err != nil { 322 + log.Println("invalid resource form:", err) 323 htmx.HxError(w, http.StatusBadRequest, "Failed to create resource, ensure all fields contain valid data.") 324 return 325 } ··· 328 updatedResource.CreatedAt = resource.CreatedAt 329 330 if err := db.ValidateResource(updatedResource); err != nil { 331 + log.Println("invalid resource definition:", err) 332 switch { 333 case errors.Is(err, db.ErrResourceTitleEmpty): 334 htmx.HxError(w, http.StatusBadRequest, "Resource must have a title.") ··· 352 if updatedResource.Link != nil && (resource.Link == nil || *updatedResource.Link != *resource.Link) { 353 linkCheckResult := google.CheckResourceLinkSafety(*updatedResource.Link) 354 if linkCheckResult.Err != nil { 355 + log.Println("invalid resource definition:", linkCheckResult.Err) 356 switch { 357 case errors.Is(linkCheckResult.Err, google.ErrResourceLinkSketchy): 358 if !h.Config.Core.Dev { ··· 427 SwapRecord: cid, 428 }) 429 if err != nil { 430 + log.Println("failed to update resource record:", err) 431 htmx.HxError(w, http.StatusInternalServerError, "Failed to update resource, try again later.") 432 return 433 } 434 435 err = SavePendingUpdate(h, w, r, PendingResourceUpdates, updatedResource) 436 if err != nil { 437 + log.Printf("failed to save yoten-session to add pending resource updates: %v", err) 438 } 439 440 if !h.Config.Core.Dev { ··· 450 Set("link_provided", updatedResource.Link != nil), 451 }) 452 if err != nil { 453 + log.Println("failed to enqueue posthog event:", err) 454 } 455 } 456
REVERTED
internal/server/handlers/comment.go
··· 1 package handlers 2 3 import ( 4 "net/http" 5 "strings" 6 "time" ··· 22 ) 23 24 func (h *Handler) HandleNewComment(w http.ResponseWriter, r *http.Request) { 25 - l := h.Logger.With("handler", "HandleNewComment") 26 - 27 client, err := h.Oauth.AuthorizedClient(r) 28 if err != nil { 29 - l.Error("failed to get authorized client", "err", err) 30 htmx.HxRedirect(w, "/login") 31 return 32 } 33 34 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 35 if err != nil { 36 - l.Error("failed to get logged-in user", "err", err) 37 htmx.HxRedirect(w, "/login") 38 return 39 } 40 41 profile, err := db.GetProfile(h.Db, user.Did) 42 if err != nil { 43 - l.Error("failed to get logged-in user", "err", err) 44 htmx.HxRedirect(w, "/login") 45 return 46 } 47 48 err = r.ParseForm() 49 if err != nil { 50 - l.Error("invalid comment form", "err", err) 51 htmx.HxError(w, http.StatusBadRequest, "Unable to process comment, please try again later.") 52 return 53 } 54 55 commentBody := r.FormValue("comment") 56 if len(strings.TrimSpace(commentBody)) == 0 { 57 - l.Error("invalid comment form: missing comment body") 58 htmx.HxError(w, http.StatusBadRequest, "Comment cannot be empty.") 59 return 60 } 61 62 studySessionUri := r.FormValue("study_session_uri") 63 if len(studySessionUri) == 0 { 64 - l.Error("invalid comment form: missing study session Uri") 65 htmx.HxError(w, http.StatusBadRequest, "Unable to create comment, please try again later.") 66 return 67 } ··· 101 }, 102 }) 103 if err != nil { 104 - l.Error("failed to create comment record", "err", err) 105 htmx.HxError(w, http.StatusInternalServerError, "Failed to create comment, try again later.") 106 return 107 } ··· 122 123 err = h.Posthog.Enqueue(event) 124 if err != nil { 125 - l.Error("failed to enqueue posthog event", "err", err) 126 } 127 } 128 ··· 154 } 155 156 func (h *Handler) HandleDeleteComment(w http.ResponseWriter, r *http.Request) { 157 - l := h.Logger.With("handler", "HandleDeleteComment") 158 - 159 user := h.Oauth.GetUser(r) 160 if user == nil { 161 - l.Error("failed to get logged-in user") 162 htmx.HxRedirect(w, "/login") 163 return 164 } 165 client, err := h.Oauth.AuthorizedClient(r) 166 if err != nil { 167 - l.Error("failed to get authorized client", "err", err) 168 htmx.HxRedirect(w, "/login") 169 return 170 } ··· 174 rkey := chi.URLParam(r, "rkey") 175 comment, err := db.GetCommentByRkey(h.Db, user.Did, rkey) 176 if err != nil { 177 - l.Error("failed to get comment from db", "err", err) 178 htmx.HxError(w, http.StatusInternalServerError, "Failed to delete comment, try again later.") 179 return 180 } 181 182 if user.Did != comment.Did { 183 - l.Error("user does not own record", "did", user.Did, "commentDid", comment.Did) 184 htmx.HxError(w, http.StatusUnauthorized, "You do not have permissions to delete this comment.") 185 return 186 } ··· 191 Rkey: comment.Rkey, 192 }) 193 if err != nil { 194 - l.Error("failed to delete comment from PDS", "err", err) 195 htmx.HxError(w, http.StatusInternalServerError, "Failed to delete comment, try again later.") 196 return 197 } ··· 212 213 err = h.Posthog.Enqueue(event) 214 if err != nil { 215 - l.Error("failed to enqueue posthog event", "err", err) 216 } 217 } 218 ··· 221 } 222 223 func (h *Handler) HandleEditCommentPage(w http.ResponseWriter, r *http.Request) { 224 - l := h.Logger.With("handler", "HandleEditCommentPage") 225 - 226 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 227 if err != nil { 228 - l.Error("failed to get logged-in user", "err", err) 229 htmx.HxRedirect(w, "/login") 230 return 231 } ··· 233 rkey := chi.URLParam(r, "rkey") 234 comment, err := db.GetCommentByRkey(h.Db, user.Did, rkey) 235 if err != nil { 236 - l.Error("failed to get comment from db", "err", err) 237 htmx.HxError(w, http.StatusInternalServerError, "Failed to update comment, try again later.") 238 return 239 } 240 241 if user.Did != comment.Did { 242 - l.Error("user does not own record", "did", user.Did, "commentDid", comment.Did) 243 htmx.HxError(w, http.StatusUnauthorized, "You do not have permissions to edit this comment.") 244 return 245 } ··· 250 case http.MethodPost: 251 client, err := h.Oauth.AuthorizedClient(r) 252 if err != nil { 253 - l.Error("failed to get authorized client", "err", err) 254 htmx.HxRedirect(w, "/login") 255 return 256 } 257 258 err = r.ParseForm() 259 if err != nil { 260 - l.Error("invalid comment form", "err", err) 261 htmx.HxError(w, http.StatusBadRequest, "Unable to process comment, please try again later.") 262 return 263 } 264 265 commentBody := r.FormValue("comment") 266 if len(strings.TrimSpace(commentBody)) == 0 { 267 - l.Error("invalid comment form: missing comment body") 268 htmx.HxError(w, http.StatusBadRequest, "Comment cannot be empty.") 269 return 270 } ··· 308 SwapRecord: cid, 309 }) 310 if err != nil { 311 - l.Error("failed to update study session record", "err", err) 312 htmx.HxError(w, http.StatusInternalServerError, "Failed to update comment, try again later.") 313 return 314 } ··· 329 330 err = h.Posthog.Enqueue(event) 331 if err != nil { 332 - l.Error("failed to enqueue posthog event", "err", err) 333 } 334 } 335 ··· 392 } 393 394 func (h *Handler) HandleReply(w http.ResponseWriter, r *http.Request) { 395 - l := h.Logger.With("handler", "HandleReply") 396 - 397 user := h.Oauth.GetUser(r) 398 if user == nil { 399 - l.Error("failed to get logged-in user") 400 htmx.HxRedirect(w, "/login") 401 return 402 } ··· 404 studySessionUri := r.URL.Query().Get("root") 405 parentCommentUri := r.URL.Query().Get("parent") 406 if len(studySessionUri) == 0 || len(parentCommentUri) == 0 { 407 - l.Error("invalid reply form: study session uri or parent comment uri is empty") 408 htmx.HxError(w, http.StatusBadRequest, "Unable to process comment, please try again later.") 409 return 410 } ··· 416 } 417 418 func (h *Handler) HandleCancelCommentEdit(w http.ResponseWriter, r *http.Request) { 419 - l := h.Logger.With("handler", "HandleCancelCommentEdit") 420 - 421 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 422 if err != nil { 423 - l.Error("failed to get logged-in user", "err", err) 424 htmx.HxRedirect(w, "/login") 425 return 426 } ··· 428 rkey := chi.URLParam(r, "rkey") 429 comment, err := db.GetCommentByRkey(h.Db, user.Did, rkey) 430 if err != nil { 431 - l.Error("failed to get comment from db", "err", err) 432 htmx.HxError(w, http.StatusInternalServerError, "Failed to update comment, try again later.") 433 return 434 }
··· 1 package handlers 2 3 import ( 4 + "log" 5 "net/http" 6 "strings" 7 "time" ··· 23 ) 24 25 func (h *Handler) HandleNewComment(w http.ResponseWriter, r *http.Request) { 26 client, err := h.Oauth.AuthorizedClient(r) 27 if err != nil { 28 + log.Println("failed to get authorized client:", err) 29 htmx.HxRedirect(w, "/login") 30 return 31 } 32 33 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 34 if err != nil { 35 + log.Println("failed to get logged-in user:", err) 36 htmx.HxRedirect(w, "/login") 37 return 38 } 39 40 profile, err := db.GetProfile(h.Db, user.Did) 41 if err != nil { 42 + log.Println("failed to get logged-in user:", err) 43 htmx.HxRedirect(w, "/login") 44 return 45 } 46 47 err = r.ParseForm() 48 if err != nil { 49 + log.Println("invalid comment form:", err) 50 htmx.HxError(w, http.StatusBadRequest, "Unable to process comment, please try again later.") 51 return 52 } 53 54 commentBody := r.FormValue("comment") 55 if len(strings.TrimSpace(commentBody)) == 0 { 56 + log.Println("invalid comment form: missing comment body") 57 htmx.HxError(w, http.StatusBadRequest, "Comment cannot be empty.") 58 return 59 } 60 61 studySessionUri := r.FormValue("study_session_uri") 62 if len(studySessionUri) == 0 { 63 + log.Println("invalid comment form: missing study session Uri") 64 htmx.HxError(w, http.StatusBadRequest, "Unable to create comment, please try again later.") 65 return 66 } ··· 100 }, 101 }) 102 if err != nil { 103 + log.Println("failed to create comment record:", err) 104 htmx.HxError(w, http.StatusInternalServerError, "Failed to create comment, try again later.") 105 return 106 } ··· 121 122 err = h.Posthog.Enqueue(event) 123 if err != nil { 124 + log.Println("failed to enqueue posthog event:", err) 125 } 126 } 127 ··· 153 } 154 155 func (h *Handler) HandleDeleteComment(w http.ResponseWriter, r *http.Request) { 156 user := h.Oauth.GetUser(r) 157 if user == nil { 158 + log.Println("failed to get logged-in user") 159 htmx.HxRedirect(w, "/login") 160 return 161 } 162 client, err := h.Oauth.AuthorizedClient(r) 163 if err != nil { 164 + log.Println("failed to get authorized client:", err) 165 htmx.HxRedirect(w, "/login") 166 return 167 } ··· 171 rkey := chi.URLParam(r, "rkey") 172 comment, err := db.GetCommentByRkey(h.Db, user.Did, rkey) 173 if err != nil { 174 + log.Println("failed to get comment from db:", err) 175 htmx.HxError(w, http.StatusInternalServerError, "Failed to delete comment, try again later.") 176 return 177 } 178 179 if user.Did != comment.Did { 180 + log.Printf("user '%s' does not own record '%s'", user.Did, rkey) 181 htmx.HxError(w, http.StatusUnauthorized, "You do not have permissions to delete this comment.") 182 return 183 } ··· 188 Rkey: comment.Rkey, 189 }) 190 if err != nil { 191 + log.Println("failed to delete comment from PDS:", err) 192 htmx.HxError(w, http.StatusInternalServerError, "Failed to delete comment, try again later.") 193 return 194 } ··· 209 210 err = h.Posthog.Enqueue(event) 211 if err != nil { 212 + log.Println("failed to enqueue posthog event:", err) 213 } 214 } 215 ··· 218 } 219 220 func (h *Handler) HandleEditCommentPage(w http.ResponseWriter, r *http.Request) { 221 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 222 if err != nil { 223 + log.Println("failed to get logged-in user:", err) 224 htmx.HxRedirect(w, "/login") 225 return 226 } ··· 228 rkey := chi.URLParam(r, "rkey") 229 comment, err := db.GetCommentByRkey(h.Db, user.Did, rkey) 230 if err != nil { 231 + log.Println("failed to get comment from db:", err) 232 htmx.HxError(w, http.StatusInternalServerError, "Failed to update comment, try again later.") 233 return 234 } 235 236 if user.Did != comment.Did { 237 + log.Printf("user '%s' does not own record '%s'", user.Did, rkey) 238 htmx.HxError(w, http.StatusUnauthorized, "You do not have permissions to edit this comment.") 239 return 240 } ··· 245 case http.MethodPost: 246 client, err := h.Oauth.AuthorizedClient(r) 247 if err != nil { 248 + log.Println("failed to get authorized client:", err) 249 htmx.HxRedirect(w, "/login") 250 return 251 } 252 253 err = r.ParseForm() 254 if err != nil { 255 + log.Println("invalid comment form:", err) 256 htmx.HxError(w, http.StatusBadRequest, "Unable to process comment, please try again later.") 257 return 258 } 259 260 commentBody := r.FormValue("comment") 261 if len(strings.TrimSpace(commentBody)) == 0 { 262 + log.Println("invalid comment form: missing comment body") 263 htmx.HxError(w, http.StatusBadRequest, "Comment cannot be empty.") 264 return 265 } ··· 303 SwapRecord: cid, 304 }) 305 if err != nil { 306 + log.Println("failed to update study session record:", err) 307 htmx.HxError(w, http.StatusInternalServerError, "Failed to update comment, try again later.") 308 return 309 } ··· 324 325 err = h.Posthog.Enqueue(event) 326 if err != nil { 327 + log.Println("failed to enqueue posthog event:", err) 328 } 329 } 330 ··· 387 } 388 389 func (h *Handler) HandleReply(w http.ResponseWriter, r *http.Request) { 390 user := h.Oauth.GetUser(r) 391 if user == nil { 392 + log.Println("failed to get logged-in user") 393 htmx.HxRedirect(w, "/login") 394 return 395 } ··· 397 studySessionUri := r.URL.Query().Get("root") 398 parentCommentUri := r.URL.Query().Get("parent") 399 if len(studySessionUri) == 0 || len(parentCommentUri) == 0 { 400 + log.Println("invalid reply form: study session uri or parent comment uri is empty") 401 htmx.HxError(w, http.StatusBadRequest, "Unable to process comment, please try again later.") 402 return 403 } ··· 409 } 410 411 func (h *Handler) HandleCancelCommentEdit(w http.ResponseWriter, r *http.Request) { 412 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 413 if err != nil { 414 + log.Println("failed to get logged-in user:", err) 415 htmx.HxRedirect(w, "/login") 416 return 417 } ··· 419 rkey := chi.URLParam(r, "rkey") 420 comment, err := db.GetCommentByRkey(h.Db, user.Did, rkey) 421 if err != nil { 422 + log.Println("failed to get comment from db:", err) 423 htmx.HxError(w, http.StatusInternalServerError, "Failed to update comment, try again later.") 424 return 425 }
REVERTED
internal/server/handlers/follow.go
··· 1 package handlers 2 3 import ( 4 "net/http" 5 "time" 6 ··· 19 ) 20 21 func (h *Handler) HandleFollow(w http.ResponseWriter, r *http.Request) { 22 - l := h.Logger.With("handler", "HandleFollow") 23 - 24 client, err := h.Oauth.AuthorizedClient(r) 25 if err != nil { 26 - l.Error("failed to get authorized client", "err", err) 27 htmx.HxRedirect(w, "/login") 28 return 29 } 30 31 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 32 if err != nil { 33 - l.Error("failed to get logged-in user", "err", err) 34 htmx.HxRedirect(w, "/login") 35 return 36 } ··· 43 44 subjectIdent, err := h.IdResolver.ResolveIdent(r.Context(), subject) 45 if err != nil { 46 - l.Error("failed to follow, invalid did", "err", err) 47 htmx.HxError(w, http.StatusBadRequest, "Failed to follow profile, try again later.") 48 return 49 } 50 51 if user.Did == subjectIdent.DID.String() { 52 - l.Error("failed to follow, cannot follow yourself") 53 htmx.HxError(w, http.StatusBadRequest, "You cannot follow yourself.") 54 return 55 } ··· 69 }}, 70 }) 71 if err != nil { 72 - l.Error("failed to create follow record", "err", err) 73 htmx.HxError(w, http.StatusInternalServerError, "Failed to follow profile, try again later.") 74 return 75 } ··· 85 Set("is_mutual_follow", followStatus == db.IsMutual), 86 }) 87 if err != nil { 88 - l.Error("failed to enqueue posthog event", "err", err) 89 } 90 } 91 ··· 96 case http.MethodDelete: 97 follow, err := db.GetFollow(h.Db, user.Did, subjectIdent.DID.String()) 98 if err != nil { 99 - l.Error("failed to get follow relationship", "err", err) 100 htmx.HxError(w, http.StatusInternalServerError, "Failed to unfollow profile, try again later.") 101 return 102 } ··· 107 Rkey: follow.Rkey, 108 }) 109 if err != nil { 110 - l.Error("failed to delete follow record", "err", err) 111 htmx.HxError(w, http.StatusInternalServerError, "Failed to unfollow profile, try again later.") 112 return 113 } ··· 120 Set("subject_did", subjectIdent.DID.String()), 121 }) 122 if err != nil { 123 - l.Error("failed to enqueue posthog event", "err", err) 124 } 125 } 126
··· 1 package handlers 2 3 import ( 4 + "log" 5 "net/http" 6 "time" 7 ··· 20 ) 21 22 func (h *Handler) HandleFollow(w http.ResponseWriter, r *http.Request) { 23 client, err := h.Oauth.AuthorizedClient(r) 24 if err != nil { 25 + log.Println("failed to get authorized client:", err) 26 htmx.HxRedirect(w, "/login") 27 return 28 } 29 30 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 31 if err != nil { 32 + log.Println("failed to get logged-in user:", err) 33 htmx.HxRedirect(w, "/login") 34 return 35 } ··· 42 43 subjectIdent, err := h.IdResolver.ResolveIdent(r.Context(), subject) 44 if err != nil { 45 + log.Println("failed to follow, invalid did:", err) 46 htmx.HxError(w, http.StatusBadRequest, "Failed to follow profile, try again later.") 47 return 48 } 49 50 if user.Did == subjectIdent.DID.String() { 51 + log.Println("failed to follow, cannot follow yourself") 52 htmx.HxError(w, http.StatusBadRequest, "You cannot follow yourself.") 53 return 54 } ··· 68 }}, 69 }) 70 if err != nil { 71 + log.Println("failed to create follow record:", err) 72 htmx.HxError(w, http.StatusInternalServerError, "Failed to follow profile, try again later.") 73 return 74 } ··· 84 Set("is_mutual_follow", followStatus == db.IsMutual), 85 }) 86 if err != nil { 87 + log.Println("failed to enqueue posthog event:", err) 88 } 89 } 90 ··· 95 case http.MethodDelete: 96 follow, err := db.GetFollow(h.Db, user.Did, subjectIdent.DID.String()) 97 if err != nil { 98 + log.Println("failed to get follow relationship:", err) 99 htmx.HxError(w, http.StatusInternalServerError, "Failed to unfollow profile, try again later.") 100 return 101 } ··· 106 Rkey: follow.Rkey, 107 }) 108 if err != nil { 109 + log.Println("failed to delete follow record:", err) 110 htmx.HxError(w, http.StatusInternalServerError, "Failed to unfollow profile, try again later.") 111 return 112 } ··· 119 Set("subject_did", subjectIdent.DID.String()), 120 }) 121 if err != nil { 122 + log.Println("failed to enqueue posthog event:", err) 123 } 124 } 125
REVERTED
internal/server/handlers/activity.go
··· 3 import ( 4 "errors" 5 "fmt" 6 "net/http" 7 "time" 8 ··· 54 } 55 56 func (h *Handler) HandleNewActivityPage(w http.ResponseWriter, r *http.Request) { 57 - l := h.Logger.With("handler", "HandleNewActivityPage") 58 - 59 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 60 if err != nil { 61 - l.Error("failed to get logged-in user", "err", err) 62 htmx.HxRedirect(w, "/login") 63 return 64 } ··· 72 case http.MethodPost: 73 client, err := h.Oauth.AuthorizedClient(r) 74 if err != nil { 75 - l.Error("failed to get authorized client", "err", err) 76 htmx.HxRedirect(w, "/login") 77 return 78 } 79 80 newActivity, err := parseActivityForm(r) 81 if err != nil { 82 - l.Error("invalid activity form", "err", err) 83 htmx.HxError(w, http.StatusBadRequest, "Failed to create activity, ensure all fields contain valid data.") 84 return 85 } ··· 88 newActivity.CreatedAt = time.Now().UTC() 89 90 if err := db.ValidateActivity(newActivity); err != nil { 91 - l.Error("invalid activity def", "err", err) 92 switch { 93 case errors.Is(err, db.ErrActivityNameEmpty): 94 htmx.HxError(w, http.StatusBadRequest, "Activity must have a name.") ··· 124 }, 125 }) 126 if err != nil { 127 - l.Error("failed to create activity record", "err", err) 128 htmx.HxError(w, http.StatusInternalServerError, "Failed to create activity, try again later.") 129 return 130 } 131 132 err = SavePendingCreate(h, w, r, PendingActivityCreation, newActivity) 133 if err != nil { 134 - l.Error("failed to save yoten-session to add pending activity creation", "err", err) 135 } 136 137 if !h.Config.Core.Dev { ··· 146 Set("category_count", len(categoriesString)), 147 }) 148 if err != nil { 149 - l.Error("failed to enqueue posthog event", "err", err) 150 } 151 } 152 ··· 155 } 156 157 func (h *Handler) HandleDeleteActivity(w http.ResponseWriter, r *http.Request) { 158 - l := h.Logger.With("handler", "HandleDeleteActivity") 159 - 160 user := h.Oauth.GetUser(r) 161 if user == nil { 162 - l.Error("failed to get logged-in user") 163 htmx.HxRedirect(w, "/login") 164 return 165 } 166 client, err := h.Oauth.AuthorizedClient(r) 167 if err != nil { 168 - l.Error("failed to get authorized client", "err", err) 169 htmx.HxError(w, http.StatusUnauthorized, "Failed to delete activity, try again later.") 170 return 171 } ··· 175 rkey := chi.URLParam(r, "rkey") 176 activity, err := db.GetActivityByRkey(h.Db, user.Did, rkey) 177 if err != nil { 178 - l.Error("failed to get activity from db", "err", err) 179 htmx.HxError(w, http.StatusInternalServerError, "Failed to delete activity, try again later.") 180 return 181 } 182 183 if user.Did != activity.Did { 184 - l.Error("user does not own record", "did", user.Did, "activityDid", activity.Did) 185 htmx.HxError(w, http.StatusUnauthorized, "You do not have permissions to edit this activity.") 186 return 187 } ··· 192 Rkey: activity.Rkey, 193 }) 194 if err != nil { 195 - l.Error("failed to delete activity from PDS", "err", err) 196 htmx.HxError(w, http.StatusInternalServerError, "Failed to delete activity, try again later.") 197 return 198 } 199 200 err = SavePendingDelete(h, w, r, PendingActivityDeletion, activity) 201 if err != nil { 202 - l.Error("failed to save yoten-session to add pending activity deletion", "err", err) 203 } 204 205 if !h.Config.Core.Dev { ··· 210 Set("activity_id", activity.ID), 211 }) 212 if err != nil { 213 - l.Error("failed to enqueue posthog event", "err", err) 214 } 215 } 216 ··· 219 } 220 221 func (h *Handler) HandleEditActivityPage(w http.ResponseWriter, r *http.Request) { 222 - l := h.Logger.With("handler", "HandleEditActivityPage") 223 - 224 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 225 if err != nil { 226 - l.Error("failed to get logged-in user", "err", err) 227 htmx.HxRedirect(w, "/login") 228 return 229 } ··· 231 rkey := chi.URLParam(r, "rkey") 232 activity, err := db.GetActivityByRkey(h.Db, user.Did, rkey) 233 if err != nil { 234 - l.Error("failed to get activity from db", "err", err) 235 htmx.HxError(w, http.StatusInternalServerError, "Failed to update activity, try again later.") 236 return 237 } 238 239 if user.Did != activity.Did { 240 - l.Error("user does not own record", "did", user.Did, "activityDid", activity.Did) 241 htmx.HxError(w, http.StatusUnauthorized, "You do not have permissions to edit this activity.") 242 return 243 } ··· 252 case http.MethodPost: 253 client, err := h.Oauth.AuthorizedClient(r) 254 if err != nil { 255 - l.Error("failed to get authorized client", "err", err) 256 htmx.HxRedirect(w, "/login") 257 return 258 } 259 260 updatedActivity, err := parseActivityForm(r) 261 if err != nil { 262 - l.Error("invalid activity form", "err", err) 263 htmx.HxError(w, http.StatusBadRequest, "Failed to create activity, ensure all fields contain valid data.") 264 return 265 } ··· 268 updatedActivity.CreatedAt = activity.CreatedAt 269 270 if err := db.ValidateActivity(updatedActivity); err != nil { 271 - l.Error("invalid activity def", "err", err) 272 switch { 273 case errors.Is(err, db.ErrActivityNameEmpty): 274 htmx.HxError(w, http.StatusBadRequest, "Activity must have a name.") ··· 311 SwapRecord: cid, 312 }) 313 if err != nil { 314 - l.Error("failed to update study session record", "err", err) 315 htmx.HxError(w, http.StatusInternalServerError, "Failed to update activity, try again later.") 316 return 317 } 318 319 err = SavePendingUpdate(h, w, r, PendingActivityUpdates, updatedActivity) 320 if err != nil { 321 - l.Error("failed to save yoten-session to add pending activity updates", "err", err) 322 } 323 324 if !h.Config.Core.Dev { ··· 333 Set("category_count", len(categoriesString)), 334 }) 335 if err != nil { 336 - l.Error("failed to enqueue posthog event", "err", err) 337 } 338 } 339
··· 3 import ( 4 "errors" 5 "fmt" 6 + "log" 7 "net/http" 8 "time" 9 ··· 55 } 56 57 func (h *Handler) HandleNewActivityPage(w http.ResponseWriter, r *http.Request) { 58 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 59 if err != nil { 60 + log.Println("failed to get logged-in user:", err) 61 htmx.HxRedirect(w, "/login") 62 return 63 } ··· 71 case http.MethodPost: 72 client, err := h.Oauth.AuthorizedClient(r) 73 if err != nil { 74 + log.Println("failed to get authorized client:", err) 75 htmx.HxRedirect(w, "/login") 76 return 77 } 78 79 newActivity, err := parseActivityForm(r) 80 if err != nil { 81 + log.Println("invalid activity form:", err) 82 htmx.HxError(w, http.StatusBadRequest, "Failed to create activity, ensure all fields contain valid data.") 83 return 84 } ··· 87 newActivity.CreatedAt = time.Now().UTC() 88 89 if err := db.ValidateActivity(newActivity); err != nil { 90 + log.Println("invalid activity def:", err) 91 switch { 92 case errors.Is(err, db.ErrActivityNameEmpty): 93 htmx.HxError(w, http.StatusBadRequest, "Activity must have a name.") ··· 123 }, 124 }) 125 if err != nil { 126 + log.Println("failed to create activity record:", err) 127 htmx.HxError(w, http.StatusInternalServerError, "Failed to create activity, try again later.") 128 return 129 } 130 131 err = SavePendingCreate(h, w, r, PendingActivityCreation, newActivity) 132 if err != nil { 133 + log.Printf("failed to save yoten-session to add pending activity creation: %v", err) 134 } 135 136 if !h.Config.Core.Dev { ··· 145 Set("category_count", len(categoriesString)), 146 }) 147 if err != nil { 148 + log.Println("failed to enqueue posthog event:", err) 149 } 150 } 151 ··· 154 } 155 156 func (h *Handler) HandleDeleteActivity(w http.ResponseWriter, r *http.Request) { 157 user := h.Oauth.GetUser(r) 158 if user == nil { 159 + log.Println("failed to get logged-in user") 160 htmx.HxRedirect(w, "/login") 161 return 162 } 163 client, err := h.Oauth.AuthorizedClient(r) 164 if err != nil { 165 + log.Println("failed to get authorized client:", err) 166 htmx.HxError(w, http.StatusUnauthorized, "Failed to delete activity, try again later.") 167 return 168 } ··· 172 rkey := chi.URLParam(r, "rkey") 173 activity, err := db.GetActivityByRkey(h.Db, user.Did, rkey) 174 if err != nil { 175 + log.Println("failed to get activity from db:", err) 176 htmx.HxError(w, http.StatusInternalServerError, "Failed to delete activity, try again later.") 177 return 178 } 179 180 if user.Did != activity.Did { 181 + log.Printf("user '%s' does not own record '%s'", user.Did, rkey) 182 htmx.HxError(w, http.StatusUnauthorized, "You do not have permissions to edit this activity.") 183 return 184 } ··· 189 Rkey: activity.Rkey, 190 }) 191 if err != nil { 192 + log.Println("failed to delete activity from PDS:", err) 193 htmx.HxError(w, http.StatusInternalServerError, "Failed to delete activity, try again later.") 194 return 195 } 196 197 err = SavePendingDelete(h, w, r, PendingActivityDeletion, activity) 198 if err != nil { 199 + log.Printf("failed to save yoten-session to add pending activity deletion: %v", err) 200 } 201 202 if !h.Config.Core.Dev { ··· 207 Set("activity_id", activity.ID), 208 }) 209 if err != nil { 210 + log.Println("failed to enqueue posthog event:", err) 211 } 212 } 213 ··· 216 } 217 218 func (h *Handler) HandleEditActivityPage(w http.ResponseWriter, r *http.Request) { 219 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 220 if err != nil { 221 + log.Println("failed to get logged-in user:", err) 222 htmx.HxRedirect(w, "/login") 223 return 224 } ··· 226 rkey := chi.URLParam(r, "rkey") 227 activity, err := db.GetActivityByRkey(h.Db, user.Did, rkey) 228 if err != nil { 229 + log.Println("failed to get activity from db:", err) 230 htmx.HxError(w, http.StatusInternalServerError, "Failed to update activity, try again later.") 231 return 232 } 233 234 if user.Did != activity.Did { 235 + log.Printf("user '%s' does not own record '%s'", user.Did, rkey) 236 htmx.HxError(w, http.StatusUnauthorized, "You do not have permissions to edit this activity.") 237 return 238 } ··· 247 case http.MethodPost: 248 client, err := h.Oauth.AuthorizedClient(r) 249 if err != nil { 250 + log.Println("failed to get authorized client:", err) 251 htmx.HxRedirect(w, "/login") 252 return 253 } 254 255 updatedActivity, err := parseActivityForm(r) 256 if err != nil { 257 + log.Println("invalid activity form:", err) 258 htmx.HxError(w, http.StatusBadRequest, "Failed to create activity, ensure all fields contain valid data.") 259 return 260 } ··· 263 updatedActivity.CreatedAt = activity.CreatedAt 264 265 if err := db.ValidateActivity(updatedActivity); err != nil { 266 + log.Println("invalid activity def:", err) 267 switch { 268 case errors.Is(err, db.ErrActivityNameEmpty): 269 htmx.HxError(w, http.StatusBadRequest, "Activity must have a name.") ··· 306 SwapRecord: cid, 307 }) 308 if err != nil { 309 + log.Println("failed to update study session record:", err) 310 htmx.HxError(w, http.StatusInternalServerError, "Failed to update activity, try again later.") 311 return 312 } 313 314 err = SavePendingUpdate(h, w, r, PendingActivityUpdates, updatedActivity) 315 if err != nil { 316 + log.Printf("failed to save yoten-session to add pending activity updates: %v", err) 317 } 318 319 if !h.Config.Core.Dev { ··· 328 Set("category_count", len(categoriesString)), 329 }) 330 if err != nil { 331 + log.Println("failed to enqueue posthog event:", err) 332 } 333 } 334
REVERTED
internal/server/handlers/notification.go
··· 1 package handlers 2 3 import ( 4 "net/http" 5 "strconv" 6 ··· 11 ) 12 13 func (h *Handler) HandleNotificationFeed(w http.ResponseWriter, r *http.Request) { 14 - l := h.Logger.With("handler", "HandleNotificationFeed") 15 - 16 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 17 if err != nil { 18 - l.Error("failed to get logged-in user", "err", err) 19 htmx.HxRedirect(w, "/login") 20 return 21 } ··· 26 } 27 page, err := strconv.ParseInt(pageStr, 10, 64) 28 if err != nil { 29 - l.Error("failed to parse page value", "err", err) 30 page = 1 31 } 32 if page == 0 { ··· 40 case http.MethodGet: 41 notifications, err := db.GetNotificationsByDid(h.Db, user.Did, pageSize+1, int(offset)) 42 if err != nil { 43 - l.Error("failed to retrieve notifications", "err", err) 44 htmx.HxError(w, http.StatusInternalServerError, "Failed to get notifications, try again later.") 45 return 46 } 47 48 hydratedNotifications, err := h.getBskyProfileHydratedNotificationFeed(notifications) 49 if err != nil { 50 - l.Error("failed to hydrate notifications with bsky profile", "err", err) 51 htmx.HxError(w, http.StatusInternalServerError, "Failed to get notifications, try again later.") 52 return 53 } ··· 67 } 68 69 func (h *Handler) HandleNotificationMarkAllRead(w http.ResponseWriter, r *http.Request) { 70 - l := h.Logger.With("handler", "HandleNotificationMarkAllRead") 71 - 72 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 73 if err != nil { 74 - l.Error("failed to get logged-in user", "err", err) 75 htmx.HxRedirect(w, "/login") 76 return 77 } 78 79 err = db.MarkAllNotificationsAsRead(h.Db, user.Did) 80 if err != nil { 81 - l.Error("failed to mark all notifications", "err", err) 82 htmx.HxError(w, http.StatusInternalServerError, "Failed to mark all notifications as read, try again later.") 83 return 84 }
··· 1 package handlers 2 3 import ( 4 + "log" 5 "net/http" 6 "strconv" 7 ··· 12 ) 13 14 func (h *Handler) HandleNotificationFeed(w http.ResponseWriter, r *http.Request) { 15 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 16 if err != nil { 17 + log.Println("failed to get logged-in user:", err) 18 htmx.HxRedirect(w, "/login") 19 return 20 } ··· 25 } 26 page, err := strconv.ParseInt(pageStr, 10, 64) 27 if err != nil { 28 + log.Println("failed to parse page value:", err) 29 page = 1 30 } 31 if page == 0 { ··· 39 case http.MethodGet: 40 notifications, err := db.GetNotificationsByDid(h.Db, user.Did, pageSize+1, int(offset)) 41 if err != nil { 42 + log.Println("failed to retrieve notifications:", err) 43 htmx.HxError(w, http.StatusInternalServerError, "Failed to get notifications, try again later.") 44 return 45 } 46 47 hydratedNotifications, err := h.getBskyProfileHydratedNotificationFeed(notifications) 48 if err != nil { 49 + log.Println("failed to hydrate notifications with bsky profile:", err) 50 htmx.HxError(w, http.StatusInternalServerError, "Failed to get notifications, try again later.") 51 return 52 } ··· 66 } 67 68 func (h *Handler) HandleNotificationMarkAllRead(w http.ResponseWriter, r *http.Request) { 69 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 70 if err != nil { 71 + log.Println("failed to get logged-in user:", err) 72 htmx.HxRedirect(w, "/login") 73 return 74 } 75 76 err = db.MarkAllNotificationsAsRead(h.Db, user.Did) 77 if err != nil { 78 + log.Println("failed to mark all notifications:", err) 79 htmx.HxError(w, http.StatusInternalServerError, "Failed to mark all notifications as read, try again later.") 80 return 81 }
REVERTED
internal/server/handlers/reaction.go
··· 1 package handlers 2 3 import ( 4 "net/http" 5 "slices" 6 "strconv" ··· 20 ) 21 22 func (h *Handler) HandleReaction(w http.ResponseWriter, r *http.Request) { 23 - l := h.Logger.With("handler", "HandleReaction") 24 - 25 client, err := h.Oauth.AuthorizedClient(r) 26 if err != nil { 27 - l.Error("failed to get authorized client", "err", err) 28 htmx.HxRedirect(w, "/login") 29 return 30 } 31 32 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 33 if err != nil { 34 - l.Error("failed to get logged-in user", "err", err) 35 htmx.HxRedirect(w, "/login") 36 return 37 } ··· 54 } 55 56 if user.Did == session.Did { 57 - l.Error("failed to react to study session, cannot react to your own study session") 58 htmx.HxError(w, http.StatusBadRequest, "You cannot react to your own study sessions.") 59 return 60 } ··· 74 75 reaction, err := db.ReactionFromString(db.ReactionType(reactionId).String()) 76 if err != nil { 77 - l.Error("failed to get reaction types", "err", err) 78 htmx.HxError(w, http.StatusInternalServerError, "Failed to get global study session feed, try again later.") 79 return 80 } 81 82 reactionEvents, err := db.GetReactionsForSession(h.Db, subjectDid, subjectRkey) 83 if err != nil { 84 - l.Error("failed to get reactions for study session from db", "err", err) 85 htmx.HxError(w, http.StatusInternalServerError, "Failed to get global study session feed, try again later.") 86 return 87 } ··· 90 case http.MethodPost: 91 reactionEvent, err := db.GetReactionEvent(h.Db, user.Did, session, reaction.ID) 92 if err != nil { 93 - l.Error("failed to get reaction event from db", "err", err) 94 htmx.HxError(w, http.StatusInternalServerError, "Failed to add reaction, try again later.") 95 return 96 } 97 if reactionEvent != nil { 98 - l.Error("failed to add reaction, user already reacted") 99 htmx.HxError(w, http.StatusBadRequest, "You cannot react multiple times with the same reaction.") 100 return 101 } ··· 114 }}, 115 }) 116 if err != nil { 117 - l.Error("failed to create reaction record", "err", err) 118 htmx.HxError(w, http.StatusInternalServerError, "Failed to add reaction, try again later.") 119 return 120 } ··· 129 Set("session_rkey", subjectRkey), 130 }) 131 if err != nil { 132 - l.Error("failed to enqueue posthog event", "err", err) 133 } 134 } 135 ··· 154 case http.MethodDelete: 155 reactionEvent, err := db.GetReactionEvent(h.Db, user.Did, session, reaction.ID) 156 if err != nil { 157 - l.Error("failed to get reaction event from db", "err", err) 158 htmx.HxError(w, http.StatusInternalServerError, "Failed to remove reaction, try again later.") 159 return 160 } ··· 165 Rkey: reactionEvent.Rkey, 166 }) 167 if err != nil { 168 - l.Error("failed to delete reaction record", "err", err) 169 htmx.HxError(w, http.StatusInternalServerError, "Failed to remove reaction, try again later.") 170 return 171 } ··· 180 Set("session_rkey", subjectRkey), 181 }) 182 if err != nil { 183 - l.Error("failed to enqueue posthog event", "err", err) 184 } 185 } 186 ··· 189 }) 190 191 partials.NewReactions(partials.NewReactionsProps{ 192 - User: user, 193 - SessionRkey: subjectRkey, 194 - SessionDid: subjectDid, 195 ReactionEvents: reactionEvents, 196 }).Render(r.Context(), w) 197 }
··· 1 package handlers 2 3 import ( 4 + "log" 5 "net/http" 6 "slices" 7 "strconv" ··· 21 ) 22 23 func (h *Handler) HandleReaction(w http.ResponseWriter, r *http.Request) { 24 client, err := h.Oauth.AuthorizedClient(r) 25 if err != nil { 26 + log.Println("failed to get authorized client:", err) 27 htmx.HxRedirect(w, "/login") 28 return 29 } 30 31 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 32 if err != nil { 33 + log.Println("failed to get logged-in user:", err) 34 htmx.HxRedirect(w, "/login") 35 return 36 } ··· 53 } 54 55 if user.Did == session.Did { 56 + log.Println("failed to react to study session, cannot react to your own study session") 57 htmx.HxError(w, http.StatusBadRequest, "You cannot react to your own study sessions.") 58 return 59 } ··· 73 74 reaction, err := db.ReactionFromString(db.ReactionType(reactionId).String()) 75 if err != nil { 76 + log.Printf("failed to get reaction types: %v", err) 77 htmx.HxError(w, http.StatusInternalServerError, "Failed to get global study session feed, try again later.") 78 return 79 } 80 81 reactionEvents, err := db.GetReactionsForSession(h.Db, subjectDid, subjectRkey) 82 if err != nil { 83 + log.Println("failed to get reactions for study session from db:", err) 84 htmx.HxError(w, http.StatusInternalServerError, "Failed to get global study session feed, try again later.") 85 return 86 } ··· 89 case http.MethodPost: 90 reactionEvent, err := db.GetReactionEvent(h.Db, user.Did, session, reaction.ID) 91 if err != nil { 92 + log.Println("failed to get reaction event from db:", err) 93 htmx.HxError(w, http.StatusInternalServerError, "Failed to add reaction, try again later.") 94 return 95 } 96 if reactionEvent != nil { 97 + log.Println("failed to add reaction, user already reacted") 98 htmx.HxError(w, http.StatusBadRequest, "You cannot react multiple times with the same reaction.") 99 return 100 } ··· 113 }}, 114 }) 115 if err != nil { 116 + log.Println("failed to create reaction record:", err) 117 htmx.HxError(w, http.StatusInternalServerError, "Failed to add reaction, try again later.") 118 return 119 } ··· 128 Set("session_rkey", subjectRkey), 129 }) 130 if err != nil { 131 + log.Println("failed to enqueue posthog event:", err) 132 } 133 } 134 ··· 153 case http.MethodDelete: 154 reactionEvent, err := db.GetReactionEvent(h.Db, user.Did, session, reaction.ID) 155 if err != nil { 156 + log.Println("failed to get reaction event from db:", err) 157 htmx.HxError(w, http.StatusInternalServerError, "Failed to remove reaction, try again later.") 158 return 159 } ··· 164 Rkey: reactionEvent.Rkey, 165 }) 166 if err != nil { 167 + log.Println("failed to delete reaction record:", err) 168 htmx.HxError(w, http.StatusInternalServerError, "Failed to remove reaction, try again later.") 169 return 170 } ··· 179 Set("session_rkey", subjectRkey), 180 }) 181 if err != nil { 182 + log.Println("failed to enqueue posthog event:", err) 183 } 184 } 185 ··· 188 }) 189 190 partials.NewReactions(partials.NewReactionsProps{ 191 + User: user, 192 + SessionRkey: subjectRkey, 193 + SessionDid: subjectDid, 194 + // Reactions: reactions, 195 ReactionEvents: reactionEvents, 196 }).Render(r.Context(), w) 197 }
REVERTED
internal/server/handlers/stats.go
··· 1 package handlers 2 3 import ( 4 "net/http" 5 6 "yoten.app/internal/clients/bsky" ··· 11 ) 12 13 func (h *Handler) HandleTimePerGraphs(w http.ResponseWriter, r *http.Request) { 14 - l := h.Logger.With("handler", "HandleTimePerGraphs") 15 - 16 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 17 if err != nil { 18 - l.Error("failed to get logged-in user", "err", err) 19 htmx.HxRedirect(w, "/login") 20 return 21 } ··· 25 26 chartData, err := db.GetTimePerData(h.Db, user.Did, period) 27 if err != nil { 28 - l.Error("failed to get time per chart data", "err", err) 29 chartData = db.ChartsData{ 30 ActivityData: []db.ChartData{}, 31 CategoryData: []db.ChartData{}, ··· 39 } 40 41 func (h *Handler) HandleStatsPage(w http.ResponseWriter, r *http.Request) { 42 - l := h.Logger.With("handler", "HandleStatsPage") 43 - 44 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 45 if err != nil { 46 - l.Error("failed to get logged-in user", "err", err) 47 htmx.HxRedirect(w, "/login") 48 return 49 } 50 51 totalStudyTime, err := db.GetTotalStudyTime(h.Db, user.Did) 52 if err != nil { 53 - l.Error("failed to get total study time", "err", err) 54 } 55 56 totalStudySessions, err := db.GetTotalStudySessions(h.Db, user.Did) 57 if err != nil { 58 - l.Error("failed to get total study study sessions", "err", err) 59 } 60 61 totalActiveDays, err := db.GetTotalActiveDays(h.Db, user.Did) 62 if err != nil { 63 - l.Error("failed to get total active days", "err", err) 64 } 65 66 streak, err := db.GetCurrentStreak(h.Db, user.Did) 67 if err != nil { 68 - l.Error("failed to get streak", "err", err) 69 } 70 71 heatmap, err := db.GetHeatmapData(h.Db, user.Did) 72 if err != nil { 73 - l.Error("failed to get heatmap data", "err", err) 74 } 75 76 inputOutputPercentage, err := db.GetInputOutputPercentage(h.Db, user.Did) 77 if err != nil { 78 - l.Error("failed to get input vs output data", "err", err) 79 } 80 81 languageSummary, err := db.GetLanguageSummary(h.Db, user.Did) 82 if err != nil { 83 - l.Error("failed to get language time summary", "err", err) 84 } 85 languageChartSegments := db.ConvertToDonutChartSegments(languageSummary) 86
··· 1 package handlers 2 3 import ( 4 + "log" 5 "net/http" 6 7 "yoten.app/internal/clients/bsky" ··· 12 ) 13 14 func (h *Handler) HandleTimePerGraphs(w http.ResponseWriter, r *http.Request) { 15 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 16 if err != nil { 17 + log.Println("failed to get logged-in user:", err) 18 htmx.HxRedirect(w, "/login") 19 return 20 } ··· 24 25 chartData, err := db.GetTimePerData(h.Db, user.Did, period) 26 if err != nil { 27 + log.Println("failed to get time per chart data:", err) 28 chartData = db.ChartsData{ 29 ActivityData: []db.ChartData{}, 30 CategoryData: []db.ChartData{}, ··· 38 } 39 40 func (h *Handler) HandleStatsPage(w http.ResponseWriter, r *http.Request) { 41 user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) 42 if err != nil { 43 + log.Println("failed to get logged-in user:", err) 44 htmx.HxRedirect(w, "/login") 45 return 46 } 47 48 totalStudyTime, err := db.GetTotalStudyTime(h.Db, user.Did) 49 if err != nil { 50 + log.Println("failed to get total study time:", err) 51 } 52 53 totalStudySessions, err := db.GetTotalStudySessions(h.Db, user.Did) 54 if err != nil { 55 + log.Println("failed to get total study study sessions:", err) 56 } 57 58 totalActiveDays, err := db.GetTotalActiveDays(h.Db, user.Did) 59 if err != nil { 60 + log.Println("failed to get total active days:", err) 61 } 62 63 streak, err := db.GetCurrentStreak(h.Db, user.Did) 64 if err != nil { 65 + log.Println("failed to get streak:", err) 66 } 67 68 heatmap, err := db.GetHeatmapData(h.Db, user.Did) 69 if err != nil { 70 + log.Println("failed to get heatmap data:", err) 71 } 72 73 inputOutputPercentage, err := db.GetInputOutputPercentage(h.Db, user.Did) 74 if err != nil { 75 + log.Println("failed to get input vs output data:", err) 76 } 77 78 languageSummary, err := db.GetLanguageSummary(h.Db, user.Did) 79 if err != nil { 80 + log.Println("failed to get language time summary:", err) 81 } 82 languageChartSegments := db.ConvertToDonutChartSegments(languageSummary) 83
REVERTED
internal/consumer/ingester.go
··· 4 "context" 5 "encoding/json" 6 "fmt" 7 - "log/slog" 8 "strings" 9 "time" 10 ··· 20 type Ingester struct { 21 Db db.DbWrapper 22 Config *config.Config 23 - Logger *slog.Logger 24 } 25 26 type processFunc func(ctx context.Context, e *models.Event) error ··· 36 } 37 }() 38 39 - l := i.Logger.With("kind", e.Kind) 40 switch e.Kind { 41 case models.EventKindCommit: 42 switch e.Commit.Collection { 43 case yoten.ActorProfileNSID: 44 - l = l.With("handler", "ingestProfile") 45 err = i.ingestProfile(e) 46 case yoten.FeedSessionNSID: 47 - l = l.With("handler", "ingestStudySession") 48 err = i.ingestStudySession(e) 49 case yoten.ActivityDefNSID: 50 - l = l.With("handler", "ingestActivityDef") 51 err = i.ingestActivityDef(e) 52 case yoten.FeedResourceNSID: 53 - l = l.With("handler", "ingestResource") 54 err = i.ingestResource(e) 55 case yoten.GraphFollowNSID: 56 - l = l.With("handler", "ingestFollow") 57 err = i.ingestFollow(e) 58 case yoten.FeedReactionNSID: 59 - l = l.With("handler", "ingestReaction") 60 err = i.ingestReaction(e) 61 case yoten.FeedCommentNSID: 62 - l = l.With("handler", "ingestComment") 63 err = i.ingestComment(e) 64 } 65 - l = i.Logger.With("nsid", e.Commit.Collection) 66 } 67 if err != nil { 68 - l.Error("failed to ingest event", "err", err) 69 } 70 71 return nil ··· 139 return fmt.Errorf("failed to start transaction: %w", err) 140 } 141 142 - i.Logger.Debug("upserting profile from pds request") 143 err = db.UpsertProfile(tx, &profile) 144 if err != nil { 145 tx.Rollback() ··· 170 171 date, err := time.Parse(time.RFC3339, record.Date) 172 if err != nil { 173 - i.Logger.Error("invalid record", "err", err) 174 return err 175 } 176 ··· 237 return fmt.Errorf("failed to start transaction: %w", err) 238 } 239 240 - i.Logger.Debug("upserting study session from pds request") 241 err = db.UpsertStudySession(tx, &studySession, e.Commit.RKey) 242 if err != nil { 243 tx.Rollback() ··· 262 return fmt.Errorf("failed to start transaction: %w", err) 263 } 264 265 - i.Logger.Debug("deleting study session from pds request") 266 err = db.DeleteStudySessionByRkey(tx, did, e.Commit.RKey) 267 if err != nil { 268 tx.Rollback() ··· 354 return fmt.Errorf("failed to start transaction: %w", err) 355 } 356 357 - i.Logger.Debug("upserting activity def from pds request") 358 err = db.UpsertActivityDef(tx, &activityDef, e.Commit.RKey) 359 if err != nil { 360 tx.Rollback() ··· 362 } 363 return tx.Commit() 364 case models.CommitOperationDelete: 365 - i.Logger.Debug("deleting activity def from pds request") 366 err = db.DeleteActivityDefByRkey(i.Db, did, e.Commit.RKey) 367 } 368 if err != nil { ··· 397 398 subjectDid := record.Subject 399 400 - i.Logger.Debug("upserting follow from pds request") 401 err = db.AddFollow(tx, did, subjectDid, e.Commit.RKey) 402 if err != nil { 403 tx.Rollback() ··· 407 subjectUri := fmt.Sprintf("at://%s/%s/%s", did, yoten.GraphFollowNSID, e.Commit.RKey) 408 err = db.CreateNotification(tx, subjectDid, did, subjectUri, db.NotificationTypeFollow) 409 if err != nil { 410 - i.Logger.Error("failed to create notification record", "err", err) 411 } 412 413 return tx.Commit() 414 case models.CommitOperationDelete: 415 - i.Logger.Debug("deleting follow from pds request") 416 err = db.DeleteFollowByRkey(i.Db, did, e.Commit.RKey) 417 } 418 if err != nil { ··· 475 CreatedAt: createdAt, 476 } 477 478 - i.Logger.Debug("upserting reaction from pds request") 479 err = db.UpsertReaction(i.Db, reactionEvent) 480 if err != nil { 481 tx.Rollback() ··· 484 485 err = db.CreateNotification(tx, subjectDid.String(), did, subject.String(), db.NotificationTypeReaction) 486 if err != nil { 487 - i.Logger.Error("failed to create notification record", "err", err) 488 } 489 490 return tx.Commit() 491 case models.CommitOperationDelete: 492 - i.Logger.Debug("deleting reaction from pds request") 493 err = db.DeleteReactionByRkey(i.Db, did, e.Commit.RKey) 494 } 495 if err != nil { ··· 556 return fmt.Errorf("invalid resource: %w", err) 557 } 558 559 - i.Logger.Debug("upserting resource from pds request") 560 err = db.UpsertResource(i.Db, resource, resource.Rkey) 561 if err != nil { 562 tx.Rollback() ··· 564 } 565 return tx.Commit() 566 case models.CommitOperationDelete: 567 - i.Logger.Debug("deleting resource from pds request") 568 err = db.DeleteResourceByRkey(i.Db, did, e.Commit.RKey) 569 } 570 if err != nil { ··· 636 CreatedAt: createdAt, 637 } 638 639 - i.Logger.Debug("upserting comment from pds request") 640 err = db.UpsertComment(i.Db, comment) 641 if err != nil { 642 tx.Rollback() ··· 647 if subjectDid.String() != did { 648 err = db.CreateNotification(tx, subjectDid.String(), did, subjectUri.String(), db.NotificationTypeComment) 649 if err != nil { 650 - i.Logger.Error("failed to create notification record", "err", err) 651 } 652 } 653 ··· 655 if comment.ParentCommentUri != nil && comment.ParentCommentUri.Authority().String() != did { 656 err = db.CreateNotification(tx, comment.ParentCommentUri.Authority().String(), did, parentCommentUri.String(), db.NotificationTypeReply) 657 if err != nil { 658 - i.Logger.Error("failed to create notification record", "err", err) 659 } 660 } 661 662 return tx.Commit() 663 case models.CommitOperationDelete: 664 - i.Logger.Debug("deleting comment from pds request") 665 err = db.DeleteCommentByRkey(i.Db, did, e.Commit.RKey) 666 } 667 if err != nil {
··· 4 "context" 5 "encoding/json" 6 "fmt" 7 + "log" 8 "strings" 9 "time" 10 ··· 20 type Ingester struct { 21 Db db.DbWrapper 22 Config *config.Config 23 } 24 25 type processFunc func(ctx context.Context, e *models.Event) error ··· 35 } 36 }() 37 38 switch e.Kind { 39 case models.EventKindCommit: 40 switch e.Commit.Collection { 41 case yoten.ActorProfileNSID: 42 err = i.ingestProfile(e) 43 case yoten.FeedSessionNSID: 44 err = i.ingestStudySession(e) 45 case yoten.ActivityDefNSID: 46 err = i.ingestActivityDef(e) 47 case yoten.FeedResourceNSID: 48 err = i.ingestResource(e) 49 case yoten.GraphFollowNSID: 50 err = i.ingestFollow(e) 51 case yoten.FeedReactionNSID: 52 err = i.ingestReaction(e) 53 case yoten.FeedCommentNSID: 54 err = i.ingestComment(e) 55 } 56 } 57 if err != nil { 58 + log.Printf("failed to ingest event for collection %s: %v", e.Commit.Collection, err) 59 } 60 61 return nil ··· 129 return fmt.Errorf("failed to start transaction: %w", err) 130 } 131 132 + log.Printf("upserting profile '%s' from pds request", profile.Did) 133 err = db.UpsertProfile(tx, &profile) 134 if err != nil { 135 tx.Rollback() ··· 160 161 date, err := time.Parse(time.RFC3339, record.Date) 162 if err != nil { 163 + log.Printf("invalid record: %s", err) 164 return err 165 } 166 ··· 227 return fmt.Errorf("failed to start transaction: %w", err) 228 } 229 230 + log.Println("upserting study session from pds request") 231 err = db.UpsertStudySession(tx, &studySession, e.Commit.RKey) 232 if err != nil { 233 tx.Rollback() ··· 252 return fmt.Errorf("failed to start transaction: %w", err) 253 } 254 255 + log.Println("deleting study session from pds request") 256 err = db.DeleteStudySessionByRkey(tx, did, e.Commit.RKey) 257 if err != nil { 258 tx.Rollback() ··· 344 return fmt.Errorf("failed to start transaction: %w", err) 345 } 346 347 + log.Println("upserting activity def from pds request") 348 err = db.UpsertActivityDef(tx, &activityDef, e.Commit.RKey) 349 if err != nil { 350 tx.Rollback() ··· 352 } 353 return tx.Commit() 354 case models.CommitOperationDelete: 355 + log.Println("deleting activity def from pds request") 356 err = db.DeleteActivityDefByRkey(i.Db, did, e.Commit.RKey) 357 } 358 if err != nil { ··· 387 388 subjectDid := record.Subject 389 390 + log.Println("upserting follow from pds request") 391 err = db.AddFollow(tx, did, subjectDid, e.Commit.RKey) 392 if err != nil { 393 tx.Rollback() ··· 397 subjectUri := fmt.Sprintf("at://%s/%s/%s", did, yoten.GraphFollowNSID, e.Commit.RKey) 398 err = db.CreateNotification(tx, subjectDid, did, subjectUri, db.NotificationTypeFollow) 399 if err != nil { 400 + log.Println("failed to create notification record:", err) 401 } 402 403 return tx.Commit() 404 case models.CommitOperationDelete: 405 + log.Println("deleting follow from pds request") 406 err = db.DeleteFollowByRkey(i.Db, did, e.Commit.RKey) 407 } 408 if err != nil { ··· 465 CreatedAt: createdAt, 466 } 467 468 + log.Println("upserting reaction from pds request") 469 err = db.UpsertReaction(i.Db, reactionEvent) 470 if err != nil { 471 tx.Rollback() ··· 474 475 err = db.CreateNotification(tx, subjectDid.String(), did, subject.String(), db.NotificationTypeReaction) 476 if err != nil { 477 + log.Println("failed to create notification record:", err) 478 } 479 480 return tx.Commit() 481 case models.CommitOperationDelete: 482 + log.Println("deleting reaction from pds request") 483 err = db.DeleteReactionByRkey(i.Db, did, e.Commit.RKey) 484 } 485 if err != nil { ··· 546 return fmt.Errorf("invalid resource: %w", err) 547 } 548 549 + log.Println("upserting resource from pds request") 550 err = db.UpsertResource(i.Db, resource, resource.Rkey) 551 if err != nil { 552 tx.Rollback() ··· 554 } 555 return tx.Commit() 556 case models.CommitOperationDelete: 557 + log.Println("deleting resource from pds request") 558 err = db.DeleteResourceByRkey(i.Db, did, e.Commit.RKey) 559 } 560 if err != nil { ··· 626 CreatedAt: createdAt, 627 } 628 629 + log.Println("upserting comment from pds request") 630 err = db.UpsertComment(i.Db, comment) 631 if err != nil { 632 tx.Rollback() ··· 637 if subjectDid.String() != did { 638 err = db.CreateNotification(tx, subjectDid.String(), did, subjectUri.String(), db.NotificationTypeComment) 639 if err != nil { 640 + log.Println("failed to create notification record:", err) 641 } 642 } 643 ··· 645 if comment.ParentCommentUri != nil && comment.ParentCommentUri.Authority().String() != did { 646 err = db.CreateNotification(tx, comment.ParentCommentUri.Authority().String(), did, parentCommentUri.String(), db.NotificationTypeReply) 647 if err != nil { 648 + log.Println("failed to create notification record:", err) 649 } 650 } 651 652 return tx.Commit() 653 case models.CommitOperationDelete: 654 + log.Println("deleting comment from pds request") 655 err = db.DeleteCommentByRkey(i.Db, did, e.Commit.RKey) 656 } 657 if err != nil {
REVERTED
internal/server/handlers/router.go
··· 7 "github.com/go-chi/chi/v5" 8 9 "yoten.app/internal/server" 10 - "yoten.app/internal/server/log" 11 "yoten.app/internal/server/middleware" 12 "yoten.app/internal/server/views" 13 ) ··· 26 h.Oauth, 27 h.Db, 28 h.IdResolver, 29 - log.SubLogger(h.Logger, "middleware"), 30 ) 31 32 router.HandleFunc("/*", func(w http.ResponseWriter, r *http.Request) {
··· 7 "github.com/go-chi/chi/v5" 8 9 "yoten.app/internal/server" 10 "yoten.app/internal/server/middleware" 11 "yoten.app/internal/server/views" 12 ) ··· 25 h.Oauth, 26 h.Db, 27 h.IdResolver, 28 ) 29 30 router.HandleFunc("/*", func(w http.ResponseWriter, r *http.Request) {
REVERTED
internal/server/middleware/middleware.go
··· 3 import ( 4 "context" 5 "fmt" 6 - "log/slog" 7 "net/http" 8 "net/url" 9 "slices" ··· 25 oauth *oauth.OAuth 26 db *db.DB 27 idResolver *atproto.Resolver 28 - logger *slog.Logger 29 } 30 31 - func New(oauth *oauth.OAuth, db *db.DB, idResolver *atproto.Resolver, logger *slog.Logger) Middleware { 32 return Middleware{ 33 oauth: oauth, 34 db: db, 35 idResolver: idResolver, 36 - logger: logger, 37 } 38 } 39 40 type middlewareFunc func(http.Handler) http.Handler 41 42 func AuthMiddleware(o *oauth.OAuth) middlewareFunc { 43 - l := o.Logger.With("middleware", "AuthMiddleware") 44 - 45 return func(next http.Handler) http.Handler { 46 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 47 returnURL := "/" ··· 63 64 sess, err := o.ResumeSession(r) 65 if err != nil { 66 - l.Error("failed to resume session, redirecting...", "err", err, "url", r.URL.String()) 67 redirectFunc(w, r) 68 return 69 } 70 71 if sess == nil { 72 - l.Warn("session is nil, redirecting...") 73 redirectFunc(w, r) 74 return 75 } ··· 80 } 81 82 func (mw Middleware) ResolveIdent() middlewareFunc { 83 - l := mw.logger.With("middleware", "ResolveIdent") 84 excluded := []string{"favicon.ico"} 85 86 return func(next http.Handler) http.Handler { ··· 95 96 id, err := mw.idResolver.ResolveIdent(r.Context(), didOrHandle) 97 if err != nil { 98 - l.Error("failed to resolve did/handle", "err", err) 99 w.WriteHeader(http.StatusNotFound) 100 views.NotFoundPage(views.NotFoundPageParams{}).Render(r.Context(), w) 101 return ··· 109 } 110 111 func (mw Middleware) LoadUnreadNotificationCount() middlewareFunc { 112 - l := mw.logger.With("middleware", "LoadUnreadNotificationCount") 113 - 114 return func(next http.Handler) http.Handler { 115 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 116 user := mw.oauth.GetUser(r) ··· 121 122 count, err := db.GetUnreadNotificationCount(mw.db, user.Did) 123 if err != nil { 124 - l.Error("failed to get notification count", "err", err) 125 } 126 127 ctx := context.WithValue(r.Context(), UnreadNotificationCountCtxKey, count)
··· 3 import ( 4 "context" 5 "fmt" 6 + "log" 7 "net/http" 8 "net/url" 9 "slices" ··· 25 oauth *oauth.OAuth 26 db *db.DB 27 idResolver *atproto.Resolver 28 } 29 30 + func New(oauth *oauth.OAuth, db *db.DB, idResolver *atproto.Resolver) Middleware { 31 return Middleware{ 32 oauth: oauth, 33 db: db, 34 idResolver: idResolver, 35 } 36 } 37 38 type middlewareFunc func(http.Handler) http.Handler 39 40 func AuthMiddleware(o *oauth.OAuth) middlewareFunc { 41 return func(next http.Handler) http.Handler { 42 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 43 returnURL := "/" ··· 59 60 sess, err := o.ResumeSession(r) 61 if err != nil { 62 + log.Println("failed to resume session, redirecting...", "err", err, "url", r.URL.String()) 63 redirectFunc(w, r) 64 return 65 } 66 67 if sess == nil { 68 + log.Printf("session is nil, redirecting...") 69 redirectFunc(w, r) 70 return 71 } ··· 76 } 77 78 func (mw Middleware) ResolveIdent() middlewareFunc { 79 excluded := []string{"favicon.ico"} 80 81 return func(next http.Handler) http.Handler { ··· 90 91 id, err := mw.idResolver.ResolveIdent(r.Context(), didOrHandle) 92 if err != nil { 93 + log.Println("failed to resolve did/handle:", err) 94 w.WriteHeader(http.StatusNotFound) 95 views.NotFoundPage(views.NotFoundPageParams{}).Render(r.Context(), w) 96 return ··· 104 } 105 106 func (mw Middleware) LoadUnreadNotificationCount() middlewareFunc { 107 return func(next http.Handler) http.Handler { 108 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 109 user := mw.oauth.GetUser(r) ··· 114 115 count, err := db.GetUnreadNotificationCount(mw.db, user.Did) 116 if err != nil { 117 + log.Println("failed to get notification count:", err) 118 } 119 120 ctx := context.WithValue(r.Context(), UnreadNotificationCountCtxKey, count)
ERROR
internal/server/handlers/login.go

Failed to calculate interdiff for this file.

NEW
internal/server/app.go
··· 63 64 idResolver := atproto.DefaultResolver() 65 66 - oauth, err := oauth.New(config, posthog, log.SubLogger(logger, "oauth")) 67 if err != nil { 68 return nil, fmt.Errorf("failed to start oauth handler: %w", err) 69 }
··· 63 64 idResolver := atproto.DefaultResolver() 65 66 + oauth, err := oauth.New(config, posthog, idResolver, log.SubLogger(logger, "oauth")) 67 if err != nil { 68 return nil, fmt.Errorf("failed to start oauth handler: %w", err) 69 }
NEW
internal/server/handlers/profile.go
··· 130 }) 131 132 if err := g.Wait(); err != nil { 133 - l.Error("failed to fetch critical profile data for", "did", profileDid, "err", err) 134 htmx.HxError(w, http.StatusInternalServerError, "Failed to fetch profile data, try again later.") 135 return 136 }
··· 130 }) 131 132 if err := g.Wait(); err != nil { 133 + l.Error("failed to fetch critical profile data", "did", profileDid, "err", err) 134 htmx.HxError(w, http.StatusInternalServerError, "Failed to fetch profile data, try again later.") 135 return 136 }
NEW
internal/server/oauth/handler.go
··· 1 package oauth 2 3 import ( 4 "encoding/json" 5 "net/http" 6 7 "github.com/go-chi/chi/v5" 8 "github.com/lestrrat-go/jwx/v2/jwk" 9 "github.com/posthog/posthog-go" 10 11 ph "yoten.app/internal/clients/posthog" 12 ) 13 14 func (o *OAuth) Router() http.Handler { ··· 63 } 64 65 func (o *OAuth) callback(w http.ResponseWriter, r *http.Request) { 66 ctx := r.Context() 67 68 sessData, err := o.ClientApp.ProcessCallback(ctx, r.URL.Query()) ··· 73 } 74 75 if err := o.SaveSession(w, r, sessData); err != nil { 76 - o.Logger.Error("failed to save session", "err", err) 77 http.Error(w, err.Error(), http.StatusInternalServerError) 78 return 79 } 80 81 - if !o.Config.Core.Dev { 82 - err = o.Posthog.Enqueue(posthog.Capture{ 83 - DistinctId: sessData.AccountDID.String(), 84 - Event: ph.UserSignInSuccessEvent, 85 }) 86 if err != nil { 87 - o.Logger.Error("failed to enqueue posthog event", "err", err) 88 } 89 } 90
··· 1 package oauth 2 3 import ( 4 + "context" 5 "encoding/json" 6 + "fmt" 7 "net/http" 8 + "time" 9 10 + comatproto "github.com/bluesky-social/indigo/api/atproto" 11 + lexutil "github.com/bluesky-social/indigo/lex/util" 12 "github.com/go-chi/chi/v5" 13 "github.com/lestrrat-go/jwx/v2/jwk" 14 "github.com/posthog/posthog-go" 15 16 + "yoten.app/api/yoten" 17 ph "yoten.app/internal/clients/posthog" 18 + "yoten.app/internal/db" 19 + "yoten.app/internal/server/htmx" 20 ) 21 22 func (o *OAuth) Router() http.Handler { ··· 71 } 72 73 func (o *OAuth) callback(w http.ResponseWriter, r *http.Request) { 74 + l := o.Logger.With("handler", "callback") 75 ctx := r.Context() 76 77 sessData, err := o.ClientApp.ProcessCallback(ctx, r.URL.Query()) ··· 82 } 83 84 if err := o.SaveSession(w, r, sessData); err != nil { 85 + l.Error("failed to save session", "err", err) 86 http.Error(w, err.Error(), http.StatusInternalServerError) 87 return 88 } 89 90 + did := sessData.AccountDID.String() 91 + resolved, err := o.IdResolver.ResolveIdent(context.Background(), did) 92 + if err != nil { 93 + l.Error("failed to resolve handle", "handle", resolved.Handle.String(), "err", err) 94 + htmx.HxError(w, http.StatusBadRequest, fmt.Sprintf("'%s' is an invalid handle", resolved.Handle.String())) 95 + return 96 + } 97 + 98 + client, err := o.AuthorizedClient(r) 99 + if err != nil { 100 + l.Error("failed to get authorized client", "err", err) 101 + http.Error(w, err.Error(), http.StatusInternalServerError) 102 + return 103 + } 104 + 105 + ex, _ := comatproto.RepoGetRecord(r.Context(), client, "", yoten.ActorProfileNSID, did, "self") 106 + var cid *string 107 + if ex != nil { 108 + cid = ex.Cid 109 + } 110 + 111 + // This should only occur once per account 112 + if ex == nil { 113 + createdAt := time.Now().Format(time.RFC3339) 114 + atresp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 115 + Collection: yoten.ActorProfileNSID, 116 + Repo: did, 117 + Rkey: "self", 118 + Record: &lexutil.LexiconTypeDecoder{ 119 + Val: &yoten.ActorProfile{ 120 + DisplayName: resolved.Handle.String(), 121 + Description: db.ToPtr(""), 122 + Languages: make([]string, 0), 123 + Location: db.ToPtr(""), 124 + CreatedAt: createdAt, 125 + }}, 126 + 127 + SwapRecord: cid, 128 }) 129 if err != nil { 130 + l.Error("failed to create profile record", "err", err) 131 + htmx.HxError(w, http.StatusInternalServerError, "Failed to announce profile creation, try again later") 132 + return 133 + } 134 + 135 + l.Debug("created profile record", "uri", atresp.Uri) 136 + 137 + if !o.Config.Core.Dev { 138 + err = o.Posthog.Enqueue(posthog.Capture{ 139 + DistinctId: sessData.AccountDID.String(), 140 + Event: ph.UserSignInSuccessEvent, 141 + }) 142 + if err != nil { 143 + l.Error("failed to enqueue posthog event", "err", err) 144 + } 145 + 146 + properties := posthog.NewProperties(). 147 + Set("display_name", resolved.Handle.String()). 148 + Set("language_count", 0). 149 + Set("$set_once", posthog.NewProperties(). 150 + Set("initial_did", did). 151 + Set("initial_handle", resolved.Handle.String()). 152 + Set("created_at", createdAt), 153 + ) 154 + 155 + err = o.Posthog.Enqueue(posthog.Identify{ 156 + DistinctId: did, 157 + Properties: properties, 158 + }) 159 + if err != nil { 160 + l.Error("failed to enqueue posthog identify event", "err", err) 161 + } 162 + 163 + err = o.Posthog.Enqueue(posthog.Capture{ 164 + DistinctId: did, 165 + Event: ph.ProfileRecordCreatedEvent, 166 + }) 167 + if err != nil { 168 + l.Error("failed to enqueue posthog event", "err", err) 169 + } 170 } 171 } 172
NEW
internal/server/oauth/oauth.go
··· 15 "github.com/gorilla/sessions" 16 "github.com/posthog/posthog-go" 17 18 "yoten.app/internal/server/config" 19 "yoten.app/internal/types" 20 ) ··· 26 JwksUri string 27 Posthog posthog.Client 28 Logger *slog.Logger 29 } 30 31 - func New(config *config.Config, ph posthog.Client, logger *slog.Logger) (*OAuth, error) { 32 var oauthConfig oauth.ClientConfig 33 var clientUri string 34 ··· 58 SessionStore: sessStore, 59 JwksUri: jwksUri, 60 Posthog: ph, 61 Logger: logger, 62 }, nil 63
··· 15 "github.com/gorilla/sessions" 16 "github.com/posthog/posthog-go" 17 18 + "yoten.app/internal/atproto" 19 "yoten.app/internal/server/config" 20 "yoten.app/internal/types" 21 ) ··· 27 JwksUri string 28 Posthog posthog.Client 29 Logger *slog.Logger 30 + IdResolver *atproto.Resolver 31 } 32 33 + func New(config *config.Config, ph posthog.Client, idResolver *atproto.Resolver, logger *slog.Logger) (*OAuth, error) { 34 var oauthConfig oauth.ClientConfig 35 var clientUri string 36 ··· 60 SessionStore: sessStore, 61 JwksUri: jwksUri, 62 Posthog: ph, 63 + IdResolver: idResolver, 64 Logger: logger, 65 }, nil 66