···2323```
2424air
2525```
2626+2727+air should automatically build and run piper, and watch for changes on relevant files.
2828+2929+#### docker
3030+3131+TODO
+1-4
oauth/oauth2.go
···1313 "golang.org/x/oauth2/spotify"
1414)
15151616-// OAuth2Service represents an OAuth2 service with PKCE support
1716type OAuth2Service struct {
1817 config oauth2.Config
1918 state string
···2120 codeChallenge string
2221}
23222424-// generateRandomState creates a random state string for CSRF protection
2523func generateRandomState() string {
2624 b := make([]byte, 16)
2725 rand.Read(b)
···3230func NewOAuth2Service(clientID, clientSecret, redirectURI string, scopes []string, provider string) *OAuth2Service {
3331 var endpoint oauth2.Endpoint
34323535- // Select the appropriate provider endpoint
3633 switch strings.ToLower(provider) {
3734 case "spotify":
3835 endpoint = spotify.Endpoint
3936 default:
4040- // Use custom endpoints if not a predefined provider
3737+ // TODO: support custom endpoints plus lastfm
4138 endpoint = oauth2.Endpoint{
4239 AuthURL: "https://example.com/auth",
4340 TokenURL: "https://example.com/token",
+1-6
oauth/oauth_manager.go
···1414 SetAccessToken(token string) int64
1515}
16161717-// OAuthServiceManager manages multiple OAuth services
1717+// manages multiple oauth2 client services
1818type OAuthServiceManager struct {
1919 oauth2Services map[string]*OAuth2Service
2020 sessionManager *session.SessionManager
2121 mu sync.RWMutex
2222}
23232424-// NewOAuthServiceManager creates a new OAuthServiceManager
2524func NewOAuthServiceManager() *OAuthServiceManager {
2625 return &OAuthServiceManager{
2726 oauth2Services: make(map[string]*OAuth2Service),
···2928 }
3029}
31303232-// RegisterOAuth2Service adds a new OAuth2 service with PKCE to the manager
3331func (m *OAuthServiceManager) RegisterOAuth2Service(name string, service *OAuth2Service) {
3432 m.mu.Lock()
3533 defer m.mu.Unlock()
3634 m.oauth2Services[name] = service
3735}
38363939-// GetOAuth2Service retrieves an OAuth2 service by name
4037func (m *OAuthServiceManager) GetOAuth2Service(name string) (*OAuth2Service, bool) {
4138 m.mu.RLock()
4239 defer m.mu.RUnlock()
···4441 return service, exists
4542}
46434747-// HandleLogin creates a handler for the login endpoint for a specific service
4844func (m *OAuthServiceManager) HandleLogin(serviceName string) http.HandlerFunc {
4945 return func(w http.ResponseWriter, r *http.Request) {
5046 m.mu.RLock()
···6056 }
6157}
62586363-// HandleCallback creates a handler for the callback endpoint for a specific service
6459func (m *OAuthServiceManager) HandleCallback(serviceName string, tokenReceiver TokenReceiver) http.HandlerFunc {
6560 return func(w http.ResponseWriter, r *http.Request) {
6661 m.mu.RLock()