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