Yōten: A social tracker for your language learning journey built on the atproto.

feat: add redis

Signed-off-by: brookjeynes <me@brookjeynes.dev>

brookjeynes.dev 2943344e 08cab6d1

verified
+47 -5
+4 -3
go.mod
··· 4 5 toolchain go1.24.4 6 7 - replace github.com/bluesky-social/indigo => github.com/oppiliappan/indigo v0.0.0-20250728090836-5f170569da93 8 - 9 require ( 10 github.com/a-h/templ v0.3.898 11 - github.com/bluesky-social/indigo v0.0.0-20250728163042-01ae6633b28c 12 github.com/bluesky-social/jetstream v0.0.0-20250414024304-d17bd81a945e 13 github.com/carlmjohnson/versioninfo v0.22.5 14 github.com/go-chi/chi/v5 v5.2.1 ··· 17 github.com/lestrrat-go/jwx/v2 v2.0.12 18 github.com/mattn/go-sqlite3 v1.14.22 19 github.com/posthog/posthog-go v1.5.12 20 github.com/sethvargo/go-envconfig v1.3.0 21 github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e 22 golang.org/x/net v0.42.0 ··· 34 github.com/cespare/xxhash/v2 v2.3.0 // indirect 35 github.com/cli/browser v1.3.0 // indirect 36 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect 37 github.com/fatih/color v1.16.0 // indirect 38 github.com/felixge/httpsnoop v1.0.4 // indirect 39 github.com/fsnotify/fsnotify v1.7.0 // indirect ··· 42 github.com/goccy/go-json v0.10.2 // indirect 43 github.com/gogo/protobuf v1.3.2 // indirect 44 github.com/golang-jwt/jwt/v5 v5.2.2 // indirect 45 github.com/google/uuid v1.6.0 // indirect 46 github.com/gorilla/securecookie v1.1.2 // indirect 47 github.com/gorilla/websocket v1.5.1 // indirect
··· 4 5 toolchain go1.24.4 6 7 require ( 8 github.com/a-h/templ v0.3.898 9 + github.com/bluesky-social/indigo v0.0.0-20251003000214-3259b215110e 10 github.com/bluesky-social/jetstream v0.0.0-20250414024304-d17bd81a945e 11 github.com/carlmjohnson/versioninfo v0.22.5 12 github.com/go-chi/chi/v5 v5.2.1 ··· 15 github.com/lestrrat-go/jwx/v2 v2.0.12 16 github.com/mattn/go-sqlite3 v1.14.22 17 github.com/posthog/posthog-go v1.5.12 18 + github.com/redis/go-redis/v9 v9.14.0 19 github.com/sethvargo/go-envconfig v1.3.0 20 github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e 21 golang.org/x/net v0.42.0 ··· 33 github.com/cespare/xxhash/v2 v2.3.0 // indirect 34 github.com/cli/browser v1.3.0 // indirect 35 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect 36 + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect 37 github.com/fatih/color v1.16.0 // indirect 38 github.com/felixge/httpsnoop v1.0.4 // indirect 39 github.com/fsnotify/fsnotify v1.7.0 // indirect ··· 42 github.com/goccy/go-json v0.10.2 // indirect 43 github.com/gogo/protobuf v1.3.2 // indirect 44 github.com/golang-jwt/jwt/v5 v5.2.2 // indirect 45 + github.com/google/go-querystring v1.1.0 // indirect 46 github.com/google/uuid v1.6.0 // indirect 47 github.com/gorilla/securecookie v1.1.2 // indirect 48 github.com/gorilla/websocket v1.5.1 // indirect
+13 -2
go.sum
··· 8 github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 9 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 10 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 11 github.com/bluesky-social/jetstream v0.0.0-20250414024304-d17bd81a945e h1:P/O6TDHs53gwgV845uDHI+Nri889ixksRrh4bCkCdxo= 12 github.com/bluesky-social/jetstream v0.0.0-20250414024304-d17bd81a945e/go.mod h1:WiYEeyJSdUwqoaZ71KJSpTblemUCpwJfh5oVXplK6T4= 13 github.com/carlmjohnson/versioninfo v0.22.5 h1:O00sjOLUAFxYQjlN/bzYTuZiS0y6fWDQjMRvwtKgwwc= 14 github.com/carlmjohnson/versioninfo v0.22.5/go.mod h1:QT9mph3wcVfISUKd0i9sZfVrPviHuSF+cUtLjm2WSf8= 15 github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= ··· 26 github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= 27 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= 28 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= 29 github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= 30 github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= 31 github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= ··· 46 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 47 github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= 48 github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= 49 github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 50 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 51 github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= 52 github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 53 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= ··· 165 github.com/natefinch/atomic v1.0.1/go.mod h1:N/D/ELrljoqDyT3rZrsUmtsuzvHkeB/wWjHV22AZRbM= 166 github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= 167 github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= 168 - github.com/oppiliappan/indigo v0.0.0-20250728090836-5f170569da93 h1:7HH9daZ4xfUJUlHlH82i1xHyKSqKj/OWO+3aQLZbQWM= 169 - github.com/oppiliappan/indigo v0.0.0-20250728090836-5f170569da93/go.mod h1:0XUyOCRtL4/OiyeqMTmr6RlVHQMDgw3LS7CfibuZR5Q= 170 github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 h1:1/WtZae0yGtPq+TI6+Tv1WTxkukpXeMlviSxvL7SRgk= 171 github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9/go.mod h1:x3N5drFsm2uilKKuuYo6LdyD8vZAW55sH/9w+pbo1sw= 172 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= ··· 185 github.com/prometheus/common v0.54.0/go.mod h1:/TQgMJP5CuVYveyT7n/0Ix8yLNNXy9yRSkhnLTHPDIQ= 186 github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= 187 github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= 188 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 189 github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= 190 github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
··· 8 github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 9 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 10 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 11 + github.com/bluesky-social/indigo v0.0.0-20251003000214-3259b215110e h1:IutKPwmbU0LrYqw03EuwJtMdAe67rDTrL1U8S8dicRU= 12 + github.com/bluesky-social/indigo v0.0.0-20251003000214-3259b215110e/go.mod h1:n6QE1NDPFoi7PRbMUZmc2y7FibCqiVU4ePpsvhHUBR8= 13 github.com/bluesky-social/jetstream v0.0.0-20250414024304-d17bd81a945e h1:P/O6TDHs53gwgV845uDHI+Nri889ixksRrh4bCkCdxo= 14 github.com/bluesky-social/jetstream v0.0.0-20250414024304-d17bd81a945e/go.mod h1:WiYEeyJSdUwqoaZ71KJSpTblemUCpwJfh5oVXplK6T4= 15 + github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= 16 + github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= 17 + github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= 18 + github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= 19 github.com/carlmjohnson/versioninfo v0.22.5 h1:O00sjOLUAFxYQjlN/bzYTuZiS0y6fWDQjMRvwtKgwwc= 20 github.com/carlmjohnson/versioninfo v0.22.5/go.mod h1:QT9mph3wcVfISUKd0i9sZfVrPviHuSF+cUtLjm2WSf8= 21 github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= ··· 32 github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= 33 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= 34 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= 35 + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= 36 + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= 37 github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= 38 github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= 39 github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= ··· 54 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 55 github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= 56 github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= 57 + github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 58 github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 59 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 60 + github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= 61 + github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= 62 github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= 63 github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 64 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= ··· 176 github.com/natefinch/atomic v1.0.1/go.mod h1:N/D/ELrljoqDyT3rZrsUmtsuzvHkeB/wWjHV22AZRbM= 177 github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= 178 github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= 179 github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 h1:1/WtZae0yGtPq+TI6+Tv1WTxkukpXeMlviSxvL7SRgk= 180 github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9/go.mod h1:x3N5drFsm2uilKKuuYo6LdyD8vZAW55sH/9w+pbo1sw= 181 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= ··· 194 github.com/prometheus/common v0.54.0/go.mod h1:/TQgMJP5CuVYveyT7n/0Ix8yLNNXy9yRSkhnLTHPDIQ= 195 github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= 196 github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= 197 + github.com/redis/go-redis/v9 v9.14.0 h1:u4tNCjXOyzfgeLN+vAZaW1xUooqWDqVEsZN0U01jfAE= 198 + github.com/redis/go-redis/v9 v9.14.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= 199 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 200 github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= 201 github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
+7
internal/server/app.go
··· 34 Config *config.Config 35 Posthog posthog.Client 36 IdResolver *atproto.Resolver 37 ComputedData ComputedData 38 } 39 ··· 52 oauth := oauth.NewOAuth(d, config) 53 54 idResolver := atproto.DefaultResolver() 55 56 posthog, err := posthog.NewWithConfig(config.Posthog.ApiKey, posthog.Config{Endpoint: config.Posthog.Endpoint}) 57 if err != nil { ··· 88 return nil, fmt.Errorf("failed to start jetstream watcher: %w", err) 89 } 90 91 sortedLanguages := make([]db.Language, 0, len(db.Languages)) 92 for _, l := range db.Languages { 93 sortedLanguages = append(sortedLanguages, l) ··· 119 slices.SortFunc(sortedReactions, func(a, b db.Reaction) int { 120 return cmp.Compare(a.ID, b.ID) 121 }) 122 123 state := &Server{ 124 Db: d, ··· 126 Config: config, 127 Posthog: posthog, 128 IdResolver: idResolver, 129 ComputedData: ComputedData{ 130 SortedLanguages: sortedLanguages, 131 SortedCategories: sortedCategories,
··· 34 Config *config.Config 35 Posthog posthog.Client 36 IdResolver *atproto.Resolver 37 + Session *session.SessionStore 38 ComputedData ComputedData 39 } 40 ··· 53 oauth := oauth.NewOAuth(d, config) 54 55 idResolver := atproto.DefaultResolver() 56 + 57 + cache := cache.New(config.Redis.Addr) 58 + sess := session.New(cache) 59 60 posthog, err := posthog.NewWithConfig(config.Posthog.ApiKey, posthog.Config{Endpoint: config.Posthog.Endpoint}) 61 if err != nil { ··· 92 return nil, fmt.Errorf("failed to start jetstream watcher: %w", err) 93 } 94 95 + // -- Computed data 96 sortedLanguages := make([]db.Language, 0, len(db.Languages)) 97 for _, l := range db.Languages { 98 sortedLanguages = append(sortedLanguages, l) ··· 124 slices.SortFunc(sortedReactions, func(a, b db.Reaction) int { 125 return cmp.Compare(a.ID, b.ID) 126 }) 127 + // -- 128 129 state := &Server{ 130 Db: d, ··· 132 Config: config, 133 Posthog: posthog, 134 IdResolver: idResolver, 135 + Session: sess, 136 ComputedData: ComputedData{ 137 SortedLanguages: sortedLanguages, 138 SortedCategories: sortedCategories,
+23
internal/server/config/config.go
··· 2 3 import ( 4 "context" 5 6 "github.com/sethvargo/go-envconfig" 7 ) ··· 31 ApiKey string `env:"API_KEY"` 32 } 33 34 type Config struct { 35 Core CoreConfig `env:",prefix=YOTEN_"` 36 Jetstream JetstreamConfig `env:",prefix=TANGLED_JETSTREAM_"` 37 OAuth OAuthConfig `env:",prefix=YOTEN_OAUTH_"` 38 Posthog PosthogConfig `env:",prefix=YOTEN_POSTHOG_"` 39 Google GoogleConfig `env:",prefix=YOTEN_GOOGLE_"` 40 } 41 42 func LoadConfig(ctx context.Context) (*Config, error) {
··· 2 3 import ( 4 "context" 5 + "fmt" 6 + "net/url" 7 8 "github.com/sethvargo/go-envconfig" 9 ) ··· 33 ApiKey string `env:"API_KEY"` 34 } 35 36 + type RedisConfig struct { 37 + Addr string `env:"ADDR, default=localhost:6379"` 38 + Password string `env:"PASS"` 39 + DB int `env:"DB, default=0"` 40 + } 41 + 42 + func (cfg RedisConfig) ToURL() string { 43 + u := &url.URL{ 44 + Scheme: "redis", 45 + Host: cfg.Addr, 46 + Path: fmt.Sprintf("/%d", cfg.DB), 47 + } 48 + 49 + if cfg.Password != "" { 50 + u.User = url.UserPassword("default", cfg.Password) 51 + } 52 + 53 + return u.String() 54 + } 55 + 56 type Config struct { 57 Core CoreConfig `env:",prefix=YOTEN_"` 58 Jetstream JetstreamConfig `env:",prefix=TANGLED_JETSTREAM_"` 59 OAuth OAuthConfig `env:",prefix=YOTEN_OAUTH_"` 60 Posthog PosthogConfig `env:",prefix=YOTEN_POSTHOG_"` 61 Google GoogleConfig `env:",prefix=YOTEN_GOOGLE_"` 62 + Redis RedisConfig `env:",prefix=YOTEN_REDIS_"` 63 } 64 65 func LoadConfig(ctx context.Context) (*Config, error) {