A fork of https://github.com/teal-fm/piper
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: implement listenbrainz /1/validate-token required for some clients (like pano scrobbler) to work

Signed-off-by: rooot <hey@rooot.gay>

+42 -7
+34
cmd/handlers.go
··· 8 8 "strconv" 9 9 10 10 "github.com/teal-fm/piper/db" 11 + "github.com/teal-fm/piper/db/apikey" 11 12 "github.com/teal-fm/piper/models" 12 13 atprotoauth "github.com/teal-fm/piper/oauth/atproto" 13 14 pages "github.com/teal-fm/piper/pages" ··· 454 455 jsonResponse(w, http.StatusOK, response) 455 456 } 456 457 } 458 + 459 + // apiMbTokenValidateHandler handles ListenBrainz token validation requests 460 + func apiMbTokenValidateHandler(sm *session.SessionManager) http.HandlerFunc { 461 + return func(w http.ResponseWriter, r *http.Request) { 462 + apiKeyStr, apiKeyErr := apikey.ExtractApiKey(r) 463 + 464 + if apiKeyErr != nil || apiKeyStr == "" { 465 + jsonResponse(w, http.StatusBadRequest, map[string]any{ 466 + "code": 400, 467 + "message": "you need to specify a token", 468 + "valid": false, 469 + }) 470 + return 471 + } 472 + 473 + key, valid := sm.ApiKeyMgr.GetApiKey(apiKeyStr) 474 + if !valid { 475 + jsonResponse(w, http.StatusUnauthorized, map[string]any{ 476 + "code": 401, 477 + "message": "invalid token", 478 + "valid": false, 479 + }) 480 + return 481 + } 482 + 483 + jsonResponse(w, http.StatusOK, map[string]any{ 484 + "code": 200, 485 + "message": "token valid", 486 + "valid": true, 487 + "user_name": key.Name, // this is required to be set for pano scrobbler 488 + }) 489 + } 490 + }
+1
cmd/routes.go
··· 41 41 42 42 // ListenBrainz-compatible endpoint 43 43 mux.HandleFunc("/1/submit-listens", session.WithAPIAuth(apiSubmitListensHandler(app.database, app.atprotoService, app.playingNowService, app.mbService), app.sessionManager)) 44 + mux.HandleFunc("/1/validate-token", apiMbTokenValidateHandler(app.sessionManager)) 44 45 45 46 serverUrlRoot := viper.GetString("server.root_url") 46 47 atpClientId := viper.GetString("atproto.client_id")
+7 -7
session/session.go
··· 31 31 type SessionManager struct { 32 32 db *db.DB 33 33 sessions map[string]*Session // use in memory cache if necessary 34 - apiKeyMgr *apikey.ApiKeyManager 34 + ApiKeyMgr *apikey.ApiKeyManager 35 35 mu sync.RWMutex 36 36 } 37 37 ··· 56 56 return &SessionManager{ 57 57 db: database, 58 58 sessions: make(map[string]*Session), 59 - apiKeyMgr: apiKeyMgr, 59 + ApiKeyMgr: apiKeyMgr, 60 60 } 61 61 } 62 62 ··· 185 185 } 186 186 187 187 func (sm *SessionManager) GetAPIKeyManager() *apikey.ApiKeyManager { 188 - return sm.apiKeyMgr 188 + return sm.ApiKeyMgr 189 189 } 190 190 191 191 func (sm *SessionManager) CreateAPIKey(userID int64, name string, validityDays int) (*apikey.ApiKey, error) { 192 - return sm.apiKeyMgr.CreateApiKey(userID, name, validityDays) 192 + return sm.ApiKeyMgr.CreateApiKey(userID, name, validityDays) 193 193 } 194 194 195 195 // middleware that checks if a user is authenticated via cookies or API key ··· 198 198 // first: check API keys 199 199 apiKeyStr, apiKeyErr := apikey.ExtractApiKey(r) 200 200 if apiKeyErr == nil && apiKeyStr != "" { 201 - apiKey, valid := sm.apiKeyMgr.GetApiKey(apiKeyStr) 201 + apiKey, valid := sm.ApiKeyMgr.GetApiKey(apiKeyStr) 202 202 if valid { 203 203 ctx := WithUserID(r.Context(), apiKey.UserID) 204 204 r = r.WithContext(ctx) ··· 240 240 241 241 apiKeyStr, apiKeyErr := apikey.ExtractApiKey(r) 242 242 if apiKeyErr == nil && apiKeyStr != "" { 243 - apiKey, valid := sm.apiKeyMgr.GetApiKey(apiKeyStr) 243 + apiKey, valid := sm.ApiKeyMgr.GetApiKey(apiKeyStr) 244 244 if valid { 245 245 ctx = WithUserID(ctx, apiKey.UserID) 246 246 ctx = WithAPIRequest(ctx, true) ··· 278 278 return 279 279 } 280 280 281 - apiKey, valid := sm.apiKeyMgr.GetApiKey(apiKeyStr) 281 + apiKey, valid := sm.ApiKeyMgr.GetApiKey(apiKeyStr) 282 282 if !valid { 283 283 w.Header().Set("Content-Type", "application/json") 284 284 w.WriteHeader(http.StatusUnauthorized)