From 08a9d833848435dd3a7e25428f04b6d953ed3a29 Mon Sep 17 00:00:00 2001 From: brookjeynes Date: Mon, 13 Oct 2025 10:05:01 +1000 Subject: [PATCH] refactor: move computed data code to function to simplify visuals Change-Id: ttprpxmrrnnuvoqrvqwprowxzvtxnpwl Signed-off-by: brookjeynes --- internal/server/app.go | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/internal/server/app.go b/internal/server/app.go index a47459d..4abc452 100644 --- a/internal/server/app.go +++ b/internal/server/app.go @@ -97,7 +97,22 @@ func Make(ctx context.Context, config *config.Config) (*Server, error) { return nil, fmt.Errorf("failed to start jetstream watcher: %w", err) } - // -- Computed data + computed := computeData() + + state := &Server{ + Db: d, + Oauth: oauth, + Config: config, + Posthog: posthog, + IdResolver: idResolver, + Session: sess, + ComputedData: computed, + } + + return state, nil +} + +func computeData() ComputedData { sortedLanguages := make([]db.Language, 0, len(db.Languages)) for _, l := range db.Languages { sortedLanguages = append(sortedLanguages, l) @@ -129,24 +144,13 @@ func Make(ctx context.Context, config *config.Config) (*Server, error) { slices.SortFunc(sortedReactions, func(a, b db.Reaction) int { return cmp.Compare(a.ID, b.ID) }) - // -- - state := &Server{ - Db: d, - Oauth: oauth, - Config: config, - Posthog: posthog, - IdResolver: idResolver, - Session: sess, - ComputedData: ComputedData{ - SortedLanguages: sortedLanguages, - SortedCategories: sortedCategories, - SortedResourceTypes: sortedResourceTypes, - SortedReactions: sortedReactions, - }, + return ComputedData{ + SortedLanguages: sortedLanguages, + SortedCategories: sortedCategories, + SortedResourceTypes: sortedResourceTypes, + SortedReactions: sortedReactions, } - - return state, nil } func (s *Server) HandleIndexPage(w http.ResponseWriter, r *http.Request) { -- 2.43.0 From 14d5022ace78d3e1fddf2f9e44f3ee4e512f96a6 Mon Sep 17 00:00:00 2001 From: brookjeynes Date: Mon, 13 Oct 2025 10:05:01 +1000 Subject: [PATCH] feat: use charmbracelet logger Change-Id: olnsoqlksklpkkxrzlypqyxnvkvpqxvx Signed-off-by: brookjeynes --- go.mod | 14 ++++++++++++++ go.sum | 27 +++++++++++++++++++++++++++ internal/server/app.go | 5 +++++ internal/server/log/log.go | 32 +++++++++++++++++++++++++------- 4 files changed, 71 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index e145dc8..6bf130b 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/bluesky-social/indigo v0.0.0-20251003000214-3259b215110e github.com/bluesky-social/jetstream v0.0.0-20250414024304-d17bd81a945e github.com/carlmjohnson/versioninfo v0.22.5 + github.com/charmbracelet/log v0.4.2 github.com/go-chi/chi/v5 v5.2.1 github.com/gorilla/sessions v1.4.0 github.com/ipfs/go-cid v0.4.1 @@ -28,15 +29,22 @@ require ( require ( github.com/a-h/parse v0.0.0-20250122154542-74294addb73e // indirect github.com/andybalholm/brotli v1.1.0 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect + github.com/charmbracelet/lipgloss v1.1.0 // indirect + github.com/charmbracelet/x/ansi v0.8.0 // indirect + github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect + github.com/charmbracelet/x/term v0.2.1 // indirect github.com/cli/browser v1.3.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/fatih/color v1.16.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/goccy/go-json v0.10.2 // indirect @@ -69,10 +77,13 @@ require ( github.com/lestrrat-go/httprc v1.0.4 // indirect github.com/lestrrat-go/iter v1.0.2 // indirect github.com/lestrrat-go/option v1.0.1 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect github.com/minio/sha256-simd v1.0.1 // indirect github.com/mr-tron/base58 v1.2.0 // indirect + github.com/muesli/termenv v0.16.0 // indirect github.com/multiformats/go-base32 v0.1.0 // indirect github.com/multiformats/go-base36 v0.2.0 // indirect github.com/multiformats/go-multibase v0.2.0 // indirect @@ -85,8 +96,10 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.54.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/segmentio/asm v1.2.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b // indirect gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect @@ -97,6 +110,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect golang.org/x/crypto v0.40.0 // indirect + golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 // indirect golang.org/x/mod v0.26.0 // indirect golang.org/x/sys v0.34.0 // indirect golang.org/x/time v0.8.0 // indirect diff --git a/go.sum b/go.sum index 314d5ca..2681ef6 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,8 @@ github.com/a-h/templ v0.3.898 h1:g9oxL/dmM6tvwRe2egJS8hBDQTncokbMoOFk1oJMX7s= github.com/a-h/templ v0.3.898/go.mod h1:oLBbZVQ6//Q6zpvSMPTuBK0F3qOtBdFBcGRspcT+VNQ= github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= @@ -22,6 +24,18 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3 github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs= +github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= +github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= +github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= +github.com/charmbracelet/log v0.4.2 h1:hYt8Qj6a8yLnvR+h7MwsJv/XvmBJXiueUcI3cIxsyig= +github.com/charmbracelet/log v0.4.2/go.mod h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw= +github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE= +github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q= +github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8= +github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= +github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= +github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= github.com/cli/browser v1.3.0 h1:LejqCrpWr+1pRqmEPDGnTZOjsMe7sehifLynZJuqJpo= github.com/cli/browser v1.3.0/go.mod h1:HH8s+fOAxjhQoBUAsKuPCbqUuxZDhQ2/aD+SzsEfBTk= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= @@ -42,6 +56,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8= github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= +github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= +github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -148,18 +164,24 @@ github.com/lestrrat-go/jwx/v2 v2.0.12/go.mod h1:Mq4KN1mM7bp+5z/W5HS8aCNs5RKZ911G github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= +github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= @@ -196,6 +218,9 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/redis/go-redis/v9 v9.14.0 h1:u4tNCjXOyzfgeLN+vAZaW1xUooqWDqVEsZN0U01jfAE= github.com/redis/go-redis/v9 v9.14.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= @@ -231,6 +256,8 @@ github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11 h1:5HZfQkwe0mIf github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11/go.mod h1:Wlo/SzPmxVp6vXpGt/zaXhHH0fn4IxgqZc82aKg6bpQ= github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e h1:28X54ciEwwUxyHn9yrZfl5ojgF4CBNLWX7LR0rvBkf4= github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= diff --git a/internal/server/app.go b/internal/server/app.go index 4abc452..b2d4907 100644 --- a/internal/server/app.go +++ b/internal/server/app.go @@ -19,6 +19,7 @@ import ( "yoten.app/internal/consumer" "yoten.app/internal/db" "yoten.app/internal/server/config" + "yoten.app/internal/server/log" "yoten.app/internal/server/oauth" "yoten.app/internal/server/views" ) @@ -37,6 +38,7 @@ type Server struct { Posthog posthog.Client IdResolver *atproto.Resolver Session *session.SessionStore + Logger *slog.Logger ComputedData ComputedData } @@ -47,6 +49,8 @@ func (s *Server) Close() error { } func Make(ctx context.Context, config *config.Config) (*Server, error) { + logger := log.FromContext(ctx) + d, err := db.Make(config.Core.DbPath) if err != nil { return nil, err @@ -106,6 +110,7 @@ func Make(ctx context.Context, config *config.Config) (*Server, error) { Posthog: posthog, IdResolver: idResolver, Session: sess, + Logger: logger, ComputedData: computed, } diff --git a/internal/server/log/log.go b/internal/server/log/log.go index 6705eb2..9f9d793 100644 --- a/internal/server/log/log.go +++ b/internal/server/log/log.go @@ -4,16 +4,16 @@ import ( "context" "log/slog" "os" + + "github.com/charmbracelet/log" ) -// NewHandler sets up a new slog.Handler with the service name as an attribute func NewHandler(name string) slog.Handler { - handler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{}) - - var attrs []slog.Attr - attrs = append(attrs, slog.Attr{Key: "service", Value: slog.StringValue(name)}) - handler.WithAttrs(attrs) - return handler + return log.NewWithOptions(os.Stderr, log.Options{ + ReportTimestamp: true, + Prefix: name, + Level: log.DebugLevel, + }) } func New(name string) *slog.Logger { @@ -45,3 +45,21 @@ func FromContext(ctx context.Context) *slog.Logger { return slog.Default() } + +// Sublogger derives a new logger from an existing one by appending a suffix to +// its prefix. +func SubLogger(base *slog.Logger, suffix string) *slog.Logger { + // Try to get the underlying charmbracelet logger. + if cl, ok := base.Handler().(*log.Logger); ok { + prefix := cl.GetPrefix() + if prefix != "" { + prefix = prefix + "/" + suffix + } else { + prefix = suffix + } + return slog.New(NewHandler(prefix)) + } + + // Fallback to no known handler type. + return slog.New(NewHandler(suffix)) +} -- 2.43.0 From e960f66491e170f29dcb99cd28fd0fd0b0e00943 Mon Sep 17 00:00:00 2001 From: brookjeynes Date: Wed, 15 Oct 2025 07:00:44 +1000 Subject: [PATCH] feat(oauth): use slogger Change-Id: luvytrrxvlwmppxwkxksssuppwnmrnsk Signed-off-by: brookjeynes --- internal/server/app.go | 2 +- internal/server/oauth/handler.go | 7 ++++--- internal/server/oauth/oauth.go | 5 ++++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/internal/server/app.go b/internal/server/app.go index b2d4907..8eeb61b 100644 --- a/internal/server/app.go +++ b/internal/server/app.go @@ -63,7 +63,7 @@ func Make(ctx context.Context, config *config.Config) (*Server, error) { idResolver := atproto.DefaultResolver() - oauth, err := oauth.New(config, posthog) + oauth, err := oauth.New(config, posthog, log.SubLogger(logger, "oauth")) if err != nil { return nil, fmt.Errorf("failed to start oauth handler: %w", err) } diff --git a/internal/server/oauth/handler.go b/internal/server/oauth/handler.go index e041bbb..f0243e5 100644 --- a/internal/server/oauth/handler.go +++ b/internal/server/oauth/handler.go @@ -2,7 +2,6 @@ package oauth import ( "encoding/json" - "log" "net/http" "github.com/go-chi/chi/v5" @@ -49,7 +48,7 @@ func (o *OAuth) jwks(w http.ResponseWriter, r *http.Request) { jwks := o.Config.OAuth.Jwks pubKey, err := pubKeyFromJwk(jwks) if err != nil { - log.Printf("failed to parse public key: %v", err) + o.Logger.Error("failed to parse public key", "err", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -68,11 +67,13 @@ func (o *OAuth) callback(w http.ResponseWriter, r *http.Request) { sessData, err := o.ClientApp.ProcessCallback(ctx, r.URL.Query()) if err != nil { + o.Logger.Error("failed to process callback", "err", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } if err := o.SaveSession(w, r, sessData); err != nil { + o.Logger.Error("failed to save session", "err", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -83,7 +84,7 @@ func (o *OAuth) callback(w http.ResponseWriter, r *http.Request) { Event: ph.UserSignInSuccessEvent, }) if err != nil { - log.Println("failed to enqueue posthog event:", err) + o.Logger.Error("failed to enqueue posthog event", "err", err) } } diff --git a/internal/server/oauth/oauth.go b/internal/server/oauth/oauth.go index cfb61ba..1e5a41a 100644 --- a/internal/server/oauth/oauth.go +++ b/internal/server/oauth/oauth.go @@ -3,6 +3,7 @@ package oauth import ( "errors" "fmt" + "log/slog" "net/http" "time" @@ -24,9 +25,10 @@ type OAuth struct { Config *config.Config JwksUri string Posthog posthog.Client + Logger *slog.Logger } -func New(config *config.Config, ph posthog.Client) (*OAuth, error) { +func New(config *config.Config, ph posthog.Client, logger *slog.Logger) (*OAuth, error) { var oauthConfig oauth.ClientConfig var clientUri string @@ -56,6 +58,7 @@ func New(config *config.Config, ph posthog.Client) (*OAuth, error) { SessionStore: sessStore, JwksUri: jwksUri, Posthog: ph, + Logger: logger, }, nil } -- 2.43.0 From 9c09b4466f70bc52a4bf2e7b4dc17a1f81f18d7c Mon Sep 17 00:00:00 2001 From: brookjeynes Date: Wed, 15 Oct 2025 07:07:22 +1000 Subject: [PATCH] feat(db): use slogger Change-Id: ztulxmxuwlttlqstplkxwnxnqqwzlwsx Signed-off-by: brookjeynes --- internal/db/db.go | 11 +++++++---- internal/server/app.go | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/internal/db/db.go b/internal/db/db.go index 7e063bb..90d2512 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -4,6 +4,7 @@ import ( "context" "database/sql" "fmt" + "log/slog" "strings" _ "github.com/mattn/go-sqlite3" @@ -11,6 +12,7 @@ import ( type DB struct { *sql.DB + logger *slog.Logger } type Execer interface { @@ -24,7 +26,7 @@ type Execer interface { PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) } -func Make(dbPath string) (*DB, error) { +func Make(ctx context.Context, dbPath string, logger *slog.Logger) (*DB, error) { opts := []string{ "_foreign_keys=1", "_journal_mode=WAL", @@ -37,8 +39,6 @@ func Make(dbPath string) (*DB, error) { return nil, fmt.Errorf("failed to open db: %w", err) } - ctx := context.Background() - conn, err := db.Conn(ctx) if err != nil { return nil, err @@ -231,5 +231,8 @@ func Make(dbPath string) (*DB, error) { return nil, fmt.Errorf("failed to execute db create statement: %w", err) } - return &DB{db}, nil + return &DB{ + db, + logger, + }, nil } diff --git a/internal/server/app.go b/internal/server/app.go index 8eeb61b..c7cf92f 100644 --- a/internal/server/app.go +++ b/internal/server/app.go @@ -51,7 +51,7 @@ func (s *Server) Close() error { func Make(ctx context.Context, config *config.Config) (*Server, error) { logger := log.FromContext(ctx) - d, err := db.Make(config.Core.DbPath) + d, err := db.Make(ctx, config.Core.DbPath, log.SubLogger(logger, "db")) if err != nil { return nil, err } -- 2.43.0 From b5f6ba79ed86ca13e7f3e76d9ea76057dd56555c Mon Sep 17 00:00:00 2001 From: brookjeynes Date: Wed, 15 Oct 2025 07:15:31 +1000 Subject: [PATCH] feat(handlers/study-session): use slogger Change-Id: quxqummxpuzroymtottwqusltkustlup Signed-off-by: brookjeynes --- internal/server/handlers/study-session.go | 105 ++++++++++++---------- 1 file changed, 58 insertions(+), 47 deletions(-) diff --git a/internal/server/handlers/study-session.go b/internal/server/handlers/study-session.go index 9ed7167..6deba4b 100644 --- a/internal/server/handlers/study-session.go +++ b/internal/server/handlers/study-session.go @@ -3,7 +3,6 @@ package handlers import ( "errors" "fmt" - "log" "net/http" "strconv" "time" @@ -117,9 +116,11 @@ func (h *Handler) parseStudySessionForm(r *http.Request) (db.StudySession, error } func (h *Handler) HandleStudySessionFeed(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleStudySessionFeed") + isFriends, err := strconv.ParseBool(r.URL.Query().Get("friends")) if err != nil { - log.Println("failed to parse friends value:", err) + l.Error("failed to parse friends value", "err", err) isFriends = false } @@ -129,7 +130,7 @@ func (h *Handler) HandleStudySessionFeed(w http.ResponseWriter, r *http.Request) } page, err := strconv.ParseInt(pageStr, 10, 64) if err != nil { - log.Println("failed to parse page value:", err) + l.Error("failed to parse page value", "err", err) page = 1 } if page == 0 { @@ -148,32 +149,32 @@ func (h *Handler) HandleStudySessionFeed(w http.ResponseWriter, r *http.Request) if !isFriends { feed, err = db.GetStudySessionFeed(h.Db, pageSize+1, int(offset)) if err != nil { - log.Println("failed to get global feed:", err) + l.Error("failed to get global feed", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to get global study session feed, try again later.") return } err = h.GetBskyProfileHydratedSessionFeed(feed) if err != nil { - log.Println("failed to hydrate bsky profiles:", err) + l.Error("failed to hydrate bsky profiles", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to get global study session feed, try again later.") return } } else { if fetchUserError != nil { - log.Println("failed to get logged-in user:", err) + l.Error("failed to get logged-in user", "err", err) htmx.HxRedirect(w, "/login") return } feed, err = db.GetFriendsStudySessionFeed(h.Db, user.Did, pageSize+1, int(offset)) if err != nil { - log.Println("failed to get global feed:", err) + l.Error("failed to get global feed", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to get global study session feed, try again later.") return } err = h.GetBskyProfileHydratedSessionFeed(feed) if err != nil { - log.Println("failed to hydrate bsky profiles:", err) + l.Error("failed to hydrate bsky profiles", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to get global study session feed, try again later.") return } @@ -181,7 +182,7 @@ func (h *Handler) HandleStudySessionFeed(w http.ResponseWriter, r *http.Request) feed, err = ApplyPendingChanges(h, w, r, feed, PendingStudySessionCreation, PendingStudySessionUpdates, PendingStudySessionDeletion) if err != nil { - log.Printf("failed to save yoten-session after processing pending changes: %v", err) + l.Error("failed to save yoten-session after processing pending changes", "err", err) } nextPage := 0 @@ -200,16 +201,18 @@ func (h *Handler) HandleStudySessionFeed(w http.ResponseWriter, r *http.Request) } func (h *Handler) HandleEditStudySessionPage(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleEditStudySessionPage") + client, err := h.Oauth.AuthorizedClient(r) if err != nil { - log.Println("failed to get authorized client:", err) + l.Error("failed to get authorized client", "err", err) htmx.HxRedirect(w, "/login") return } user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) if err != nil { - log.Println("failed to get logged-in user:", err) + l.Error("failed to get logged-in user", "err", err) htmx.HxRedirect(w, "/login") return } @@ -217,13 +220,13 @@ func (h *Handler) HandleEditStudySessionPage(w http.ResponseWriter, r *http.Requ rkey := chi.URLParam(r, "rkey") studySession, err := db.GetStudySessionByRkey(h.Db, user.Did, rkey) if err != nil { - log.Println("failed to get study session from db:", err) + l.Error("failed to get study session from db", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to update study session, try again later.") return } if user.Did != studySession.Did { - log.Printf("user '%s' does not own record '%s'", user.Did, studySession.Rkey) + l.Error("user does not own record", "did", user.Did, "sessionDid", studySession.Did) htmx.HxError(w, http.StatusUnauthorized, "You do not have permissions to edit this study session.") return } @@ -232,17 +235,17 @@ func (h *Handler) HandleEditStudySessionPage(w http.ResponseWriter, r *http.Requ case http.MethodGet: userDefinedActivities, err := db.GetActivitiesByDid(h.Db, user.Did) if err != nil { - log.Println("failed to get user-defined activities:", err) + l.Error("failed to get user-defined activities", "err", err) } resources, err := db.GetResourcesByDid(h.Db, user.Did) if err != nil { - log.Println("failed to get user-defined resources:", err) + l.Error("failed to get user-defined resources", "err", err) } preDefinedActivities, err := db.GetPredefinedActivities(h.Db) if err != nil { - log.Println("failed to get pre-defined activities:", err) + l.Error("failed to get pre-defined activities", "err", err) } currentResource := studySession.Resource @@ -267,7 +270,7 @@ func (h *Handler) HandleEditStudySessionPage(w http.ResponseWriter, r *http.Requ languages, err := db.GetProfileLanguages(h.Db, user.Did) if err != nil { - log.Println("failed to fetch profile languages:", err) + l.Error("failed to fetch profile languages", "err", err) } views.EditStudySessionPage(views.EditStudySessionPageParams{ @@ -280,7 +283,7 @@ func (h *Handler) HandleEditStudySessionPage(w http.ResponseWriter, r *http.Requ case http.MethodPost: updatedStudySession, err := h.parseStudySessionForm(r) if err != nil { - log.Println("invalid study session form:", err) + l.Error("invalid study session form", "err", err) htmx.HxError(w, http.StatusBadRequest, "Failed to update study session, ensure all data is valid.") return } @@ -289,7 +292,7 @@ func (h *Handler) HandleEditStudySessionPage(w http.ResponseWriter, r *http.Requ updatedStudySession.CreatedAt = studySession.CreatedAt if err := db.ValidateStudySession(updatedStudySession); err != nil { - log.Println("invalid study session:", err) + l.Error("invalid study session", "err", err) switch { case errors.Is(err, db.ErrSessionDescriptionTooLong): htmx.HxError(w, http.StatusBadRequest, "Study session description cannot be more than 256 characters.") @@ -358,14 +361,14 @@ func (h *Handler) HandleEditStudySessionPage(w http.ResponseWriter, r *http.Requ SwapRecord: cid, }) if err != nil { - log.Println("failed to update study session record:", err) + l.Error("failed to update study session record", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to update study session, try again later.") return } err = SavePendingUpdate(h, w, r, PendingStudySessionUpdates, updatedStudySession) if err != nil { - log.Printf("failed to save yoten-session to add pending study session updates: %v", err) + l.Error("failed to save yoten-session to add pending study session updates", "err", err) } if !h.Config.Core.Dev { @@ -377,7 +380,7 @@ func (h *Handler) HandleEditStudySessionPage(w http.ResponseWriter, r *http.Requ Set("rkey", rkey), }) if err != nil { - log.Println("failed to enqueue posthog event:", err) + l.Error("failed to enqueue posthog event", "err", err) } } @@ -386,16 +389,18 @@ func (h *Handler) HandleEditStudySessionPage(w http.ResponseWriter, r *http.Requ } func (h *Handler) HandleNewStudySessionPage(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleNewStudySessionPage") + user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) if err != nil { - log.Println("failed to get logged-in user:", err) + l.Error("failed to get logged-in user", "err", err) htmx.HxRedirect(w, "/login") return } client, err := h.Oauth.AuthorizedClient(r) if err != nil { - log.Println("failed to get authorized client:", err) + l.Error("failed to get authorized client", "err", err) htmx.HxRedirect(w, "/login") return } @@ -404,18 +409,18 @@ func (h *Handler) HandleNewStudySessionPage(w http.ResponseWriter, r *http.Reque case http.MethodGet: profile, err := db.GetProfile(h.Db, user.Did) if err != nil { - log.Printf("failed to find %s in db: %s", user.Did, err) + l.Error("failed to find user in db", "did", user.Did, "err", err) htmx.HxError(w, http.StatusNotFound, "Failed to find user.") return } userDefinedActivities, err := db.GetActivitiesByDid(h.Db, user.Did) if err != nil { - log.Println("failed to get user-defined activities:", err) + l.Error("failed to get user-defined activities", "err", err) } preDefinedActivities, err := db.GetPredefinedActivities(h.Db) if err != nil { - log.Println("failed to get pre-defined activities:", err) + l.Error("failed to get pre-defined activities", "err", err) } activeActivities := utils.Filter(userDefinedActivities, func(activity db.Activity) bool { return activity.Status != db.Deleted @@ -424,7 +429,7 @@ func (h *Handler) HandleNewStudySessionPage(w http.ResponseWriter, r *http.Reque resources, err := db.GetResourcesByDid(h.Db, user.Did) if err != nil { - log.Println("failed to get user-defined resources:", err) + l.Error("failed to get user-defined resources", "err", err) } activeResources := utils.Filter(resources, func(resource db.Resource) bool { return resource.Status != db.Deleted @@ -440,7 +445,7 @@ func (h *Handler) HandleNewStudySessionPage(w http.ResponseWriter, r *http.Reque case http.MethodPost: newStudySession, err := h.parseStudySessionForm(r) if err != nil { - log.Println("invalid study session form:", err) + l.Error("invalid study session form", "err", err) htmx.HxError(w, http.StatusBadRequest, "Failed to update study session, ensure all data is valid.") return } @@ -459,7 +464,7 @@ func (h *Handler) HandleNewStudySessionPage(w http.ResponseWriter, r *http.Reque } if err := db.ValidateStudySession(newStudySession); err != nil { - log.Println("invalid study session:", err) + l.Error("invalid study session", "err", err) switch { case errors.Is(err, db.ErrSessionDescriptionTooLong): htmx.HxError(w, http.StatusBadRequest, "Study session description cannot be more than 256 characters.") @@ -521,14 +526,14 @@ func (h *Handler) HandleNewStudySessionPage(w http.ResponseWriter, r *http.Reque }, }) if err != nil { - log.Println("failed to create study session record:", err) + l.Error("failed to create study session record", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to create study session, try again later.") return } err = SavePendingCreate(h, w, r, PendingStudySessionCreation, newStudySession) if err != nil { - log.Printf("failed to save yoten-session to add pending study session creation: %v", err) + l.Error("failed to save yoten-session to add pending study session creation", "err", err) } if !h.Config.Core.Dev { @@ -545,7 +550,7 @@ func (h *Handler) HandleNewStudySessionPage(w http.ResponseWriter, r *http.Reque Set("date_is_today", newStudySession.Date.Truncate(24*time.Hour).Equal(time.Now().UTC().In(loc).Truncate(24*time.Hour))), }) if err != nil { - log.Println("failed to enqueue posthog event:", err) + l.Error("failed to enqueue posthog event", "err", err) } } @@ -554,16 +559,18 @@ func (h *Handler) HandleNewStudySessionPage(w http.ResponseWriter, r *http.Reque } func (h *Handler) HandleDeleteStudySession(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleDeleteStudySession") + user := h.Oauth.GetUser(r) if user == nil { - log.Println("failed to get logged-in user") + l.Error("failed to get logged-in user") htmx.HxRedirect(w, "/login") return } client, err := h.Oauth.AuthorizedClient(r) if err != nil { - log.Println("failed to get authorized client:", err) + l.Error("failed to get authorized client", "err", err) htmx.HxError(w, http.StatusUnauthorized, "Failed to delete study session, try again later.") return } @@ -572,7 +579,7 @@ func (h *Handler) HandleDeleteStudySession(w http.ResponseWriter, r *http.Reques case http.MethodDelete: err := r.ParseForm() if err != nil { - log.Println("failed to parse study session delete form:", err) + l.Error("failed to parse study session delete form", "err", err) htmx.HxError(w, http.StatusBadRequest, "Failed to delete study session, try again later.") return } @@ -580,13 +587,13 @@ func (h *Handler) HandleDeleteStudySession(w http.ResponseWriter, r *http.Reques rkey := chi.URLParam(r, "rkey") studySession, err := db.GetStudySessionByRkey(h.Db, user.Did, rkey) if err != nil { - log.Println("failed to get study session from db:", err) + l.Error("failed to get study session from db", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to delete study session, try again later.") return } if user.Did != studySession.Did { - log.Println("failed to delete study session: user does not own record") + l.Error("user does not own record", "did", user.Did, "sessionDid", studySession.Did) htmx.HxError(w, http.StatusUnauthorized, "Failed to delete study session, try again later.") return } @@ -597,14 +604,14 @@ func (h *Handler) HandleDeleteStudySession(w http.ResponseWriter, r *http.Reques Rkey: rkey, }) if err != nil { - log.Println("failed to delete study session from PDS:", err) + l.Error("failed to delete study session from PDS", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to delete study session, try again later.") return } err = SavePendingDelete(h, w, r, PendingStudySessionDeletion, studySession) if err != nil { - log.Printf("failed to save yoten-session to add pending study session deletion: %v", err) + l.Error("failed to save yoten-session to add pending study session deletion", "err", err) } if !h.Config.Core.Dev { @@ -617,7 +624,7 @@ func (h *Handler) HandleDeleteStudySession(w http.ResponseWriter, r *http.Reques Set("session_age_seconds", time.Since(studySession.CreatedAt).Seconds()), }) if err != nil { - log.Println("failed to enqueue posthog event:", err) + l.Error("failed to enqueue posthog event", "err", err) } } @@ -644,6 +651,8 @@ func (h *Handler) GetBskyProfileHydratedSessionFeed(feed []*db.StudySessionFeedI } func (h *Handler) HandleStudySessionPage(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleStudySessionPage") + user, _ := bsky.GetUserWithBskyProfile(h.Oauth, r) didOrHandle := chi.URLParam(r, "user") if didOrHandle == "" { @@ -661,21 +670,21 @@ func (h *Handler) HandleStudySessionPage(w http.ResponseWriter, r *http.Request) studySession, err := db.GetStudySessionByRkey(h.Db, ident.DID.String(), rkey) if err != nil { - log.Println("failed to retrieve study session:", err) + l.Error("failed to retrieve study session", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to retrieve study session, try again later.") return } bskyProfile, err := bsky.GetBskyProfile(ident.DID.String()) if err != nil { - log.Println("failed to retrieve bsky profile for study session:", err) + l.Error("failed to retrieve bsky profile for study session", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to retrieve bsky profile, try again later.") return } profile, err := db.GetProfile(h.Db, ident.DID.String()) if err != nil { - log.Println("failed to retrieve profile for study session:", err) + l.Error("failed to retrieve profile for study session", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to retrieve profile, try again later.") return } @@ -698,6 +707,8 @@ func (h *Handler) HandleStudySessionPage(w http.ResponseWriter, r *http.Request) } func (h *Handler) HandleStudySessionPageCommentFeed(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleStudySessionPageCommentFeed") + user, _ := bsky.GetUserWithBskyProfile(h.Oauth, r) didOrHandle := chi.URLParam(r, "user") @@ -721,7 +732,7 @@ func (h *Handler) HandleStudySessionPageCommentFeed(w http.ResponseWriter, r *ht } page, err := strconv.ParseInt(pageStr, 10, 64) if err != nil { - log.Println("failed to parse page value:", err) + l.Error("failed to parse page value", "err", err) page = 1 } if page == 0 { @@ -733,7 +744,7 @@ func (h *Handler) HandleStudySessionPageCommentFeed(w http.ResponseWriter, r *ht commentFeed, err := db.GetCommentsForSession(h.Db, studySessionUri.String(), pageSize+1, int(offset)) if err != nil { - log.Println("failed to get comment feed:", err) + l.Error("failed to get comment feed", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to get comment feed, try again later.") return } @@ -769,7 +780,7 @@ func (h *Handler) HandleStudySessionPageCommentFeed(w http.ResponseWriter, r *ht populatedCommentFeed, err := h.BuildCommentFeed(finalFeed) if err != nil { - log.Println("failed to populate comment feed:", err) + l.Error("failed to populate comment feed", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to get comment feed, try again later.") return } -- 2.43.0 From ee751d178e3b66f43d93772413ac12ea013427e1 Mon Sep 17 00:00:00 2001 From: brookjeynes Date: Wed, 15 Oct 2025 08:37:43 +1000 Subject: [PATCH] feat(handlers/resource): use slogger Change-Id: lkqonxosrvnsvmzxpkoyysslvumrkpyy Signed-off-by: brookjeynes --- internal/server/handlers/resource.go | 57 +++++++++++++++------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/internal/server/handlers/resource.go b/internal/server/handlers/resource.go index f6935c7..dc92694 100644 --- a/internal/server/handlers/resource.go +++ b/internal/server/handlers/resource.go @@ -3,7 +3,6 @@ package handlers import ( "errors" "fmt" - "log" "net/http" "time" @@ -63,9 +62,11 @@ func parseResourceForm(r *http.Request) (db.Resource, error) { } func (h *Handler) HandleNewResourcePage(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleNewResourcePage") + user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) if err != nil { - log.Println("failed to get logged-in user:", err) + l.Error("failed to get logged-in user", "err", err) htmx.HxRedirect(w, "/login") return } @@ -79,14 +80,14 @@ func (h *Handler) HandleNewResourcePage(w http.ResponseWriter, r *http.Request) case http.MethodPost: client, err := h.Oauth.AuthorizedClient(r) if err != nil { - log.Println("failed to get authorized client:", err) + l.Error("failed to get authorized client", "err", err) htmx.HxRedirect(w, "/login") return } newResource, err := parseResourceForm(r) if err != nil { - log.Println("invalid resource form:", err) + l.Error("invalid resource form", "err", err) htmx.HxError(w, http.StatusBadRequest, "Failed to create resource, ensure all fields contain valid data.") return } @@ -95,7 +96,7 @@ func (h *Handler) HandleNewResourcePage(w http.ResponseWriter, r *http.Request) newResource.CreatedAt = time.Now().UTC() if err := db.ValidateResource(newResource); err != nil { - log.Println("invalid resource definition:", err) + l.Error("invalid resource definition", "err", err) switch { case errors.Is(err, db.ErrResourceTitleEmpty): htmx.HxError(w, http.StatusBadRequest, "Resource must have a title.") @@ -118,7 +119,7 @@ func (h *Handler) HandleNewResourcePage(w http.ResponseWriter, r *http.Request) if newResource.Link != nil { linkCheckResult := google.CheckResourceLinkSafety(*newResource.Link) if linkCheckResult.Err != nil { - log.Println("invalid resource definition:", linkCheckResult.Err) + l.Error("invalid resource definition", "err", linkCheckResult.Err) switch { case errors.Is(linkCheckResult.Err, google.ErrResourceLinkSketchy): if !h.Config.Core.Dev { @@ -186,14 +187,14 @@ func (h *Handler) HandleNewResourcePage(w http.ResponseWriter, r *http.Request) }, }) if err != nil { - log.Println("failed to create resource record:", err) + l.Error("failed to create resource record", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to create resource, try again later.") return } err = SavePendingCreate(h, w, r, PendingResourceCreation, newResource) if err != nil { - log.Printf("failed to save yoten-session to add pending resource creation: %v", err) + l.Error("failed to save yoten-session to add pending resource creation", "err", err) } if !h.Config.Core.Dev { @@ -209,7 +210,7 @@ func (h *Handler) HandleNewResourcePage(w http.ResponseWriter, r *http.Request) Set("link_provided", newResource.Link != nil), }) if err != nil { - log.Println("failed to enqueue posthog event:", err) + l.Error("failed to enqueue posthog event", "err", err) } } @@ -218,15 +219,17 @@ func (h *Handler) HandleNewResourcePage(w http.ResponseWriter, r *http.Request) } func (h *Handler) HandleDeleteResource(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleDeleteResource") + user := h.Oauth.GetUser(r) if user == nil { - log.Println("failed to get logged-in user") + l.Error("failed to get logged-in user") htmx.HxRedirect(w, "/login") return } client, err := h.Oauth.AuthorizedClient(r) if err != nil { - log.Println("failed to get authorized client:", err) + l.Error("failed to get authorized client", "err", err) htmx.HxError(w, http.StatusUnauthorized, "Failed to delete resource, try again later.") return } @@ -236,13 +239,13 @@ func (h *Handler) HandleDeleteResource(w http.ResponseWriter, r *http.Request) { rkey := chi.URLParam(r, "rkey") resource, err := db.GetResourceByRkey(h.Db, user.Did, rkey) if err != nil { - log.Println("failed to get resource from db:", err) + l.Error("failed to get resource from db", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to delete resource, try again later.") return } if user.Did != resource.Did { - log.Printf("user '%s' does not own record '%s'", user.Did, rkey) + l.Error("user does not own record", "did", user.Did, "resourceDid", resource.Did) htmx.HxError(w, http.StatusUnauthorized, "You do not have permissions to delete this resource.") return } @@ -253,14 +256,14 @@ func (h *Handler) HandleDeleteResource(w http.ResponseWriter, r *http.Request) { Rkey: resource.Rkey, }) if err != nil { - log.Println("failed to delete resource from PDS:", err) + l.Error("failed to delete resource from PDS", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to delete resource, try again later.") return } err = SavePendingDelete(h, w, r, PendingResourceDeletion, resource) if err != nil { - log.Printf("failed to save yoten-session to add pending resource deletion: %v", err) + l.Error("failed to save yoten-session to add pending resource deletion", "err", err) } if !h.Config.Core.Dev { @@ -272,7 +275,7 @@ func (h *Handler) HandleDeleteResource(w http.ResponseWriter, r *http.Request) { Set("resource_type", resource.Type), }) if err != nil { - log.Println("failed to enqueue posthog event:", err) + l.Error("failed to enqueue posthog event", "err", err) } } @@ -281,9 +284,11 @@ func (h *Handler) HandleDeleteResource(w http.ResponseWriter, r *http.Request) { } func (h *Handler) HandleEditResourcePage(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleEditResourcePage") + user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) if err != nil { - log.Println("failed to get logged-in user:", err) + l.Error("failed to get logged-in user", "err", err) htmx.HxRedirect(w, "/login") return } @@ -291,13 +296,13 @@ func (h *Handler) HandleEditResourcePage(w http.ResponseWriter, r *http.Request) rkey := chi.URLParam(r, "rkey") resource, err := db.GetResourceByRkey(h.Db, user.Did, rkey) if err != nil { - log.Println("failed to get resource from db:", err) + l.Error("failed to get resource from db", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to update resource, try again later.") return } if user.Did != resource.Did { - log.Printf("user '%s' does not own record '%s'", user.Did, rkey) + l.Error("user does not own record", "did", user.Did, "resourceDid", resource.Did) htmx.HxError(w, http.StatusUnauthorized, "You do not have permissions to edit this resource.") return } @@ -312,14 +317,14 @@ func (h *Handler) HandleEditResourcePage(w http.ResponseWriter, r *http.Request) case http.MethodPost: client, err := h.Oauth.AuthorizedClient(r) if err != nil { - log.Println("failed to get authorized client:", err) + l.Error("failed to get authorized client", "err", err) htmx.HxRedirect(w, "/login") return } updatedResource, err := parseResourceForm(r) if err != nil { - log.Println("invalid resource form:", err) + l.Error("invalid resource form", "err", err) htmx.HxError(w, http.StatusBadRequest, "Failed to create resource, ensure all fields contain valid data.") return } @@ -328,7 +333,7 @@ func (h *Handler) HandleEditResourcePage(w http.ResponseWriter, r *http.Request) updatedResource.CreatedAt = resource.CreatedAt if err := db.ValidateResource(updatedResource); err != nil { - log.Println("invalid resource definition:", err) + l.Error("invalid resource definition", "err", err) switch { case errors.Is(err, db.ErrResourceTitleEmpty): htmx.HxError(w, http.StatusBadRequest, "Resource must have a title.") @@ -352,7 +357,7 @@ func (h *Handler) HandleEditResourcePage(w http.ResponseWriter, r *http.Request) if updatedResource.Link != nil && (resource.Link == nil || *updatedResource.Link != *resource.Link) { linkCheckResult := google.CheckResourceLinkSafety(*updatedResource.Link) if linkCheckResult.Err != nil { - log.Println("invalid resource definition:", linkCheckResult.Err) + l.Error("invalid resource link", "link", resource.Link, "threatType", linkCheckResult.ThreatType, "err", linkCheckResult.Err) switch { case errors.Is(linkCheckResult.Err, google.ErrResourceLinkSketchy): if !h.Config.Core.Dev { @@ -427,14 +432,14 @@ func (h *Handler) HandleEditResourcePage(w http.ResponseWriter, r *http.Request) SwapRecord: cid, }) if err != nil { - log.Println("failed to update resource record:", err) + l.Error("failed to update resource record", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to update resource, try again later.") return } err = SavePendingUpdate(h, w, r, PendingResourceUpdates, updatedResource) if err != nil { - log.Printf("failed to save yoten-session to add pending resource updates: %v", err) + l.Error("failed to save yoten-session to add pending resource updates", "err", err) } if !h.Config.Core.Dev { @@ -450,7 +455,7 @@ func (h *Handler) HandleEditResourcePage(w http.ResponseWriter, r *http.Request) Set("link_provided", updatedResource.Link != nil), }) if err != nil { - log.Println("failed to enqueue posthog event:", err) + l.Error("failed to enqueue posthog event", "err", err) } } -- 2.43.0 From e67fce3271c3fb38102a39274a00393674ca2cfd Mon Sep 17 00:00:00 2001 From: brookjeynes Date: Wed, 15 Oct 2025 08:54:00 +1000 Subject: [PATCH] feat(handlers/comment): use slogger Change-Id: lwxlwylqzwmlrtwsnlvmxvtlotrokkum Signed-off-by: brookjeynes --- internal/server/handlers/comment.go | 63 ++++++++++++++++------------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/internal/server/handlers/comment.go b/internal/server/handlers/comment.go index a1e1078..cd42f0c 100644 --- a/internal/server/handlers/comment.go +++ b/internal/server/handlers/comment.go @@ -1,7 +1,6 @@ package handlers import ( - "log" "net/http" "strings" "time" @@ -23,44 +22,46 @@ import ( ) func (h *Handler) HandleNewComment(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleNewComment") + client, err := h.Oauth.AuthorizedClient(r) if err != nil { - log.Println("failed to get authorized client:", err) + l.Error("failed to get authorized client", "err", err) htmx.HxRedirect(w, "/login") return } user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) if err != nil { - log.Println("failed to get logged-in user:", err) + l.Error("failed to get logged-in user", "err", err) htmx.HxRedirect(w, "/login") return } profile, err := db.GetProfile(h.Db, user.Did) if err != nil { - log.Println("failed to get logged-in user:", err) + l.Error("failed to get logged-in user", "err", err) htmx.HxRedirect(w, "/login") return } err = r.ParseForm() if err != nil { - log.Println("invalid comment form:", err) + l.Error("invalid comment form", "err", err) htmx.HxError(w, http.StatusBadRequest, "Unable to process comment, please try again later.") return } commentBody := r.FormValue("comment") if len(strings.TrimSpace(commentBody)) == 0 { - log.Println("invalid comment form: missing comment body") + l.Error("invalid comment form: missing comment body") htmx.HxError(w, http.StatusBadRequest, "Comment cannot be empty.") return } studySessionUri := r.FormValue("study_session_uri") if len(studySessionUri) == 0 { - log.Println("invalid comment form: missing study session Uri") + l.Error("invalid comment form: missing study session Uri") htmx.HxError(w, http.StatusBadRequest, "Unable to create comment, please try again later.") return } @@ -100,7 +101,7 @@ func (h *Handler) HandleNewComment(w http.ResponseWriter, r *http.Request) { }, }) if err != nil { - log.Println("failed to create comment record:", err) + l.Error("failed to create comment record", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to create comment, try again later.") return } @@ -121,7 +122,7 @@ func (h *Handler) HandleNewComment(w http.ResponseWriter, r *http.Request) { err = h.Posthog.Enqueue(event) if err != nil { - log.Println("failed to enqueue posthog event:", err) + l.Error("failed to enqueue posthog event", "err", err) } } @@ -153,15 +154,17 @@ func (h *Handler) HandleNewComment(w http.ResponseWriter, r *http.Request) { } func (h *Handler) HandleDeleteComment(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleDeleteComment") + user := h.Oauth.GetUser(r) if user == nil { - log.Println("failed to get logged-in user") + l.Error("failed to get logged-in user") htmx.HxRedirect(w, "/login") return } client, err := h.Oauth.AuthorizedClient(r) if err != nil { - log.Println("failed to get authorized client:", err) + l.Error("failed to get authorized client", "err", err) htmx.HxRedirect(w, "/login") return } @@ -171,13 +174,13 @@ func (h *Handler) HandleDeleteComment(w http.ResponseWriter, r *http.Request) { rkey := chi.URLParam(r, "rkey") comment, err := db.GetCommentByRkey(h.Db, user.Did, rkey) if err != nil { - log.Println("failed to get comment from db:", err) + l.Error("failed to get comment from db", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to delete comment, try again later.") return } if user.Did != comment.Did { - log.Printf("user '%s' does not own record '%s'", user.Did, rkey) + l.Error("user does not own record", "did", user.Did, "commentDid", comment.Did) htmx.HxError(w, http.StatusUnauthorized, "You do not have permissions to delete this comment.") return } @@ -188,7 +191,7 @@ func (h *Handler) HandleDeleteComment(w http.ResponseWriter, r *http.Request) { Rkey: comment.Rkey, }) if err != nil { - log.Println("failed to delete comment from PDS:", err) + l.Error("failed to delete comment from PDS", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to delete comment, try again later.") return } @@ -209,7 +212,7 @@ func (h *Handler) HandleDeleteComment(w http.ResponseWriter, r *http.Request) { err = h.Posthog.Enqueue(event) if err != nil { - log.Println("failed to enqueue posthog event:", err) + l.Error("failed to enqueue posthog event", "err", err) } } @@ -218,9 +221,11 @@ func (h *Handler) HandleDeleteComment(w http.ResponseWriter, r *http.Request) { } func (h *Handler) HandleEditCommentPage(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleEditCommentPage") + user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) if err != nil { - log.Println("failed to get logged-in user:", err) + l.Error("failed to get logged-in user", "err", err) htmx.HxRedirect(w, "/login") return } @@ -228,13 +233,13 @@ func (h *Handler) HandleEditCommentPage(w http.ResponseWriter, r *http.Request) rkey := chi.URLParam(r, "rkey") comment, err := db.GetCommentByRkey(h.Db, user.Did, rkey) if err != nil { - log.Println("failed to get comment from db:", err) + l.Error("failed to get comment from db", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to update comment, try again later.") return } if user.Did != comment.Did { - log.Printf("user '%s' does not own record '%s'", user.Did, rkey) + l.Error("user does not own record", "did", user.Did, "commentDid", comment.Did) htmx.HxError(w, http.StatusUnauthorized, "You do not have permissions to edit this comment.") return } @@ -245,21 +250,21 @@ func (h *Handler) HandleEditCommentPage(w http.ResponseWriter, r *http.Request) case http.MethodPost: client, err := h.Oauth.AuthorizedClient(r) if err != nil { - log.Println("failed to get authorized client:", err) + l.Error("failed to get authorized client", "err", err) htmx.HxRedirect(w, "/login") return } err = r.ParseForm() if err != nil { - log.Println("invalid comment form:", err) + l.Error("invalid comment form", "err", err) htmx.HxError(w, http.StatusBadRequest, "Unable to process comment, please try again later.") return } commentBody := r.FormValue("comment") if len(strings.TrimSpace(commentBody)) == 0 { - log.Println("invalid comment form: missing comment body") + l.Error("invalid comment form: missing comment body") htmx.HxError(w, http.StatusBadRequest, "Comment cannot be empty.") return } @@ -303,7 +308,7 @@ func (h *Handler) HandleEditCommentPage(w http.ResponseWriter, r *http.Request) SwapRecord: cid, }) if err != nil { - log.Println("failed to update study session record:", err) + l.Error("failed to update study session record", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to update comment, try again later.") return } @@ -324,7 +329,7 @@ func (h *Handler) HandleEditCommentPage(w http.ResponseWriter, r *http.Request) err = h.Posthog.Enqueue(event) if err != nil { - log.Println("failed to enqueue posthog event:", err) + l.Error("failed to enqueue posthog event", "err", err) } } @@ -387,9 +392,11 @@ func assembleCommentFeed(localComments []db.CommentWithLocalProfile, bskyProfile } func (h *Handler) HandleReply(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleReply") + user := h.Oauth.GetUser(r) if user == nil { - log.Println("failed to get logged-in user") + l.Error("failed to get logged-in user") htmx.HxRedirect(w, "/login") return } @@ -397,7 +404,7 @@ func (h *Handler) HandleReply(w http.ResponseWriter, r *http.Request) { studySessionUri := r.URL.Query().Get("root") parentCommentUri := r.URL.Query().Get("parent") if len(studySessionUri) == 0 || len(parentCommentUri) == 0 { - log.Println("invalid reply form: study session uri or parent comment uri is empty") + l.Error("invalid reply form: study session uri or parent comment uri is empty") htmx.HxError(w, http.StatusBadRequest, "Unable to process comment, please try again later.") return } @@ -409,9 +416,11 @@ func (h *Handler) HandleReply(w http.ResponseWriter, r *http.Request) { } func (h *Handler) HandleCancelCommentEdit(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleCancelCommentEdit") + user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) if err != nil { - log.Println("failed to get logged-in user:", err) + l.Error("failed to get logged-in user", "err", err) htmx.HxRedirect(w, "/login") return } @@ -419,7 +428,7 @@ func (h *Handler) HandleCancelCommentEdit(w http.ResponseWriter, r *http.Request rkey := chi.URLParam(r, "rkey") comment, err := db.GetCommentByRkey(h.Db, user.Did, rkey) if err != nil { - log.Println("failed to get comment from db:", err) + l.Error("failed to get comment from db", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to update comment, try again later.") return } -- 2.43.0 From 7ae34f5bf384c6393f8c066df428bd134b9034b9 Mon Sep 17 00:00:00 2001 From: brookjeynes Date: Wed, 15 Oct 2025 11:34:04 +1000 Subject: [PATCH] feat(handlers/follow): use slogger Change-Id: oknuzwklolrpwpsyynwvsmyvwxotxmxu Signed-off-by: brookjeynes --- internal/server/handlers/follow.go | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/internal/server/handlers/follow.go b/internal/server/handlers/follow.go index c332ea7..3032e89 100644 --- a/internal/server/handlers/follow.go +++ b/internal/server/handlers/follow.go @@ -1,7 +1,6 @@ package handlers import ( - "log" "net/http" "time" @@ -20,16 +19,18 @@ import ( ) func (h *Handler) HandleFollow(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleFollow") + client, err := h.Oauth.AuthorizedClient(r) if err != nil { - log.Println("failed to get authorized client:", err) + l.Error("failed to get authorized client", "err", err) htmx.HxRedirect(w, "/login") return } user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) if err != nil { - log.Println("failed to get logged-in user:", err) + l.Error("failed to get logged-in user", "err", err) htmx.HxRedirect(w, "/login") return } @@ -42,13 +43,13 @@ func (h *Handler) HandleFollow(w http.ResponseWriter, r *http.Request) { subjectIdent, err := h.IdResolver.ResolveIdent(r.Context(), subject) if err != nil { - log.Println("failed to follow, invalid did:", err) + l.Error("failed to follow, invalid did", "err", err) htmx.HxError(w, http.StatusBadRequest, "Failed to follow profile, try again later.") return } if user.Did == subjectIdent.DID.String() { - log.Println("failed to follow, cannot follow yourself") + l.Error("failed to follow, cannot follow yourself") htmx.HxError(w, http.StatusBadRequest, "You cannot follow yourself.") return } @@ -68,7 +69,7 @@ func (h *Handler) HandleFollow(w http.ResponseWriter, r *http.Request) { }}, }) if err != nil { - log.Println("failed to create follow record:", err) + l.Error("failed to create follow record", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to follow profile, try again later.") return } @@ -84,7 +85,7 @@ func (h *Handler) HandleFollow(w http.ResponseWriter, r *http.Request) { Set("is_mutual_follow", followStatus == db.IsMutual), }) if err != nil { - log.Println("failed to enqueue posthog event:", err) + l.Error("failed to enqueue posthog event", "err", err) } } @@ -95,7 +96,7 @@ func (h *Handler) HandleFollow(w http.ResponseWriter, r *http.Request) { case http.MethodDelete: follow, err := db.GetFollow(h.Db, user.Did, subjectIdent.DID.String()) if err != nil { - log.Println("failed to get follow relationship:", err) + l.Error("failed to get follow relationship", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to unfollow profile, try again later.") return } @@ -106,7 +107,7 @@ func (h *Handler) HandleFollow(w http.ResponseWriter, r *http.Request) { Rkey: follow.Rkey, }) if err != nil { - log.Println("failed to delete follow record:", err) + l.Error("failed to delete follow record", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to unfollow profile, try again later.") return } @@ -119,7 +120,7 @@ func (h *Handler) HandleFollow(w http.ResponseWriter, r *http.Request) { Set("subject_did", subjectIdent.DID.String()), }) if err != nil { - log.Println("failed to enqueue posthog event:", err) + l.Error("failed to enqueue posthog event", "err", err) } } -- 2.43.0 From 226346a65be5b9c8c684dcc0df83ca578d414b8f Mon Sep 17 00:00:00 2001 From: brookjeynes Date: Wed, 15 Oct 2025 11:46:13 +1000 Subject: [PATCH] feat(handlers/activity): use slogger Change-Id: xotllokmvtvnmxtxmvxkstmwplxmsspo Signed-off-by: brookjeynes --- internal/server/handlers/activity.go | 53 +++++++++++++++------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/internal/server/handlers/activity.go b/internal/server/handlers/activity.go index f519ff2..d4f2ac9 100644 --- a/internal/server/handlers/activity.go +++ b/internal/server/handlers/activity.go @@ -3,7 +3,6 @@ package handlers import ( "errors" "fmt" - "log" "net/http" "time" @@ -55,9 +54,11 @@ func parseActivityForm(r *http.Request) (db.Activity, error) { } func (h *Handler) HandleNewActivityPage(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleNewActivityPage") + user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) if err != nil { - log.Println("failed to get logged-in user:", err) + l.Error("failed to get logged-in user", "err", err) htmx.HxRedirect(w, "/login") return } @@ -71,14 +72,14 @@ func (h *Handler) HandleNewActivityPage(w http.ResponseWriter, r *http.Request) case http.MethodPost: client, err := h.Oauth.AuthorizedClient(r) if err != nil { - log.Println("failed to get authorized client:", err) + l.Error("failed to get authorized client", "err", err) htmx.HxRedirect(w, "/login") return } newActivity, err := parseActivityForm(r) if err != nil { - log.Println("invalid activity form:", err) + l.Error("invalid activity form", "err", err) htmx.HxError(w, http.StatusBadRequest, "Failed to create activity, ensure all fields contain valid data.") return } @@ -87,7 +88,7 @@ func (h *Handler) HandleNewActivityPage(w http.ResponseWriter, r *http.Request) newActivity.CreatedAt = time.Now().UTC() if err := db.ValidateActivity(newActivity); err != nil { - log.Println("invalid activity def:", err) + l.Error("invalid activity def", "err", err) switch { case errors.Is(err, db.ErrActivityNameEmpty): htmx.HxError(w, http.StatusBadRequest, "Activity must have a name.") @@ -123,14 +124,14 @@ func (h *Handler) HandleNewActivityPage(w http.ResponseWriter, r *http.Request) }, }) if err != nil { - log.Println("failed to create activity record:", err) + l.Error("failed to create activity record", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to create activity, try again later.") return } err = SavePendingCreate(h, w, r, PendingActivityCreation, newActivity) if err != nil { - log.Printf("failed to save yoten-session to add pending activity creation: %v", err) + l.Error("failed to save yoten-session to add pending activity creation", "err", err) } if !h.Config.Core.Dev { @@ -145,7 +146,7 @@ func (h *Handler) HandleNewActivityPage(w http.ResponseWriter, r *http.Request) Set("category_count", len(categoriesString)), }) if err != nil { - log.Println("failed to enqueue posthog event:", err) + l.Error("failed to enqueue posthog event", "err", err) } } @@ -154,15 +155,17 @@ func (h *Handler) HandleNewActivityPage(w http.ResponseWriter, r *http.Request) } func (h *Handler) HandleDeleteActivity(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleDeleteActivity") + user := h.Oauth.GetUser(r) if user == nil { - log.Println("failed to get logged-in user") + l.Error("failed to get logged-in user") htmx.HxRedirect(w, "/login") return } client, err := h.Oauth.AuthorizedClient(r) if err != nil { - log.Println("failed to get authorized client:", err) + l.Error("failed to get authorized client", "err", err) htmx.HxError(w, http.StatusUnauthorized, "Failed to delete activity, try again later.") return } @@ -172,13 +175,13 @@ func (h *Handler) HandleDeleteActivity(w http.ResponseWriter, r *http.Request) { rkey := chi.URLParam(r, "rkey") activity, err := db.GetActivityByRkey(h.Db, user.Did, rkey) if err != nil { - log.Println("failed to get activity from db:", err) + l.Error("failed to get activity from db", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to delete activity, try again later.") return } if user.Did != activity.Did { - log.Printf("user '%s' does not own record '%s'", user.Did, rkey) + l.Error("user does not own record", "did", user.Did, "activityDid", activity.Did) htmx.HxError(w, http.StatusUnauthorized, "You do not have permissions to edit this activity.") return } @@ -189,14 +192,14 @@ func (h *Handler) HandleDeleteActivity(w http.ResponseWriter, r *http.Request) { Rkey: activity.Rkey, }) if err != nil { - log.Println("failed to delete activity from PDS:", err) + l.Error("failed to delete activity from PDS", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to delete activity, try again later.") return } err = SavePendingDelete(h, w, r, PendingActivityDeletion, activity) if err != nil { - log.Printf("failed to save yoten-session to add pending activity deletion: %v", err) + l.Error("failed to save yoten-session to add pending activity deletion", "err", err) } if !h.Config.Core.Dev { @@ -207,7 +210,7 @@ func (h *Handler) HandleDeleteActivity(w http.ResponseWriter, r *http.Request) { Set("activity_id", activity.ID), }) if err != nil { - log.Println("failed to enqueue posthog event:", err) + l.Error("failed to enqueue posthog event", "err", err) } } @@ -216,9 +219,11 @@ func (h *Handler) HandleDeleteActivity(w http.ResponseWriter, r *http.Request) { } func (h *Handler) HandleEditActivityPage(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleEditActivityPage") + user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) if err != nil { - log.Println("failed to get logged-in user:", err) + l.Error("failed to get logged-in user", "err", err) htmx.HxRedirect(w, "/login") return } @@ -226,13 +231,13 @@ func (h *Handler) HandleEditActivityPage(w http.ResponseWriter, r *http.Request) rkey := chi.URLParam(r, "rkey") activity, err := db.GetActivityByRkey(h.Db, user.Did, rkey) if err != nil { - log.Println("failed to get activity from db:", err) + l.Error("failed to get activity from db", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to update activity, try again later.") return } if user.Did != activity.Did { - log.Printf("user '%s' does not own record '%s'", user.Did, rkey) + l.Error("user does not own record", "did", user.Did, "activityDid", activity.Did) htmx.HxError(w, http.StatusUnauthorized, "You do not have permissions to edit this activity.") return } @@ -247,14 +252,14 @@ func (h *Handler) HandleEditActivityPage(w http.ResponseWriter, r *http.Request) case http.MethodPost: client, err := h.Oauth.AuthorizedClient(r) if err != nil { - log.Println("failed to get authorized client:", err) + l.Error("failed to get authorized client", "err", err) htmx.HxRedirect(w, "/login") return } updatedActivity, err := parseActivityForm(r) if err != nil { - log.Println("invalid activity form:", err) + l.Error("invalid activity form", "err", err) htmx.HxError(w, http.StatusBadRequest, "Failed to create activity, ensure all fields contain valid data.") return } @@ -263,7 +268,7 @@ func (h *Handler) HandleEditActivityPage(w http.ResponseWriter, r *http.Request) updatedActivity.CreatedAt = activity.CreatedAt if err := db.ValidateActivity(updatedActivity); err != nil { - log.Println("invalid activity def:", err) + l.Error("invalid activity def", "err", err) switch { case errors.Is(err, db.ErrActivityNameEmpty): htmx.HxError(w, http.StatusBadRequest, "Activity must have a name.") @@ -306,14 +311,14 @@ func (h *Handler) HandleEditActivityPage(w http.ResponseWriter, r *http.Request) SwapRecord: cid, }) if err != nil { - log.Println("failed to update study session record:", err) + l.Error("failed to update study session record", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to update activity, try again later.") return } err = SavePendingUpdate(h, w, r, PendingActivityUpdates, updatedActivity) if err != nil { - log.Printf("failed to save yoten-session to add pending activity updates: %v", err) + l.Error("failed to save yoten-session to add pending activity updates", "err", err) } if !h.Config.Core.Dev { @@ -328,7 +333,7 @@ func (h *Handler) HandleEditActivityPage(w http.ResponseWriter, r *http.Request) Set("category_count", len(categoriesString)), }) if err != nil { - log.Println("failed to enqueue posthog event:", err) + l.Error("failed to enqueue posthog event", "err", err) } } -- 2.43.0 From 9fe76f71a3c5683b9af4189645164c1503fef40a Mon Sep 17 00:00:00 2001 From: brookjeynes Date: Wed, 15 Oct 2025 11:48:52 +1000 Subject: [PATCH] feat(handlers/login): use slogger Change-Id: krvowxppymsqsvupksrkzpquksqtprmw Signed-off-by: brookjeynes --- internal/server/handlers/login.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/internal/server/handlers/login.go b/internal/server/handlers/login.go index 0d8e7d4..467bd17 100644 --- a/internal/server/handlers/login.go +++ b/internal/server/handlers/login.go @@ -3,7 +3,6 @@ package handlers import ( "context" "fmt" - "log" "net/http" "strings" @@ -17,6 +16,8 @@ import ( ) func (h *Handler) Login(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "Login") + switch r.Method { case http.MethodGet: var user *types.User @@ -24,7 +25,7 @@ func (h *Handler) Login(w http.ResponseWriter, r *http.Request) { if oauth != nil { bskyProfile, err := bsky.GetBskyProfile(oauth.Did) if err != nil { - log.Println("failed to get bsky profile:", err) + l.Error("failed to get bsky profile", "err", err) } user = &types.User{ OauthUser: *oauth, @@ -54,7 +55,7 @@ func (h *Handler) Login(w http.ResponseWriter, r *http.Request) { // Basic handle validation if !strings.Contains(handle, ".") { - log.Println("invalid handle format:", handle) + l.Error("invalid handle format", "err", handle) htmx.HxError(w, http.StatusBadGateway, fmt.Sprintf("'%s' is an invalid handle. Did you mean %s.bsky.social?", handle, handle)) return } @@ -67,7 +68,7 @@ func (h *Handler) Login(w http.ResponseWriter, r *http.Request) { Event: ph.UserSignInInitiatedEvent, }) if err != nil { - log.Println("failed to enqueue posthog event:", err) + l.Error("failed to enqueue posthog event", "err", err) } } } @@ -83,13 +84,15 @@ func (h *Handler) Login(w http.ResponseWriter, r *http.Request) { } func (h *Handler) Logout(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "Logout") + did := h.Oauth.GetDid(r) err := h.Oauth.DeleteSession(w, r) if err != nil { - log.Println("failed to logout", "err", err) + l.Error("failed to logout", "err", err) } else { - log.Println("logged out successfully") + l.Error("logged out successfully") } if !h.Config.Core.Dev && did != "" { @@ -98,7 +101,7 @@ func (h *Handler) Logout(w http.ResponseWriter, r *http.Request) { Event: ph.UserLoggedOutEvent, }) if err != nil { - log.Println("failed to enqueue posthog event:", err) + l.Error("failed to enqueue posthog event", "err", err) } } -- 2.43.0 From 5888672ce5f9d7987e6d1337cb2431acfe60c320 Mon Sep 17 00:00:00 2001 From: brookjeynes Date: Wed, 15 Oct 2025 11:49:39 +1000 Subject: [PATCH] feat(handlers/notification): use slogger Change-Id: ryrmqnpsuytzmqulyvkwvxpvllvryxms Signed-off-by: brookjeynes --- internal/server/handlers/notification.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/internal/server/handlers/notification.go b/internal/server/handlers/notification.go index 3cbc757..d7a59bd 100644 --- a/internal/server/handlers/notification.go +++ b/internal/server/handlers/notification.go @@ -1,7 +1,6 @@ package handlers import ( - "log" "net/http" "strconv" @@ -12,9 +11,11 @@ import ( ) func (h *Handler) HandleNotificationFeed(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleNotificationFeed") + user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) if err != nil { - log.Println("failed to get logged-in user:", err) + l.Error("failed to get logged-in user", "err", err) htmx.HxRedirect(w, "/login") return } @@ -25,7 +26,7 @@ func (h *Handler) HandleNotificationFeed(w http.ResponseWriter, r *http.Request) } page, err := strconv.ParseInt(pageStr, 10, 64) if err != nil { - log.Println("failed to parse page value:", err) + l.Error("failed to parse page value", "err", err) page = 1 } if page == 0 { @@ -39,14 +40,14 @@ func (h *Handler) HandleNotificationFeed(w http.ResponseWriter, r *http.Request) case http.MethodGet: notifications, err := db.GetNotificationsByDid(h.Db, user.Did, pageSize+1, int(offset)) if err != nil { - log.Println("failed to retrieve notifications:", err) + l.Error("failed to retrieve notifications", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to get notifications, try again later.") return } hydratedNotifications, err := h.getBskyProfileHydratedNotificationFeed(notifications) if err != nil { - log.Println("failed to hydrate notifications with bsky profile:", err) + l.Error("failed to hydrate notifications with bsky profile", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to get notifications, try again later.") return } @@ -66,16 +67,18 @@ func (h *Handler) HandleNotificationFeed(w http.ResponseWriter, r *http.Request) } func (h *Handler) HandleNotificationMarkAllRead(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleNotificationMarkAllRead") + user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) if err != nil { - log.Println("failed to get logged-in user:", err) + l.Error("failed to get logged-in user", "err", err) htmx.HxRedirect(w, "/login") return } err = db.MarkAllNotificationsAsRead(h.Db, user.Did) if err != nil { - log.Println("failed to mark all notifications:", err) + l.Error("failed to mark all notifications", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to mark all notifications as read, try again later.") return } -- 2.43.0 From a6d903dc567071353e5014bea4537a9b9c74a80d Mon Sep 17 00:00:00 2001 From: brookjeynes Date: Wed, 15 Oct 2025 11:50:39 +1000 Subject: [PATCH] feat(handlers/profile): use slogger Change-Id: rowvvuplwxlmvtvkskrqmrvmzmxopyps Signed-off-by: brookjeynes --- internal/server/handlers/profile.go | 96 +++++++++++++++++------------ 1 file changed, 56 insertions(+), 40 deletions(-) diff --git a/internal/server/handlers/profile.go b/internal/server/handlers/profile.go index 369b148..76f51ed 100644 --- a/internal/server/handlers/profile.go +++ b/internal/server/handlers/profile.go @@ -3,7 +3,7 @@ package handlers import ( "errors" "fmt" - "log" + "log/slog" "net/http" "strconv" "time" @@ -30,7 +30,7 @@ const ( PendingProfileUpdate string = "pending_profile_update" ) -func parseProfileForm(r *http.Request) (db.Profile, error) { +func parseProfileForm(r *http.Request, logger *slog.Logger) (db.Profile, error) { err := r.ParseForm() if err != nil { return db.Profile{}, fmt.Errorf("invalid profile form: %w", err) @@ -45,7 +45,7 @@ func parseProfileForm(r *http.Request) (db.Profile, error) { for _, code := range languageCodes { language, ok := db.Languages[db.LanguageCode(code)] if !ok { - log.Printf("invalid language code submitted: %s", code) + logger.Warn("invalid language code submitted", "languageCode", code) continue } languages = append(languages, language) @@ -62,6 +62,8 @@ func parseProfileForm(r *http.Request) (db.Profile, error) { } func (h *Handler) HandleProfilePage(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleProfilePage") + didOrHandle := chi.URLParam(r, "user") if didOrHandle == "" { http.Error(w, "Bad request", http.StatusBadRequest) @@ -102,22 +104,22 @@ func (h *Handler) HandleProfilePage(w http.ResponseWriter, r *http.Request) { totalStudyTime, err = db.GetTotalStudyTime(h.Db, profileDid) if err != nil { - log.Println("failed to get total study time:", err) + l.Error("failed to get total study time", "err", err) } totalStudySessions, _ = db.GetTotalStudySessions(h.Db, profileDid) if err != nil { - log.Println("failed to get total study study sessions:", err) + l.Error("failed to get total study study sessions", "err", err) } followers, following, _ = db.GetFollowerFollowingCount(h.Db, profileDid) if err != nil { - log.Println("failed to get follow stats:", err) + l.Error("failed to get follow stats", "err", err) } streak, _ = db.GetCurrentStreak(h.Db, profileDid) if err != nil { - log.Println("failed to get streak:", err) + l.Error("failed to get streak", "err", err) } if user != nil { @@ -128,7 +130,7 @@ func (h *Handler) HandleProfilePage(w http.ResponseWriter, r *http.Request) { }) if err := g.Wait(); err != nil { - log.Printf("failed to fetch critical profile data for %s: %v", profileDid, err) + l.Error("failed to fetch critical profile data for", "did", profileDid, "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to fetch profile data, try again later.") return } @@ -160,7 +162,7 @@ func (h *Handler) HandleProfilePage(w http.ResponseWriter, r *http.Request) { err := h.Posthog.Enqueue(capture) if err != nil { - log.Println("failed to enqueue posthog event:", err) + l.Error("failed to enqueue posthog event", "err", err) } } @@ -178,23 +180,25 @@ func (h *Handler) HandleProfilePage(w http.ResponseWriter, r *http.Request) { } func (h *Handler) HandleEditProfilePage(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleEditProfilePage") + user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) if err != nil { - log.Println("failed to get logged-in user:", err) + l.Error("failed to get logged-in user", "err", err) htmx.HxRedirect(w, "/login") return } profile, err := h.GetUserProfileWithAvatar(user.Did) if err != nil { - log.Printf("failed to find %s in db: %s", user.Did, err) + l.Error("failed to find user in db", "did", user.Did, "err", err) w.WriteHeader(http.StatusNotFound) views.NotFoundPage(views.NotFoundPageParams{}).Render(r.Context(), w) return } if user.Did != profile.Did { - log.Printf("user '%s' does not own record '%s'", user.Did, profile.Did) + l.Error("user does not own record", "did", user.Did, "profileDid", profile.Did) htmx.HxError(w, http.StatusUnauthorized, "You do not have permissions to edit this profile.") return } @@ -214,14 +218,14 @@ func (h *Handler) HandleEditProfilePage(w http.ResponseWriter, r *http.Request) case http.MethodPost: client, err := h.Oauth.AuthorizedClient(r) if err != nil { - log.Println("failed to get authorized client:", err) + l.Error("failed to get authorized client", "err", err) htmx.HxRedirect(w, "/login") return } - updatedProfile, err := parseProfileForm(r) + updatedProfile, err := parseProfileForm(r, l) if err != nil { - log.Println("invalid profile form:", err) + l.Error("invalid profile form", "err", err) htmx.HxError(w, http.StatusBadRequest, "Failed to update profile, ensure all fields contain valid data.") return } @@ -236,7 +240,7 @@ func (h *Handler) HandleEditProfilePage(w http.ResponseWriter, r *http.Request) } if err := db.ValidateProfile(updatedProfile); err != nil { - log.Println("invalid profile:", err) + l.Error("invalid profile", "err", err) switch { case errors.Is(err, db.ErrProfileNameTooLong): htmx.HxError(w, http.StatusBadRequest, "Profile name cannot be more than 64 characters.") @@ -280,14 +284,14 @@ func (h *Handler) HandleEditProfilePage(w http.ResponseWriter, r *http.Request) SwapRecord: cid, }) if err != nil { - log.Println("failed to put profile record:", err) + l.Error("failed to put profile record", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to update PDS, try again later.") return } err = SavePendingUpdate(h, w, r, PendingProfileUpdate, profile) if err != nil { - log.Printf("failed to save yoten-session to add pending profile update: %v", err) + l.Error("failed to save yoten-session to add pending profile update", "err", err) } if !h.Config.Core.Dev { @@ -308,7 +312,7 @@ func (h *Handler) HandleEditProfilePage(w http.ResponseWriter, r *http.Request) Properties: properties, }) if err != nil { - log.Println("failed to enqueue posthog identify event:", err) + l.Error("failed to enqueue posthog identify event", "err", err) } err = h.Posthog.Enqueue(posthog.Capture{ @@ -316,7 +320,7 @@ func (h *Handler) HandleEditProfilePage(w http.ResponseWriter, r *http.Request) Event: ph.ProfileRecordEditedEvent, }) if err != nil { - log.Println("failed to enqueue posthog event:", err) + l.Error("failed to enqueue posthog event", "err", err) } } @@ -325,23 +329,25 @@ func (h *Handler) HandleEditProfilePage(w http.ResponseWriter, r *http.Request) } func (h *Handler) HandleResourcesPage(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleResourcesPage") + user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) if err != nil { - log.Println("failed to get logged-in user:", err) + l.Error("failed to get logged-in user", "err", err) htmx.HxRedirect(w, "/login") return } resources, err := db.GetResourcesByDid(h.Db, user.Did) if err != nil { - log.Println("failed to get resources:", err) + l.Error("failed to get resources", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to retrieve profile resources, try again later.") return } resources, err = ApplyPendingChanges(h, w, r, resources, PendingResourceCreation, PendingResourceUpdates, PendingResourceDeletion) if err != nil { - log.Printf("failed to save yoten-session after processing pending changes: %v", err) + l.Error("failed to save yoten-session after processing pending changes", "err", err) } activeResources := utils.Filter(resources, func(resource db.Resource) bool { @@ -355,23 +361,25 @@ func (h *Handler) HandleResourcesPage(w http.ResponseWriter, r *http.Request) { } func (h *Handler) HandleActivitiesPage(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleActivitiesPage") + user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) if err != nil { - log.Println("failed to get logged-in user:", err) + l.Error("failed to get logged-in user", "err", err) htmx.HxRedirect(w, "/login") return } activities, err := db.GetActivitiesByDid(h.Db, user.Did) if err != nil { - log.Println("failed to get activities:", err) + l.Error("failed to get activities", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to retrieve profile activities, try again later.") return } activities, err = ApplyPendingChanges(h, w, r, activities, PendingActivityCreation, PendingActivityUpdates, PendingActivityDeletion) if err != nil { - log.Printf("failed to save yoten-session after processing pending changes: %v", err) + l.Error("failed to save yoten-session after processing pending changes", "err", err) } activeActivities := utils.Filter(activities, func(activity db.Activity) bool { @@ -400,6 +408,8 @@ func (h *Handler) GetUserProfileWithAvatar(did string) (db.Profile, error) { } func (h *Handler) HandleProfileFeed(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleProfileFeed") + didOrHandle := chi.URLParam(r, "user") if didOrHandle == "" { http.Error(w, "Bad request", http.StatusBadRequest) @@ -415,7 +425,7 @@ func (h *Handler) HandleProfileFeed(w http.ResponseWriter, r *http.Request) { profile, err := h.GetUserProfileWithAvatar(ident.DID.String()) if err != nil { - log.Printf("failed to find %s in db: %s", ident.DID.String(), err) + l.Error("failed to find user in db", "did", ident.DID.String(), "err", err) w.WriteHeader(http.StatusNotFound) views.NotFoundPage(views.NotFoundPageParams{}).Render(r.Context(), w) return @@ -423,7 +433,7 @@ func (h *Handler) HandleProfileFeed(w http.ResponseWriter, r *http.Request) { bskyProfile, err := bsky.GetBskyProfile(ident.DID.String()) if err != nil { - log.Println("failed to get bsky profile:", err) + l.Error("failed to get bsky profile", "err", err) w.WriteHeader(http.StatusNotFound) views.NotFoundPage(views.NotFoundPageParams{}).Render(r.Context(), w) return @@ -435,7 +445,7 @@ func (h *Handler) HandleProfileFeed(w http.ResponseWriter, r *http.Request) { } page, err := strconv.ParseInt(pageStr, 10, 64) if err != nil { - log.Println("failed to parse page value:", err) + l.Error("failed to parse page value", "err", err) page = 1 } if page == 0 { @@ -447,7 +457,7 @@ func (h *Handler) HandleProfileFeed(w http.ResponseWriter, r *http.Request) { sessions, err := db.GetStudySessionLogs(h.Db, ident.DID.String(), pageSize+1, int(offset)) if err != nil { - log.Println("failed to get study sessions:", err) + l.Error("failed to get study sessions", "err", err) w.WriteHeader(http.StatusNotFound) views.NotFoundPage(views.NotFoundPageParams{}).Render(r.Context(), w) return @@ -455,7 +465,7 @@ func (h *Handler) HandleProfileFeed(w http.ResponseWriter, r *http.Request) { sessions, err = ApplyPendingChanges(h, w, r, sessions, PendingStudySessionCreation, PendingStudySessionUpdates, PendingStudySessionDeletion) if err != nil { - log.Printf("failed to save yoten-session after processing pending changes: %v", err) + l.Error("failed to save yoten-session after processing pending changes", "err", err) } feed := []*db.StudySessionFeedItem{} @@ -493,16 +503,18 @@ func (h *Handler) HandleProfileFeed(w http.ResponseWriter, r *http.Request) { } func (h *Handler) HandleFriendsPage(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleFriendsPage") + user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) if err != nil { - log.Println("failed to get logged-in user:", err) + l.Error("failed to get logged-in user", "err", err) htmx.HxRedirect(w, "/login") return } followers, following, err := db.GetFollowerFollowingCount(h.Db, user.Did) if err != nil { - log.Printf("getting follow stats repos for %s: %s", user.Did, err) + l.Error("failed to get follow stats", "err", err) } views.FriendsPage(views.FriendsPageParams{ @@ -513,9 +525,11 @@ func (h *Handler) HandleFriendsPage(w http.ResponseWriter, r *http.Request) { } func (h *Handler) HandleFriendsFeed(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleFriendsFeed") + user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) if err != nil { - log.Println("failed to get logged-in user") + l.Error("failed to get logged-in user") htmx.HxRedirect(w, "/login") return } @@ -529,7 +543,7 @@ func (h *Handler) HandleFriendsFeed(w http.ResponseWriter, r *http.Request) { } page, err := strconv.ParseInt(pageStr, 10, 64) if err != nil { - log.Println("failed to parse page value:", err) + l.Error("failed to parse page value", "err", err) page = 1 } if page == 0 { @@ -545,26 +559,26 @@ func (h *Handler) HandleFriendsFeed(w http.ResponseWriter, r *http.Request) { if mode == partials.Following { feed, err := db.GetFollowing(h.Db, user.Did, pageSize+1, int(offset)) if err != nil { - log.Println("failed to get following list:", err) + l.Error("failed to get following list", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to get following list, try again later.") return } bskyHydratedFeed, err = h.GetHydratedFollowerProfiles(feed) if err != nil { - log.Println("failed to hydrate bsky profiles:", err) + l.Error("failed to hydrate bsky profiles", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to get following list, try again later.") return } } else { feed, err := db.GetFollowers(h.Db, user.Did, pageSize+1, int(offset)) if err != nil { - log.Println("failed to get followers list:", err) + l.Error("failed to get followers list", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to get followers list, try again later.") return } bskyHydratedFeed, err = h.GetHydratedFollowerProfiles(feed) if err != nil { - log.Println("failed to hydrate bsky profiles:", err) + l.Error("failed to hydrate bsky profiles", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to get following list, try again later.") return } @@ -586,9 +600,11 @@ func (h *Handler) HandleFriendsFeed(w http.ResponseWriter, r *http.Request) { } func (h *Handler) HandleNotificationsPage(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleNotificationsPage") + user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) if err != nil { - log.Println("failed to get logged-in user:", err) + l.Error("failed to get logged-in user", "err", err) htmx.HxRedirect(w, "/login") return } -- 2.43.0 From 95604a77f1d5698c8d62259f374dec277fe715e9 Mon Sep 17 00:00:00 2001 From: brookjeynes Date: Wed, 15 Oct 2025 12:00:44 +1000 Subject: [PATCH] feat(handlers/reaction): use slogger Change-Id: rykvnnktrsrwmxnmyykykxkxozqxzwsk Signed-off-by: brookjeynes --- internal/server/handlers/reaction.go | 34 ++++++++++++++-------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/internal/server/handlers/reaction.go b/internal/server/handlers/reaction.go index 2d4013f..b3c4513 100644 --- a/internal/server/handlers/reaction.go +++ b/internal/server/handlers/reaction.go @@ -1,7 +1,6 @@ package handlers import ( - "log" "net/http" "slices" "strconv" @@ -21,16 +20,18 @@ import ( ) func (h *Handler) HandleReaction(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleReaction") + client, err := h.Oauth.AuthorizedClient(r) if err != nil { - log.Println("failed to get authorized client:", err) + l.Error("failed to get authorized client", "err", err) htmx.HxRedirect(w, "/login") return } user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) if err != nil { - log.Println("failed to get logged-in user:", err) + l.Error("failed to get logged-in user", "err", err) htmx.HxRedirect(w, "/login") return } @@ -53,7 +54,7 @@ func (h *Handler) HandleReaction(w http.ResponseWriter, r *http.Request) { } if user.Did == session.Did { - log.Println("failed to react to study session, cannot react to your own study session") + l.Error("failed to react to study session, cannot react to your own study session") htmx.HxError(w, http.StatusBadRequest, "You cannot react to your own study sessions.") return } @@ -73,14 +74,14 @@ func (h *Handler) HandleReaction(w http.ResponseWriter, r *http.Request) { reaction, err := db.ReactionFromString(db.ReactionType(reactionId).String()) if err != nil { - log.Printf("failed to get reaction types: %v", err) + l.Error("failed to get reaction types", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to get global study session feed, try again later.") return } reactionEvents, err := db.GetReactionsForSession(h.Db, subjectDid, subjectRkey) if err != nil { - log.Println("failed to get reactions for study session from db:", err) + l.Error("failed to get reactions for study session from db", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to get global study session feed, try again later.") return } @@ -89,12 +90,12 @@ func (h *Handler) HandleReaction(w http.ResponseWriter, r *http.Request) { case http.MethodPost: reactionEvent, err := db.GetReactionEvent(h.Db, user.Did, session, reaction.ID) if err != nil { - log.Println("failed to get reaction event from db:", err) + l.Error("failed to get reaction event from db", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to add reaction, try again later.") return } if reactionEvent != nil { - log.Println("failed to add reaction, user already reacted") + l.Error("failed to add reaction, user already reacted") htmx.HxError(w, http.StatusBadRequest, "You cannot react multiple times with the same reaction.") return } @@ -113,7 +114,7 @@ func (h *Handler) HandleReaction(w http.ResponseWriter, r *http.Request) { }}, }) if err != nil { - log.Println("failed to create reaction record:", err) + l.Error("failed to create reaction record", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to add reaction, try again later.") return } @@ -128,7 +129,7 @@ func (h *Handler) HandleReaction(w http.ResponseWriter, r *http.Request) { Set("session_rkey", subjectRkey), }) if err != nil { - log.Println("failed to enqueue posthog event:", err) + l.Error("failed to enqueue posthog event", "err", err) } } @@ -153,7 +154,7 @@ func (h *Handler) HandleReaction(w http.ResponseWriter, r *http.Request) { case http.MethodDelete: reactionEvent, err := db.GetReactionEvent(h.Db, user.Did, session, reaction.ID) if err != nil { - log.Println("failed to get reaction event from db:", err) + l.Error("failed to get reaction event from db", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to remove reaction, try again later.") return } @@ -164,7 +165,7 @@ func (h *Handler) HandleReaction(w http.ResponseWriter, r *http.Request) { Rkey: reactionEvent.Rkey, }) if err != nil { - log.Println("failed to delete reaction record:", err) + l.Error("failed to delete reaction record", "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to remove reaction, try again later.") return } @@ -179,7 +180,7 @@ func (h *Handler) HandleReaction(w http.ResponseWriter, r *http.Request) { Set("session_rkey", subjectRkey), }) if err != nil { - log.Println("failed to enqueue posthog event:", err) + l.Error("failed to enqueue posthog event", "err", err) } } @@ -188,10 +189,9 @@ func (h *Handler) HandleReaction(w http.ResponseWriter, r *http.Request) { }) partials.NewReactions(partials.NewReactionsProps{ - User: user, - SessionRkey: subjectRkey, - SessionDid: subjectDid, - // Reactions: reactions, + User: user, + SessionRkey: subjectRkey, + SessionDid: subjectDid, ReactionEvents: reactionEvents, }).Render(r.Context(), w) } -- 2.43.0 From 2c2bc4f297f44dcd9edb82381d084c3d32350181 Mon Sep 17 00:00:00 2001 From: brookjeynes Date: Wed, 15 Oct 2025 12:02:39 +1000 Subject: [PATCH] feat(handlers/stats): use slogger Change-Id: tyrzuxkutmttkpqpqlxzztownwzuprlx Signed-off-by: brookjeynes --- internal/server/handlers/stats.go | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/internal/server/handlers/stats.go b/internal/server/handlers/stats.go index cc61b21..b4b7b36 100644 --- a/internal/server/handlers/stats.go +++ b/internal/server/handlers/stats.go @@ -1,7 +1,6 @@ package handlers import ( - "log" "net/http" "yoten.app/internal/clients/bsky" @@ -12,9 +11,11 @@ import ( ) func (h *Handler) HandleTimePerGraphs(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleTimePerGraphs") + user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) if err != nil { - log.Println("failed to get logged-in user:", err) + l.Error("failed to get logged-in user", "err", err) htmx.HxRedirect(w, "/login") return } @@ -24,7 +25,7 @@ func (h *Handler) HandleTimePerGraphs(w http.ResponseWriter, r *http.Request) { chartData, err := db.GetTimePerData(h.Db, user.Did, period) if err != nil { - log.Println("failed to get time per chart data:", err) + l.Error("failed to get time per chart data", "err", err) chartData = db.ChartsData{ ActivityData: []db.ChartData{}, CategoryData: []db.ChartData{}, @@ -38,46 +39,48 @@ func (h *Handler) HandleTimePerGraphs(w http.ResponseWriter, r *http.Request) { } func (h *Handler) HandleStatsPage(w http.ResponseWriter, r *http.Request) { + l := h.Logger.With("handler", "HandleStatsPage") + user, err := bsky.GetUserWithBskyProfile(h.Oauth, r) if err != nil { - log.Println("failed to get logged-in user:", err) + l.Error("failed to get logged-in user", "err", err) htmx.HxRedirect(w, "/login") return } totalStudyTime, err := db.GetTotalStudyTime(h.Db, user.Did) if err != nil { - log.Println("failed to get total study time:", err) + l.Error("failed to get total study time", "err", err) } totalStudySessions, err := db.GetTotalStudySessions(h.Db, user.Did) if err != nil { - log.Println("failed to get total study study sessions:", err) + l.Error("failed to get total study study sessions", "err", err) } totalActiveDays, err := db.GetTotalActiveDays(h.Db, user.Did) if err != nil { - log.Println("failed to get total active days:", err) + l.Error("failed to get total active days", "err", err) } streak, err := db.GetCurrentStreak(h.Db, user.Did) if err != nil { - log.Println("failed to get streak:", err) + l.Error("failed to get streak", "err", err) } heatmap, err := db.GetHeatmapData(h.Db, user.Did) if err != nil { - log.Println("failed to get heatmap data:", err) + l.Error("failed to get heatmap data", "err", err) } inputOutputPercentage, err := db.GetInputOutputPercentage(h.Db, user.Did) if err != nil { - log.Println("failed to get input vs output data:", err) + l.Error("failed to get input vs output data", "err", err) } languageSummary, err := db.GetLanguageSummary(h.Db, user.Did) if err != nil { - log.Println("failed to get language time summary:", err) + l.Error("failed to get language time summary", "err", err) } languageChartSegments := db.ConvertToDonutChartSegments(languageSummary) -- 2.43.0 From b598c0404f217a2051d56bd2ab4562a3016e4e1d Mon Sep 17 00:00:00 2001 From: brookjeynes Date: Wed, 15 Oct 2025 12:18:27 +1000 Subject: [PATCH] feat(consumer/ingester): use slogger Change-Id: vxnqqryvltmwrxrrtxvvspklprxowpku Signed-off-by: brookjeynes --- internal/consumer/ingester.go | 50 +++++++++++++++++++++-------------- internal/server/app.go | 6 +++-- 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/internal/consumer/ingester.go b/internal/consumer/ingester.go index af23b09..571332b 100644 --- a/internal/consumer/ingester.go +++ b/internal/consumer/ingester.go @@ -4,7 +4,7 @@ import ( "context" "encoding/json" "fmt" - "log" + "log/slog" "strings" "time" @@ -20,6 +20,7 @@ import ( type Ingester struct { Db db.DbWrapper Config *config.Config + Logger *slog.Logger } type processFunc func(ctx context.Context, e *models.Event) error @@ -35,27 +36,36 @@ func (i *Ingester) Ingest() processFunc { } }() + l := i.Logger.With("kind", e.Kind) switch e.Kind { case models.EventKindCommit: switch e.Commit.Collection { case yoten.ActorProfileNSID: + l = l.With("handler", "ingestProfile") err = i.ingestProfile(e) case yoten.FeedSessionNSID: + l = l.With("handler", "ingestStudySession") err = i.ingestStudySession(e) case yoten.ActivityDefNSID: + l = l.With("handler", "ingestActivityDef") err = i.ingestActivityDef(e) case yoten.FeedResourceNSID: + l = l.With("handler", "ingestResource") err = i.ingestResource(e) case yoten.GraphFollowNSID: + l = l.With("handler", "ingestFollow") err = i.ingestFollow(e) case yoten.FeedReactionNSID: + l = l.With("handler", "ingestReaction") err = i.ingestReaction(e) case yoten.FeedCommentNSID: + l = l.With("handler", "ingestComment") err = i.ingestComment(e) } + l = i.Logger.With("nsid", e.Commit.Collection) } if err != nil { - log.Printf("failed to ingest event for collection %s: %v", e.Commit.Collection, err) + l.Error("failed to ingest event", "err", err) } return nil @@ -129,7 +139,7 @@ func (i *Ingester) ingestProfile(e *models.Event) error { return fmt.Errorf("failed to start transaction: %w", err) } - log.Printf("upserting profile '%s' from pds request", profile.Did) + i.Logger.Debug("upserting profile from pds request") err = db.UpsertProfile(tx, &profile) if err != nil { tx.Rollback() @@ -160,7 +170,7 @@ func (i *Ingester) ingestStudySession(e *models.Event) error { date, err := time.Parse(time.RFC3339, record.Date) if err != nil { - log.Printf("invalid record: %s", err) + i.Logger.Error("invalid record", "err", err) return err } @@ -227,7 +237,7 @@ func (i *Ingester) ingestStudySession(e *models.Event) error { return fmt.Errorf("failed to start transaction: %w", err) } - log.Println("upserting study session from pds request") + i.Logger.Debug("upserting study session from pds request") err = db.UpsertStudySession(tx, &studySession, e.Commit.RKey) if err != nil { tx.Rollback() @@ -252,7 +262,7 @@ func (i *Ingester) ingestStudySession(e *models.Event) error { return fmt.Errorf("failed to start transaction: %w", err) } - log.Println("deleting study session from pds request") + i.Logger.Debug("deleting study session from pds request") err = db.DeleteStudySessionByRkey(tx, did, e.Commit.RKey) if err != nil { tx.Rollback() @@ -344,7 +354,7 @@ func (i *Ingester) ingestActivityDef(e *models.Event) error { return fmt.Errorf("failed to start transaction: %w", err) } - log.Println("upserting activity def from pds request") + i.Logger.Debug("upserting activity def from pds request") err = db.UpsertActivityDef(tx, &activityDef, e.Commit.RKey) if err != nil { tx.Rollback() @@ -352,7 +362,7 @@ func (i *Ingester) ingestActivityDef(e *models.Event) error { } return tx.Commit() case models.CommitOperationDelete: - log.Println("deleting activity def from pds request") + i.Logger.Debug("deleting activity def from pds request") err = db.DeleteActivityDefByRkey(i.Db, did, e.Commit.RKey) } if err != nil { @@ -387,7 +397,7 @@ func (i *Ingester) ingestFollow(e *models.Event) error { subjectDid := record.Subject - log.Println("upserting follow from pds request") + i.Logger.Debug("upserting follow from pds request") err = db.AddFollow(tx, did, subjectDid, e.Commit.RKey) if err != nil { tx.Rollback() @@ -397,12 +407,12 @@ func (i *Ingester) ingestFollow(e *models.Event) error { subjectUri := fmt.Sprintf("at://%s/%s/%s", did, yoten.GraphFollowNSID, e.Commit.RKey) err = db.CreateNotification(tx, subjectDid, did, subjectUri, db.NotificationTypeFollow) if err != nil { - log.Println("failed to create notification record:", err) + i.Logger.Error("failed to create notification record", "err", err) } return tx.Commit() case models.CommitOperationDelete: - log.Println("deleting follow from pds request") + i.Logger.Debug("deleting follow from pds request") err = db.DeleteFollowByRkey(i.Db, did, e.Commit.RKey) } if err != nil { @@ -465,7 +475,7 @@ func (i *Ingester) ingestReaction(e *models.Event) error { CreatedAt: createdAt, } - log.Println("upserting reaction from pds request") + i.Logger.Debug("upserting reaction from pds request") err = db.UpsertReaction(i.Db, reactionEvent) if err != nil { tx.Rollback() @@ -474,12 +484,12 @@ func (i *Ingester) ingestReaction(e *models.Event) error { err = db.CreateNotification(tx, subjectDid.String(), did, subject.String(), db.NotificationTypeReaction) if err != nil { - log.Println("failed to create notification record:", err) + i.Logger.Error("failed to create notification record", "err", err) } return tx.Commit() case models.CommitOperationDelete: - log.Println("deleting reaction from pds request") + i.Logger.Debug("deleting reaction from pds request") err = db.DeleteReactionByRkey(i.Db, did, e.Commit.RKey) } if err != nil { @@ -546,7 +556,7 @@ func (i *Ingester) ingestResource(e *models.Event) error { return fmt.Errorf("invalid resource: %w", err) } - log.Println("upserting resource from pds request") + i.Logger.Debug("upserting resource from pds request") err = db.UpsertResource(i.Db, resource, resource.Rkey) if err != nil { tx.Rollback() @@ -554,7 +564,7 @@ func (i *Ingester) ingestResource(e *models.Event) error { } return tx.Commit() case models.CommitOperationDelete: - log.Println("deleting resource from pds request") + i.Logger.Debug("deleting resource from pds request") err = db.DeleteResourceByRkey(i.Db, did, e.Commit.RKey) } if err != nil { @@ -626,7 +636,7 @@ func (i *Ingester) ingestComment(e *models.Event) error { CreatedAt: createdAt, } - log.Println("upserting comment from pds request") + i.Logger.Debug("upserting comment from pds request") err = db.UpsertComment(i.Db, comment) if err != nil { tx.Rollback() @@ -637,7 +647,7 @@ func (i *Ingester) ingestComment(e *models.Event) error { if subjectDid.String() != did { err = db.CreateNotification(tx, subjectDid.String(), did, subjectUri.String(), db.NotificationTypeComment) if err != nil { - log.Println("failed to create notification record:", err) + i.Logger.Error("failed to create notification record", "err", err) } } @@ -645,13 +655,13 @@ func (i *Ingester) ingestComment(e *models.Event) error { if comment.ParentCommentUri != nil && comment.ParentCommentUri.Authority().String() != did { err = db.CreateNotification(tx, comment.ParentCommentUri.Authority().String(), did, parentCommentUri.String(), db.NotificationTypeReply) if err != nil { - log.Println("failed to create notification record:", err) + i.Logger.Error("failed to create notification record", "err", err) } } return tx.Commit() case models.CommitOperationDelete: - log.Println("deleting comment from pds request") + i.Logger.Debug("deleting comment from pds request") err = db.DeleteCommentByRkey(i.Db, did, e.Commit.RKey) } if err != nil { diff --git a/internal/server/app.go b/internal/server/app.go index c7cf92f..20bc6bd 100644 --- a/internal/server/app.go +++ b/internal/server/app.go @@ -85,7 +85,7 @@ func Make(ctx context.Context, config *config.Config) (*Server, error) { yoten.GraphFollowNSID, }, nil, - slog.Default(), + log.SubLogger(logger, "jetstream"), wrapper, false, ) @@ -95,7 +95,9 @@ func Make(ctx context.Context, config *config.Config) (*Server, error) { ingester := consumer.Ingester{ Db: wrapper, - Config: config} + Config: config, + Logger: log.SubLogger(logger, "ingester"), + } err = jc.StartJetstream(ctx, ingester.Ingest()) if err != nil { return nil, fmt.Errorf("failed to start jetstream watcher: %w", err) -- 2.43.0 From 254ae6865f928942eee75d7145d30782e7825e63 Mon Sep 17 00:00:00 2001 From: brookjeynes Date: Wed, 15 Oct 2025 14:16:16 +1000 Subject: [PATCH] feat(middleware): use slogger Change-Id: ontulmlvxkxnuonzqtoyszlywzvppzyr Signed-off-by: brookjeynes --- internal/server/handlers/router.go | 2 ++ internal/server/middleware/middleware.go | 19 +++++++++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/internal/server/handlers/router.go b/internal/server/handlers/router.go index d4f46e0..cf2636b 100644 --- a/internal/server/handlers/router.go +++ b/internal/server/handlers/router.go @@ -7,6 +7,7 @@ import ( "github.com/go-chi/chi/v5" "yoten.app/internal/server" + "yoten.app/internal/server/log" "yoten.app/internal/server/middleware" "yoten.app/internal/server/views" ) @@ -25,6 +26,7 @@ func (h *Handler) Router() http.Handler { h.Oauth, h.Db, h.IdResolver, + log.SubLogger(h.Logger, "middleware"), ) router.HandleFunc("/*", func(w http.ResponseWriter, r *http.Request) { diff --git a/internal/server/middleware/middleware.go b/internal/server/middleware/middleware.go index b029f38..58e85fb 100644 --- a/internal/server/middleware/middleware.go +++ b/internal/server/middleware/middleware.go @@ -3,7 +3,7 @@ package middleware import ( "context" "fmt" - "log" + "log/slog" "net/http" "net/url" "slices" @@ -25,19 +25,23 @@ type Middleware struct { oauth *oauth.OAuth db *db.DB idResolver *atproto.Resolver + logger *slog.Logger } -func New(oauth *oauth.OAuth, db *db.DB, idResolver *atproto.Resolver) Middleware { +func New(oauth *oauth.OAuth, db *db.DB, idResolver *atproto.Resolver, logger *slog.Logger) Middleware { return Middleware{ oauth: oauth, db: db, idResolver: idResolver, + logger: logger, } } type middlewareFunc func(http.Handler) http.Handler func AuthMiddleware(o *oauth.OAuth) middlewareFunc { + l := o.Logger.With("middleware", "AuthMiddleware") + return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { returnURL := "/" @@ -59,13 +63,13 @@ func AuthMiddleware(o *oauth.OAuth) middlewareFunc { sess, err := o.ResumeSession(r) if err != nil { - log.Println("failed to resume session, redirecting...", "err", err, "url", r.URL.String()) + l.Error("failed to resume session, redirecting...", "err", err, "url", r.URL.String()) redirectFunc(w, r) return } if sess == nil { - log.Printf("session is nil, redirecting...") + l.Warn("session is nil, redirecting...") redirectFunc(w, r) return } @@ -76,6 +80,7 @@ func AuthMiddleware(o *oauth.OAuth) middlewareFunc { } func (mw Middleware) ResolveIdent() middlewareFunc { + l := mw.logger.With("middleware", "ResolveIdent") excluded := []string{"favicon.ico"} return func(next http.Handler) http.Handler { @@ -90,7 +95,7 @@ func (mw Middleware) ResolveIdent() middlewareFunc { id, err := mw.idResolver.ResolveIdent(r.Context(), didOrHandle) if err != nil { - log.Println("failed to resolve did/handle:", err) + l.Error("failed to resolve did/handle", "err", err) w.WriteHeader(http.StatusNotFound) views.NotFoundPage(views.NotFoundPageParams{}).Render(r.Context(), w) return @@ -104,6 +109,8 @@ func (mw Middleware) ResolveIdent() middlewareFunc { } func (mw Middleware) LoadUnreadNotificationCount() middlewareFunc { + l := mw.logger.With("middleware", "LoadUnreadNotificationCount") + return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { user := mw.oauth.GetUser(r) @@ -114,7 +121,7 @@ func (mw Middleware) LoadUnreadNotificationCount() middlewareFunc { count, err := db.GetUnreadNotificationCount(mw.db, user.Did) if err != nil { - log.Println("failed to get notification count:", err) + l.Error("failed to get notification count", "err", err) } ctx := context.WithValue(r.Context(), UnreadNotificationCountCtxKey, count) -- 2.43.0 From f33fcdfb5ad1cc5aa359f48523ddc84b26966a89 Mon Sep 17 00:00:00 2001 From: brookjeynes Date: Wed, 15 Oct 2025 14:27:27 +1000 Subject: [PATCH] fix(auth): handle bad identity lookups Change-Id: trrpxxyxxmotqnxwwywusmsmnxtwvxxr Signed-off-by: brookjeynes --- internal/server/handlers/login.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/internal/server/handlers/login.go b/internal/server/handlers/login.go index 467bd17..a9b6c5b 100644 --- a/internal/server/handlers/login.go +++ b/internal/server/handlers/login.go @@ -55,13 +55,17 @@ func (h *Handler) Login(w http.ResponseWriter, r *http.Request) { // Basic handle validation if !strings.Contains(handle, ".") { - l.Error("invalid handle format", "err", handle) + l.Error("invalid handle format", "handle", handle) htmx.HxError(w, http.StatusBadGateway, fmt.Sprintf("'%s' is an invalid handle. Did you mean %s.bsky.social?", handle, handle)) return } resolved, err := h.IdResolver.ResolveIdent(context.Background(), handle) - if err == nil { + if err != nil { + l.Error("failed to resolve handle", "handle", handle, "err", err) + htmx.HxError(w, http.StatusBadGateway, fmt.Sprintf("'%s' is an invalid handle", handle)) + return + } else { if !h.Config.Core.Dev && resolved.DID.String() != "" { err := h.Posthog.Enqueue(posthog.Capture{ DistinctId: string(resolved.DID), @@ -75,6 +79,7 @@ func (h *Handler) Login(w http.ResponseWriter, r *http.Request) { redirectURL, err := h.Oauth.ClientApp.StartAuthFlow(r.Context(), handle) if err != nil { + l.Error("failed to resolve auth flow", "handle", handle, "err", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } -- 2.43.0 From c1d12a9488a030a8de87560341a078529a2599f2 Mon Sep 17 00:00:00 2001 From: brookjeynes Date: Thu, 16 Oct 2025 15:22:29 +1000 Subject: [PATCH] fix: accounts not created on sign-in Change-Id: lokxzmtxxkxoqlllntnnnpulooqxtsmz Signed-off-by: brookjeynes --- internal/server/app.go | 2 +- internal/server/handlers/login.go | 4 +- internal/server/handlers/profile.go | 2 +- internal/server/oauth/handler.go | 94 +++++++++++++++++++++++++++-- internal/server/oauth/oauth.go | 5 +- 5 files changed, 96 insertions(+), 11 deletions(-) diff --git a/internal/server/app.go b/internal/server/app.go index 20bc6bd..96af718 100644 --- a/internal/server/app.go +++ b/internal/server/app.go @@ -63,7 +63,7 @@ func Make(ctx context.Context, config *config.Config) (*Server, error) { idResolver := atproto.DefaultResolver() - oauth, err := oauth.New(config, posthog, log.SubLogger(logger, "oauth")) + oauth, err := oauth.New(config, posthog, idResolver, log.SubLogger(logger, "oauth")) if err != nil { return nil, fmt.Errorf("failed to start oauth handler: %w", err) } diff --git a/internal/server/handlers/login.go b/internal/server/handlers/login.go index a9b6c5b..444a088 100644 --- a/internal/server/handlers/login.go +++ b/internal/server/handlers/login.go @@ -56,14 +56,14 @@ func (h *Handler) Login(w http.ResponseWriter, r *http.Request) { // Basic handle validation if !strings.Contains(handle, ".") { l.Error("invalid handle format", "handle", handle) - htmx.HxError(w, http.StatusBadGateway, fmt.Sprintf("'%s' is an invalid handle. Did you mean %s.bsky.social?", handle, handle)) + htmx.HxError(w, http.StatusBadRequest, fmt.Sprintf("'%s' is an invalid handle. Did you mean %s.bsky.social?", handle, handle)) return } resolved, err := h.IdResolver.ResolveIdent(context.Background(), handle) if err != nil { l.Error("failed to resolve handle", "handle", handle, "err", err) - htmx.HxError(w, http.StatusBadGateway, fmt.Sprintf("'%s' is an invalid handle", handle)) + htmx.HxError(w, http.StatusBadRequest, fmt.Sprintf("'%s' is an invalid handle", handle)) return } else { if !h.Config.Core.Dev && resolved.DID.String() != "" { diff --git a/internal/server/handlers/profile.go b/internal/server/handlers/profile.go index 76f51ed..076716f 100644 --- a/internal/server/handlers/profile.go +++ b/internal/server/handlers/profile.go @@ -130,7 +130,7 @@ func (h *Handler) HandleProfilePage(w http.ResponseWriter, r *http.Request) { }) if err := g.Wait(); err != nil { - l.Error("failed to fetch critical profile data for", "did", profileDid, "err", err) + l.Error("failed to fetch critical profile data", "did", profileDid, "err", err) htmx.HxError(w, http.StatusInternalServerError, "Failed to fetch profile data, try again later.") return } diff --git a/internal/server/oauth/handler.go b/internal/server/oauth/handler.go index f0243e5..7db5107 100644 --- a/internal/server/oauth/handler.go +++ b/internal/server/oauth/handler.go @@ -1,14 +1,22 @@ package oauth import ( + "context" "encoding/json" + "fmt" "net/http" + "time" + comatproto "github.com/bluesky-social/indigo/api/atproto" + lexutil "github.com/bluesky-social/indigo/lex/util" "github.com/go-chi/chi/v5" "github.com/lestrrat-go/jwx/v2/jwk" "github.com/posthog/posthog-go" + "yoten.app/api/yoten" ph "yoten.app/internal/clients/posthog" + "yoten.app/internal/db" + "yoten.app/internal/server/htmx" ) func (o *OAuth) Router() http.Handler { @@ -63,6 +71,7 @@ func (o *OAuth) jwks(w http.ResponseWriter, r *http.Request) { } func (o *OAuth) callback(w http.ResponseWriter, r *http.Request) { + l := o.Logger.With("handler", "callback") ctx := r.Context() sessData, err := o.ClientApp.ProcessCallback(ctx, r.URL.Query()) @@ -73,18 +82,91 @@ func (o *OAuth) callback(w http.ResponseWriter, r *http.Request) { } if err := o.SaveSession(w, r, sessData); err != nil { - o.Logger.Error("failed to save session", "err", err) + l.Error("failed to save session", "err", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } - if !o.Config.Core.Dev { - err = o.Posthog.Enqueue(posthog.Capture{ - DistinctId: sessData.AccountDID.String(), - Event: ph.UserSignInSuccessEvent, + did := sessData.AccountDID.String() + resolved, err := o.IdResolver.ResolveIdent(context.Background(), did) + if err != nil { + l.Error("failed to resolve handle", "handle", resolved.Handle.String(), "err", err) + htmx.HxError(w, http.StatusBadRequest, fmt.Sprintf("'%s' is an invalid handle", resolved.Handle.String())) + return + } + + client, err := o.AuthorizedClient(r) + if err != nil { + l.Error("failed to get authorized client", "err", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + ex, _ := comatproto.RepoGetRecord(r.Context(), client, "", yoten.ActorProfileNSID, did, "self") + var cid *string + if ex != nil { + cid = ex.Cid + } + + // This should only occur once per account + if ex == nil { + createdAt := time.Now().Format(time.RFC3339) + atresp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + Collection: yoten.ActorProfileNSID, + Repo: did, + Rkey: "self", + Record: &lexutil.LexiconTypeDecoder{ + Val: &yoten.ActorProfile{ + DisplayName: resolved.Handle.String(), + Description: db.ToPtr(""), + Languages: make([]string, 0), + Location: db.ToPtr(""), + CreatedAt: createdAt, + }}, + + SwapRecord: cid, }) if err != nil { - o.Logger.Error("failed to enqueue posthog event", "err", err) + l.Error("failed to create profile record", "err", err) + htmx.HxError(w, http.StatusInternalServerError, "Failed to announce profile creation, try again later") + return + } + + l.Debug("created profile record", "uri", atresp.Uri) + + if !o.Config.Core.Dev { + err = o.Posthog.Enqueue(posthog.Capture{ + DistinctId: sessData.AccountDID.String(), + Event: ph.UserSignInSuccessEvent, + }) + if err != nil { + l.Error("failed to enqueue posthog event", "err", err) + } + + properties := posthog.NewProperties(). + Set("display_name", resolved.Handle.String()). + Set("language_count", 0). + Set("$set_once", posthog.NewProperties(). + Set("initial_did", did). + Set("initial_handle", resolved.Handle.String()). + Set("created_at", createdAt), + ) + + err = o.Posthog.Enqueue(posthog.Identify{ + DistinctId: did, + Properties: properties, + }) + if err != nil { + l.Error("failed to enqueue posthog identify event", "err", err) + } + + err = o.Posthog.Enqueue(posthog.Capture{ + DistinctId: did, + Event: ph.ProfileRecordCreatedEvent, + }) + if err != nil { + l.Error("failed to enqueue posthog event", "err", err) + } } } diff --git a/internal/server/oauth/oauth.go b/internal/server/oauth/oauth.go index 1e5a41a..eb3b483 100644 --- a/internal/server/oauth/oauth.go +++ b/internal/server/oauth/oauth.go @@ -15,6 +15,7 @@ import ( "github.com/gorilla/sessions" "github.com/posthog/posthog-go" + "yoten.app/internal/atproto" "yoten.app/internal/server/config" "yoten.app/internal/types" ) @@ -26,9 +27,10 @@ type OAuth struct { JwksUri string Posthog posthog.Client Logger *slog.Logger + IdResolver *atproto.Resolver } -func New(config *config.Config, ph posthog.Client, logger *slog.Logger) (*OAuth, error) { +func New(config *config.Config, ph posthog.Client, idResolver *atproto.Resolver, logger *slog.Logger) (*OAuth, error) { var oauthConfig oauth.ClientConfig var clientUri string @@ -58,6 +60,7 @@ func New(config *config.Config, ph posthog.Client, logger *slog.Logger) (*OAuth, SessionStore: sessStore, JwksUri: jwksUri, Posthog: ph, + IdResolver: idResolver, Logger: logger, }, nil -- 2.43.0