From fd1dfd387db6e876b5eeba8c9194d8d61bf18e08 Mon Sep 17 00:00:00 2001 From: Bailey Townsend Date: Fri, 26 Dec 2025 16:32:34 -0600 Subject: [PATCH] handled some more errors --- cmd/main.go | 6 ++++- db/apikey/apikey.go | 21 ++++++++++++--- db/db.go | 42 +++++++++++++++++++++++++----- db/lfm.go | 9 ++++++- oauth/oauth2.go | 2 ++ service/applemusic/applemusic.go | 12 +++++++-- service/lastfm/lastfm.go | 12 +++++++-- service/musicbrainz/musicbrainz.go | 8 +++++- service/spotify/playlists.go | 7 ++++- service/spotify/spotify.go | 39 ++++++++++++++++++++++----- session/session.go | 31 ++++++++++++++++++---- 11 files changed, 161 insertions(+), 28 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index f3d0fde..f829b87 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -42,7 +42,11 @@ func jsonResponse(w http.ResponseWriter, statusCode int, data any) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(statusCode) if data != nil { - json.NewEncoder(w).Encode(data) + err := json.NewEncoder(w).Encode(data) + if err != nil { + log.Printf("Error encoding JSON response: %v", err) + return + } } } diff --git a/db/apikey/apikey.go b/db/apikey/apikey.go index a02e2aa..c2d4561 100644 --- a/db/apikey/apikey.go +++ b/db/apikey/apikey.go @@ -2,8 +2,10 @@ package apikey import ( "crypto/rand" + "database/sql" "encoding/base64" "errors" + "fmt" "log" "net/http" "strings" @@ -101,7 +103,11 @@ func (am *Manager) GetApiKey(apiKeyID string) (*ApiKey, bool) { if exists { // Check if API key is expired if time.Now().UTC().After(apiKey.ExpiresAt) { - am.DeleteApiKey(apiKeyID) + err := am.DeleteApiKey(apiKeyID) + fmt.Println("Error deleting an expired API key: %w", err) + if err != nil { + return nil, false + } return nil, false } return apiKey, true @@ -119,7 +125,11 @@ func (am *Manager) GetApiKey(apiKeyID string) (*ApiKey, bool) { } if time.Now().UTC().After(apiKey.ExpiresAt) { - am.DeleteApiKey(apiKeyID) + err := am.DeleteApiKey(apiKeyID) + fmt.Println("Error deleting an expired API key: %w", err) + if err != nil { + return nil, false + } return nil, false } @@ -152,7 +162,12 @@ func (am *Manager) GetUserApiKeys(userID int64) ([]*ApiKey, error) { if err != nil { return nil, err } - defer rows.Close() + defer func(rows *sql.Rows) { + err := rows.Close() + if err != nil { + fmt.Println("Error closing API keys rows: %w", err) + } + }(rows) var apiKeys []*ApiKey for rows.Next() { diff --git a/db/db.go b/db/db.go index 5635804..893a5ee 100644 --- a/db/db.go +++ b/db/db.go @@ -330,7 +330,12 @@ func (db *DB) GetAllAppleMusicLinkedUsers() ([]*models.User, error) { if err != nil { return nil, err } - defer rows.Close() + defer func(rows *sql.Rows) { + err := rows.Close() + if err != nil { + log.Println(err) + } + }(rows) var users []*models.User for rows.Next() { @@ -419,7 +424,12 @@ func (db *DB) GetRecentTracks(userID int64, limit int) ([]*models.Track, error) if err != nil { return nil, err } - defer rows.Close() + defer func(rows *sql.Rows) { + err := rows.Close() + if err != nil { + log.Println(err) + } + }(rows) var tracks []*models.Track @@ -490,7 +500,12 @@ func (db *DB) GetUsersWithExpiredTokens() ([]*models.User, error) { if err != nil { return nil, err } - defer rows.Close() + defer func(rows *sql.Rows) { + err := rows.Close() + if err != nil { + + } + }(rows) return SpotifyQueryMapping(rows) @@ -506,7 +521,12 @@ func (db *DB) GetAllActiveUsers() ([]*models.User, error) { if err != nil { return nil, err } - defer rows.Close() + defer func(rows *sql.Rows) { + err := rows.Close() + if err != nil { + + } + }(rows) return SpotifyQueryMapping(rows) } @@ -521,7 +541,12 @@ func (db *DB) GetAllActiveUsersWithUnExpiredTokens() ([]*models.User, error) { if err != nil { return nil, err } - defer rows.Close() + defer func(rows *sql.Rows) { + err := rows.Close() + if err != nil { + + } + }(rows) return SpotifyQueryMapping(rows) } @@ -537,7 +562,12 @@ func (db *DB) DebugViewUserInformation(userID int64) (map[string]any, error) { if err != nil { return nil, fmt.Errorf("query failed: %w", err) } - defer rows.Close() + defer func(rows *sql.Rows) { + err := rows.Close() + if err != nil { + + } + }(rows) // Get column names cols, err := rows.Columns() diff --git a/db/lfm.go b/db/lfm.go index 9d6eaaa..983c1aa 100644 --- a/db/lfm.go +++ b/db/lfm.go @@ -1,6 +1,8 @@ package db import ( + "database/sql" + "github.com/teal-fm/piper/models" ) @@ -23,7 +25,12 @@ func (db *DB) GetAllUsersWithLastFM() ([]*models.User, error) { if err != nil { return nil, err } - defer rows.Close() + defer func(rows *sql.Rows) { + err := rows.Close() + if err != nil { + db.logger.Printf("Error closing rows: %s", err) + } + }(rows) var users []*models.User diff --git a/oauth/oauth2.go b/oauth/oauth2.go index e4adeea..8eb214d 100644 --- a/oauth/oauth2.go +++ b/oauth/oauth2.go @@ -26,6 +26,7 @@ type Service struct { func GenerateRandomState() string { b := make([]byte, 16) + //This probably should panic rand.Read(b) return base64.URLEncoding.EncodeToString(b) } @@ -66,6 +67,7 @@ func NewOAuth2Service(clientID, clientSecret, redirectURI string, scopes []strin // GenerateCodeVerifier generate a random code verifier, for PKCE func GenerateCodeVerifier() string { b := make([]byte, 64) + //This probably should panic rand.Read(b) return base64.RawURLEncoding.EncodeToString(b) } diff --git a/service/applemusic/applemusic.go b/service/applemusic/applemusic.go index ad5caf9..def17fb 100644 --- a/service/applemusic/applemusic.go +++ b/service/applemusic/applemusic.go @@ -94,7 +94,10 @@ func (s *Service) HandleDeveloperToken(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - w.Write([]byte(fmt.Sprintf(`{"token":"%s","expiresAt":"%s"}`, token, exp.UTC().Format(time.RFC3339)))) + _, err = w.Write([]byte(fmt.Sprintf(`{"token":"%s","expiresAt":"%s"}`, token, exp.UTC().Format(time.RFC3339)))) + if err != nil { + s.logger.Printf("failed to write response: %v", err) + } } // GenerateDeveloperTokenWithForce allows bypassing caches when force is true. @@ -311,7 +314,12 @@ func (s *Service) FetchRecentPlayedTracks(ctx context.Context, userToken string, if err != nil { return nil, err } - defer resp.Body.Close() + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + s.logger.Printf("failed to close response body: %v", err) + } + }(resp.Body) // Read the full response body to log it bodyBytes, err := io.ReadAll(resp.Body) diff --git a/service/lastfm/lastfm.go b/service/lastfm/lastfm.go index f4b21f1..2609668 100644 --- a/service/lastfm/lastfm.go +++ b/service/lastfm/lastfm.go @@ -125,7 +125,12 @@ func (l *Service) getRecentTracks(ctx context.Context, username string, limit in if err != nil { return nil, fmt.Errorf("failed to fetch recent tracks for %s: %w", username, err) } - defer resp.Body.Close() + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + l.logger.Printf("Error closing response body for %s: %v", username, err) + } + }(resp.Body) if resp.StatusCode != http.StatusOK { bodyBytes, _ := io.ReadAll(resp.Body) @@ -387,7 +392,10 @@ func (l *Service) processTracks(ctx context.Context, username string, tracks []T // we can use the track without MBIDs, it's still valid hydratedTrack = &baseTrack } - l.db.SaveTrack(user.ID, hydratedTrack) + _, err = l.db.SaveTrack(user.ID, hydratedTrack) + if err != nil { + return err + } l.logger.Printf("Submitting track") err = l.SubmitTrackToPDS(*user.ATProtoDID, *user.MostRecentAtProtoSessionID, hydratedTrack, ctx) if err != nil { diff --git a/service/musicbrainz/musicbrainz.go b/service/musicbrainz/musicbrainz.go index 2c502c4..4c25fee 100644 --- a/service/musicbrainz/musicbrainz.go +++ b/service/musicbrainz/musicbrainz.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "io" "log" "net/http" "net/url" @@ -175,7 +176,12 @@ func (s *Service) SearchMusicBrainz(ctx context.Context, params SearchParams) ([ } return nil, fmt.Errorf("failed to execute request to %s: %w", endpoint, err) } - defer resp.Body.Close() + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + s.logger.Printf("Error closing response body for %s: %v", endpoint, err) + } + }(resp.Body) if resp.StatusCode != http.StatusOK { // TODO: read body for detailed error message diff --git a/service/spotify/playlists.go b/service/spotify/playlists.go index 8aa10c2..f129efe 100644 --- a/service/spotify/playlists.go +++ b/service/spotify/playlists.go @@ -35,7 +35,12 @@ func (s *Service) getUserPlaylists(userID int64) (*PlaylistResponse, error) { if err != nil { return nil, fmt.Errorf("failed to get user playlists: %w", err) } - defer resp.Body.Close() + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + log.Fatal("failed to close resp.Body") + } + }(resp.Body) if resp.Response.StatusCode != http.StatusOK { return nil, fmt.Errorf("error response from Spotify: %s", resp.Response.Status) diff --git a/service/spotify/spotify.go b/service/spotify/spotify.go index b6c8cdb..a38ba55 100644 --- a/service/spotify/spotify.go +++ b/service/spotify/spotify.go @@ -225,7 +225,12 @@ func (s *Service) refreshTokenInner(userID int64) (string, error) { if err != nil { return "", fmt.Errorf("failed to execute refresh request: %w", err) } - defer resp.Body.Close() + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + s.logger.Printf("Failed to close refresh response body: %v", err) + } + }(resp.Body) body, readErr := io.ReadAll(resp.Body) if readErr != nil { @@ -327,7 +332,12 @@ func (s *Service) fetchSpotifyProfile(token string) (*spotifyProfile, error) { if err != nil { return nil, err } - defer resp.Body.Close() + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + s.logger.Printf("Failed to close spotify profile response body: %v", err) + } + }(resp.Body) if resp.StatusCode != 200 { body, _ := io.ReadAll(resp.Body) @@ -354,12 +364,20 @@ func (s *Service) HandleCurrentTrack(w http.ResponseWriter, r *http.Request) { s.mu.RUnlock() if !exists || track == nil { - fmt.Fprintf(w, "No track currently playing") + _, err := fmt.Fprintf(w, "No track currently playing") + if err != nil { + s.logger.Printf("Error writing response: %v", err) + return + } return } w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(track) + err := json.NewEncoder(w).Encode(track) + if err != nil { + s.logger.Printf("Error encoding response: %v", err) + return + } } func (s *Service) HandleTrackHistory(w http.ResponseWriter, r *http.Request) { @@ -377,7 +395,11 @@ func (s *Service) HandleTrackHistory(w http.ResponseWriter, r *http.Request) { } w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(tracks) + err = json.NewEncoder(w).Encode(tracks) + if err != nil { + s.logger.Printf("Error encoding response: %v", err) + return + } } func (s *Service) FetchCurrentTrack(userID int64) (*models.Track, error) { @@ -439,7 +461,12 @@ func (s *Service) FetchCurrentTrack(userID int64) (*models.Track, error) { // Ensure body is closed regardless of loop outcome if resp != nil && resp.Body != nil { - defer resp.Body.Close() + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + s.logger.Printf("Failed to close spotify response body: %v", err) + } + }(resp.Body) } // Handle final response after loop diff --git a/session/session.go b/session/session.go index 7399ca3..019e319 100644 --- a/session/session.go +++ b/session/session.go @@ -63,6 +63,7 @@ func (sm *Manager) CreateSession(userID int64, atProtoSessionId string) *Session // random session id b := make([]byte, 32) + //This probably should panic rand.Read(b) sessionID := base64.URLEncoding.EncodeToString(b) @@ -270,7 +271,11 @@ func WithAPIAuth(handler http.HandlerFunc, sm *Manager) http.HandlerFunc { if apiKeyErr != nil || apiKeyStr == "" { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte(`{"error": "API key is required"}`)) + _, err := w.Write([]byte(`{"error": "API key is required"}`)) + if err != nil { + log.Printf("Error writing error response: %v", err) + return + } return } @@ -278,7 +283,11 @@ func WithAPIAuth(handler http.HandlerFunc, sm *Manager) http.HandlerFunc { if !valid { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte(`{"error": "Invalid or expired API key"}`)) + _, err := w.Write([]byte(`{"error": "Invalid or expired API key"}`)) + if err != nil { + log.Printf("Error writing error response: %v", err) + return + } return } @@ -296,7 +305,11 @@ func (sm *Manager) HandleDebug(w http.ResponseWriter, r *http.Request) { if !ok { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte(`{"error": "User ID not found in context"}`)) + _, err := w.Write([]byte(`{"error": "User ID not found in context"}`)) + if err != nil { + log.Printf("Error writing error response: %v", err) + return + } return } @@ -304,13 +317,21 @@ func (sm *Manager) HandleDebug(w http.ResponseWriter, r *http.Request) { if err != nil { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf(`{"error": "Failed to retrieve user information: %v"}`, err))) + _, err := w.Write([]byte(fmt.Sprintf(`{"error": "Failed to retrieve user information: %v"}`, err))) + if err != nil { + log.Printf("Error writing error response: %v", err) + return + } return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(res) + err = json.NewEncoder(w).Encode(res) + if err != nil { + log.Printf("Error encoding JSON response: %v", err) + return + } } type contextKey int -- 2.51.2