[WIP] music platform user data scraper
teal-fm atproto

split routes and handlers out of main.go

Changed files
+378 -324
cmd
+310
cmd/handlers.go
··· 1 + package main 2 + 3 + import ( 4 + "fmt" 5 + "log" 6 + "net/http" 7 + "strconv" 8 + 9 + "github.com/teal-fm/piper/db" 10 + "github.com/teal-fm/piper/service/musicbrainz" 11 + "github.com/teal-fm/piper/service/spotify" 12 + "github.com/teal-fm/piper/session" 13 + ) 14 + 15 + func home(database *db.DB) http.HandlerFunc { 16 + return func(w http.ResponseWriter, r *http.Request) { 17 + 18 + w.Header().Set("Content-Type", "text/html") 19 + 20 + userID, authenticated := session.GetUserID(r.Context()) 21 + isLoggedIn := authenticated 22 + lastfmUsername := "" 23 + 24 + if isLoggedIn { 25 + user, err := database.GetUserByID(userID) 26 + fmt.Printf("User: %+v\n", user) 27 + if err == nil && user != nil && user.LastFMUsername != nil { 28 + lastfmUsername = *user.LastFMUsername 29 + } else if err != nil { 30 + log.Printf("Error fetching user %d details for home page: %v", userID, err) 31 + } 32 + } 33 + 34 + html := ` 35 + <html> 36 + <head> 37 + <title>Piper - Spotify & Last.fm Tracker</title> 38 + <style> 39 + body { 40 + font-family: Arial, sans-serif; 41 + max-width: 800px; 42 + margin: 0 auto; 43 + padding: 20px; 44 + line-height: 1.6; 45 + } 46 + h1 { 47 + color: #1DB954; /* Spotify green */ 48 + } 49 + .nav { 50 + display: flex; 51 + flex-wrap: wrap; /* Allow wrapping on smaller screens */ 52 + margin-bottom: 20px; 53 + } 54 + .nav a { 55 + margin-right: 15px; 56 + margin-bottom: 5px; /* Add spacing below links */ 57 + text-decoration: none; 58 + color: #1DB954; 59 + font-weight: bold; 60 + } 61 + .card { 62 + border: 1px solid #ddd; 63 + border-radius: 8px; 64 + padding: 20px; 65 + margin-bottom: 20px; 66 + } 67 + .service-status { 68 + font-style: italic; 69 + color: #555; 70 + } 71 + </style> 72 + </head> 73 + <body> 74 + <h1>Piper - Multi-User Spotify & Last.fm Tracker via ATProto</h1> 75 + <div class="nav"> 76 + <a href="/">Home</a>` 77 + 78 + if isLoggedIn { 79 + html += ` 80 + <a href="/current-track">Spotify Current</a> 81 + <a href="/history">Spotify History</a> 82 + <a href="/link-lastfm">Link Last.fm</a>` // Link to Last.fm page 83 + if lastfmUsername != "" { 84 + html += ` <a href="/lastfm/recent">Last.fm Recent</a>` // Show only if linked 85 + } 86 + html += ` 87 + <a href="/api-keys">API Keys</a> 88 + <a href="/login/spotify">Connect Spotify Account</a> 89 + <a href="/logout">Logout</a>` 90 + } else { 91 + html += ` 92 + <a href="/login/atproto">Login with ATProto</a>` 93 + } 94 + 95 + html += ` 96 + </div> 97 + 98 + <div class="card"> 99 + <h2>Welcome to Piper</h2> 100 + <p>Piper is a multi-user application that records what you're listening to on Spotify and Last.fm, saving your listening history.</p>` 101 + 102 + if !isLoggedIn { 103 + html += ` 104 + <p><a href="/login/atproto">Login with ATProto</a> to get started!</p>` 105 + } else { 106 + html += ` 107 + <p>You're logged in!</p> 108 + <ul> 109 + <li><a href="/login/spotify">Connect your Spotify account</a> to start tracking.</li> 110 + <li><a href="/link-lastfm">Link your Last.fm account</a> to track scrobbles.</li> 111 + </ul> 112 + <p>Once connected, you can check out your:</p> 113 + <ul> 114 + <li><a href="/current-track">Spotify current track</a> or <a href="/history">listening history</a>.</li>` 115 + if lastfmUsername != "" { 116 + html += `<li><a href="/lastfm/recent">Last.fm recent tracks</a>.</li>` 117 + } 118 + html += ` 119 + </ul> 120 + <p>You can also manage your <a href="/api-keys">API keys</a> for programmatic access.</p>` 121 + if lastfmUsername != "" { 122 + html += fmt.Sprintf("<p class='service-status'>Last.fm Username: %s</p>", lastfmUsername) 123 + } else { 124 + html += "<p class='service-status'>Last.fm account not linked.</p>" 125 + } 126 + 127 + } 128 + 129 + html += ` 130 + </div> <!-- Close card div --> 131 + </body> 132 + </html> 133 + ` 134 + 135 + w.Write([]byte(html)) 136 + } 137 + } 138 + 139 + func handleLinkLastfmForm(database *db.DB) http.HandlerFunc { 140 + return func(w http.ResponseWriter, r *http.Request) { 141 + userID, _ := session.GetUserID(r.Context()) 142 + if r.Method == http.MethodPost { 143 + if err := r.ParseForm(); err != nil { 144 + http.Error(w, "Failed to parse form", http.StatusBadRequest) 145 + return 146 + } 147 + 148 + lastfmUsername := r.FormValue("lastfm_username") 149 + if lastfmUsername == "" { 150 + http.Error(w, "Last.fm username cannot be empty", http.StatusBadRequest) 151 + return 152 + } 153 + 154 + err := database.AddLastFMUsername(userID, lastfmUsername) 155 + if err != nil { 156 + log.Printf("Error saving Last.fm username for user %d: %v", userID, err) 157 + http.Error(w, "Failed to save Last.fm username", http.StatusInternalServerError) 158 + return 159 + } 160 + 161 + log.Printf("Successfully linked Last.fm username '%s' for user ID %d", lastfmUsername, userID) 162 + 163 + http.Redirect(w, r, "/", http.StatusSeeOther) 164 + } 165 + 166 + currentUser, err := database.GetUserByID(userID) 167 + currentUsername := "" 168 + if err == nil && currentUser != nil && currentUser.LastFMUsername != nil { 169 + currentUsername = *currentUser.LastFMUsername 170 + } else if err != nil { 171 + log.Printf("Error fetching user %d for Last.fm form: %v", userID, err) 172 + // Don't fail, just show an empty form 173 + } 174 + 175 + w.Header().Set("Content-Type", "text/html") 176 + fmt.Fprintf(w, ` 177 + <html> 178 + <head><title>Link Last.fm Account</title> 179 + <style> 180 + body { font-family: Arial, sans-serif; max-width: 600px; margin: 20px auto; padding: 20px; border: 1px solid #ddd; border-radius: 8px; } 181 + label, input { display: block; margin-bottom: 10px; } 182 + input[type='text'] { width: 95%%; padding: 8px; } /* Corrected width */ 183 + input[type='submit'] { padding: 10px 15px; background-color: #d51007; color: white; border: none; border-radius: 4px; cursor: pointer; } 184 + .nav { margin-bottom: 20px; } 185 + .nav a { margin-right: 10px; text-decoration: none; color: #1DB954; font-weight: bold; } 186 + .error { color: red; margin-bottom: 10px; } 187 + </style> 188 + </head> 189 + <body> 190 + <div class="nav"> 191 + <a href="/">Home</a> 192 + <a href="/link-lastfm">Link Last.fm</a> 193 + <a href="/logout">Logout</a> 194 + </div> 195 + <h2>Link Your Last.fm Account</h2> 196 + <p>Enter your Last.fm username to start tracking your scrobbles.</p> 197 + <form method="post" action="/link-lastfm"> 198 + <label for="lastfm_username">Last.fm Username:</label> 199 + <input type="text" id="lastfm_username" name="lastfm_username" value="%s" required> 200 + <input type="submit" value="Save Username"> 201 + </form> 202 + </body> 203 + </html>`, currentUsername) 204 + } 205 + } 206 + 207 + func handleLinkLastfmSubmit(database *db.DB) http.HandlerFunc { 208 + return func(w http.ResponseWriter, r *http.Request) { 209 + userID, _ := session.GetUserID(r.Context()) // Auth middleware ensures this exists 210 + 211 + if err := r.ParseForm(); err != nil { 212 + http.Error(w, "Failed to parse form", http.StatusBadRequest) 213 + return 214 + } 215 + 216 + lastfmUsername := r.FormValue("lastfm_username") 217 + if lastfmUsername == "" { 218 + http.Error(w, "Last.fm username cannot be empty", http.StatusBadRequest) 219 + return 220 + } 221 + 222 + err := database.AddLastFMUsername(userID, lastfmUsername) 223 + if err != nil { 224 + log.Printf("Error saving Last.fm username for user %d: %v", userID, err) 225 + http.Error(w, "Failed to save Last.fm username", http.StatusInternalServerError) 226 + return 227 + } 228 + 229 + log.Printf("Successfully linked Last.fm username '%s' for user ID %d", lastfmUsername, userID) 230 + 231 + http.Redirect(w, r, "/", http.StatusSeeOther) 232 + } 233 + } 234 + 235 + func apiCurrentTrack(spotifyService *spotify.SpotifyService) http.HandlerFunc { 236 + return func(w http.ResponseWriter, r *http.Request) { 237 + userID, ok := session.GetUserID(r.Context()) 238 + if !ok { 239 + jsonResponse(w, http.StatusUnauthorized, map[string]string{"error": "Unauthorized"}) 240 + return 241 + } 242 + 243 + tracks, err := spotifyService.DB.GetRecentTracks(userID, 1) 244 + if err != nil { 245 + jsonResponse(w, http.StatusInternalServerError, map[string]string{"error": "Failed to get current track: " + err.Error()}) 246 + return 247 + } 248 + 249 + if len(tracks) == 0 { 250 + jsonResponse(w, http.StatusOK, nil) 251 + return 252 + } 253 + 254 + jsonResponse(w, http.StatusOK, tracks[0]) 255 + } 256 + } 257 + 258 + func apiTrackHistory(spotifyService *spotify.SpotifyService) http.HandlerFunc { 259 + return func(w http.ResponseWriter, r *http.Request) { 260 + userID, ok := session.GetUserID(r.Context()) 261 + if !ok { 262 + jsonResponse(w, http.StatusUnauthorized, map[string]string{"error": "Unauthorized"}) 263 + return 264 + } 265 + 266 + limitStr := r.URL.Query().Get("limit") 267 + limit := 50 // Default limit 268 + if limitStr != "" { 269 + if l, err := strconv.Atoi(limitStr); err == nil && l > 0 { 270 + limit = l 271 + } 272 + } 273 + if limit > 200 { 274 + limit = 200 275 + } 276 + 277 + tracks, err := spotifyService.DB.GetRecentTracks(userID, limit) 278 + if err != nil { 279 + jsonResponse(w, http.StatusInternalServerError, map[string]string{"error": "Failed to get track history: " + err.Error()}) 280 + return 281 + } 282 + 283 + jsonResponse(w, http.StatusOK, tracks) 284 + } 285 + } 286 + 287 + func apiMusicBrainzSearch(mbService *musicbrainz.MusicBrainzService) http.HandlerFunc { 288 + return func(w http.ResponseWriter, r *http.Request) { 289 + 290 + params := musicbrainz.SearchParams{ 291 + Track: r.URL.Query().Get("track"), 292 + Artist: r.URL.Query().Get("artist"), 293 + Release: r.URL.Query().Get("release"), 294 + } 295 + 296 + if params.Track == "" && params.Artist == "" && params.Release == "" { 297 + jsonResponse(w, http.StatusBadRequest, map[string]string{"error": "At least one query parameter (track, artist, release) is required"}) 298 + return 299 + } 300 + 301 + recordings, err := mbService.SearchMusicBrainz(r.Context(), params) 302 + if err != nil { 303 + log.Printf("Error searching MusicBrainz: %v", err) // Log the error 304 + jsonResponse(w, http.StatusInternalServerError, map[string]string{"error": "Failed to search MusicBrainz"}) 305 + return 306 + } 307 + 308 + jsonResponse(w, http.StatusOK, recordings) 309 + } 310 + }
+25 -324
cmd/main.go
··· 6 6 "log" 7 7 "net/http" 8 8 "os" 9 - "strconv" 10 9 "time" 11 10 12 11 "github.com/spf13/viper" ··· 21 20 "github.com/teal-fm/piper/session" 22 21 ) 23 22 24 - func home(database *db.DB) http.HandlerFunc { 25 - return func(w http.ResponseWriter, r *http.Request) { 26 - 27 - w.Header().Set("Content-Type", "text/html") 28 - 29 - userID, authenticated := session.GetUserID(r.Context()) 30 - isLoggedIn := authenticated 31 - lastfmUsername := "" 32 - 33 - if isLoggedIn { 34 - user, err := database.GetUserByID(userID) 35 - fmt.Printf("User: %+v\n", user) 36 - if err == nil && user != nil && user.LastFMUsername != nil { 37 - lastfmUsername = *user.LastFMUsername 38 - } else if err != nil { 39 - log.Printf("Error fetching user %d details for home page: %v", userID, err) 40 - } 41 - } 42 - 43 - html := ` 44 - <html> 45 - <head> 46 - <title>Piper - Spotify & Last.fm Tracker</title> 47 - <style> 48 - body { 49 - font-family: Arial, sans-serif; 50 - max-width: 800px; 51 - margin: 0 auto; 52 - padding: 20px; 53 - line-height: 1.6; 54 - } 55 - h1 { 56 - color: #1DB954; /* Spotify green */ 57 - } 58 - .nav { 59 - display: flex; 60 - flex-wrap: wrap; /* Allow wrapping on smaller screens */ 61 - margin-bottom: 20px; 62 - } 63 - .nav a { 64 - margin-right: 15px; 65 - margin-bottom: 5px; /* Add spacing below links */ 66 - text-decoration: none; 67 - color: #1DB954; 68 - font-weight: bold; 69 - } 70 - .card { 71 - border: 1px solid #ddd; 72 - border-radius: 8px; 73 - padding: 20px; 74 - margin-bottom: 20px; 75 - } 76 - .service-status { 77 - font-style: italic; 78 - color: #555; 79 - } 80 - </style> 81 - </head> 82 - <body> 83 - <h1>Piper - Multi-User Spotify & Last.fm Tracker via ATProto</h1> 84 - <div class="nav"> 85 - <a href="/">Home</a>` 86 - 87 - if isLoggedIn { 88 - html += ` 89 - <a href="/current-track">Spotify Current</a> 90 - <a href="/history">Spotify History</a> 91 - <a href="/link-lastfm">Link Last.fm</a>` // Link to Last.fm page 92 - if lastfmUsername != "" { 93 - html += ` <a href="/lastfm/recent">Last.fm Recent</a>` // Show only if linked 94 - } 95 - html += ` 96 - <a href="/api-keys">API Keys</a> 97 - <a href="/login/spotify">Connect Spotify Account</a> 98 - <a href="/logout">Logout</a>` 99 - } else { 100 - html += ` 101 - <a href="/login/atproto">Login with ATProto</a>` 102 - } 103 - 104 - html += ` 105 - </div> 106 - 107 - <div class="card"> 108 - <h2>Welcome to Piper</h2> 109 - <p>Piper is a multi-user application that records what you're listening to on Spotify and Last.fm, saving your listening history.</p>` 110 - 111 - if !isLoggedIn { 112 - html += ` 113 - <p><a href="/login/atproto">Login with ATProto</a> to get started!</p>` 114 - } else { 115 - html += ` 116 - <p>You're logged in!</p> 117 - <ul> 118 - <li><a href="/login/spotify">Connect your Spotify account</a> to start tracking.</li> 119 - <li><a href="/link-lastfm">Link your Last.fm account</a> to track scrobbles.</li> 120 - </ul> 121 - <p>Once connected, you can check out your:</p> 122 - <ul> 123 - <li><a href="/current-track">Spotify current track</a> or <a href="/history">listening history</a>.</li>` 124 - if lastfmUsername != "" { 125 - html += `<li><a href="/lastfm/recent">Last.fm recent tracks</a>.</li>` 126 - } 127 - html += ` 128 - </ul> 129 - <p>You can also manage your <a href="/api-keys">API keys</a> for programmatic access.</p>` 130 - if lastfmUsername != "" { 131 - html += fmt.Sprintf("<p class='service-status'>Last.fm Username: %s</p>", lastfmUsername) 132 - } else { 133 - html += "<p class='service-status'>Last.fm account not linked.</p>" 134 - } 135 - 136 - } 137 - 138 - html += ` 139 - </div> <!-- Close card div --> 140 - </body> 141 - </html> 142 - ` 143 - 144 - w.Write([]byte(html)) 145 - } 146 - } 147 - 148 - func handleLinkLastfmForm(database *db.DB) http.HandlerFunc { 149 - return func(w http.ResponseWriter, r *http.Request) { 150 - userID, _ := session.GetUserID(r.Context()) 151 - if r.Method == http.MethodPost { 152 - if err := r.ParseForm(); err != nil { 153 - http.Error(w, "Failed to parse form", http.StatusBadRequest) 154 - return 155 - } 156 - 157 - lastfmUsername := r.FormValue("lastfm_username") 158 - if lastfmUsername == "" { 159 - http.Error(w, "Last.fm username cannot be empty", http.StatusBadRequest) 160 - return 161 - } 162 - 163 - err := database.AddLastFMUsername(userID, lastfmUsername) 164 - if err != nil { 165 - log.Printf("Error saving Last.fm username for user %d: %v", userID, err) 166 - http.Error(w, "Failed to save Last.fm username", http.StatusInternalServerError) 167 - return 168 - } 169 - 170 - log.Printf("Successfully linked Last.fm username '%s' for user ID %d", lastfmUsername, userID) 171 - 172 - http.Redirect(w, r, "/", http.StatusSeeOther) 173 - } 174 - 175 - currentUser, err := database.GetUserByID(userID) 176 - currentUsername := "" 177 - if err == nil && currentUser != nil && currentUser.LastFMUsername != nil { 178 - currentUsername = *currentUser.LastFMUsername 179 - } else if err != nil { 180 - log.Printf("Error fetching user %d for Last.fm form: %v", userID, err) 181 - // Don't fail, just show an empty form 182 - } 183 - 184 - w.Header().Set("Content-Type", "text/html") 185 - fmt.Fprintf(w, ` 186 - <html> 187 - <head><title>Link Last.fm Account</title> 188 - <style> 189 - body { font-family: Arial, sans-serif; max-width: 600px; margin: 20px auto; padding: 20px; border: 1px solid #ddd; border-radius: 8px; } 190 - label, input { display: block; margin-bottom: 10px; } 191 - input[type='text'] { width: 95%%; padding: 8px; } /* Corrected width */ 192 - input[type='submit'] { padding: 10px 15px; background-color: #d51007; color: white; border: none; border-radius: 4px; cursor: pointer; } 193 - .nav { margin-bottom: 20px; } 194 - .nav a { margin-right: 10px; text-decoration: none; color: #1DB954; font-weight: bold; } 195 - .error { color: red; margin-bottom: 10px; } 196 - </style> 197 - </head> 198 - <body> 199 - <div class="nav"> 200 - <a href="/">Home</a> 201 - <a href="/link-lastfm">Link Last.fm</a> 202 - <a href="/logout">Logout</a> 203 - </div> 204 - <h2>Link Your Last.fm Account</h2> 205 - <p>Enter your Last.fm username to start tracking your scrobbles.</p> 206 - <form method="post" action="/link-lastfm"> 207 - <label for="lastfm_username">Last.fm Username:</label> 208 - <input type="text" id="lastfm_username" name="lastfm_username" value="%s" required> 209 - <input type="submit" value="Save Username"> 210 - </form> 211 - </body> 212 - </html>`, currentUsername) 213 - } 214 - } 215 - 216 - func handleLinkLastfmSubmit(database *db.DB) http.HandlerFunc { 217 - return func(w http.ResponseWriter, r *http.Request) { 218 - userID, _ := session.GetUserID(r.Context()) // Auth middleware ensures this exists 219 - 220 - if err := r.ParseForm(); err != nil { 221 - http.Error(w, "Failed to parse form", http.StatusBadRequest) 222 - return 223 - } 224 - 225 - lastfmUsername := r.FormValue("lastfm_username") 226 - if lastfmUsername == "" { 227 - http.Error(w, "Last.fm username cannot be empty", http.StatusBadRequest) 228 - return 229 - } 230 - 231 - err := database.AddLastFMUsername(userID, lastfmUsername) 232 - if err != nil { 233 - log.Printf("Error saving Last.fm username for user %d: %v", userID, err) 234 - http.Error(w, "Failed to save Last.fm username", http.StatusInternalServerError) 235 - return 236 - } 237 - 238 - log.Printf("Successfully linked Last.fm username '%s' for user ID %d", lastfmUsername, userID) 239 - 240 - http.Redirect(w, r, "/", http.StatusSeeOther) 241 - } 23 + type application struct { 24 + database *db.DB 25 + sessionManager *session.SessionManager 26 + oauthManager *oauth.OAuthServiceManager 27 + spotifyService *spotify.SpotifyService 28 + apiKeyService *apikeyService.Service 29 + mbService *musicbrainz.MusicBrainzService 30 + atprotoService *atproto.ATprotoAuthService 242 31 } 243 32 244 33 // JSON API handlers ··· 251 40 } 252 41 } 253 42 254 - func apiCurrentTrack(spotifyService *spotify.SpotifyService) http.HandlerFunc { 255 - return func(w http.ResponseWriter, r *http.Request) { 256 - userID, ok := session.GetUserID(r.Context()) 257 - if !ok { 258 - jsonResponse(w, http.StatusUnauthorized, map[string]string{"error": "Unauthorized"}) 259 - return 260 - } 261 - 262 - tracks, err := spotifyService.DB.GetRecentTracks(userID, 1) 263 - if err != nil { 264 - jsonResponse(w, http.StatusInternalServerError, map[string]string{"error": "Failed to get current track: " + err.Error()}) 265 - return 266 - } 267 - 268 - if len(tracks) == 0 { 269 - jsonResponse(w, http.StatusOK, nil) 270 - return 271 - } 272 - 273 - jsonResponse(w, http.StatusOK, tracks[0]) 274 - } 275 - } 276 - 277 - func apiTrackHistory(spotifyService *spotify.SpotifyService) http.HandlerFunc { 278 - return func(w http.ResponseWriter, r *http.Request) { 279 - userID, ok := session.GetUserID(r.Context()) 280 - if !ok { 281 - jsonResponse(w, http.StatusUnauthorized, map[string]string{"error": "Unauthorized"}) 282 - return 283 - } 284 - 285 - limitStr := r.URL.Query().Get("limit") 286 - limit := 50 // Default limit 287 - if limitStr != "" { 288 - if l, err := strconv.Atoi(limitStr); err == nil && l > 0 { 289 - limit = l 290 - } 291 - } 292 - if limit > 200 { 293 - limit = 200 294 - } 295 - 296 - tracks, err := spotifyService.DB.GetRecentTracks(userID, limit) 297 - if err != nil { 298 - jsonResponse(w, http.StatusInternalServerError, map[string]string{"error": "Failed to get track history: " + err.Error()}) 299 - return 300 - } 301 - 302 - jsonResponse(w, http.StatusOK, tracks) 303 - } 304 - } 305 - 306 - func apiMusicBrainzSearch(mbService *musicbrainz.MusicBrainzService) http.HandlerFunc { 307 - return func(w http.ResponseWriter, r *http.Request) { 308 - 309 - params := musicbrainz.SearchParams{ 310 - Track: r.URL.Query().Get("track"), 311 - Artist: r.URL.Query().Get("artist"), 312 - Release: r.URL.Query().Get("release"), 313 - } 314 - 315 - if params.Track == "" && params.Artist == "" && params.Release == "" { 316 - jsonResponse(w, http.StatusBadRequest, map[string]string{"error": "At least one query parameter (track, artist, release) is required"}) 317 - return 318 - } 319 - 320 - recordings, err := mbService.SearchMusicBrainz(r.Context(), params) 321 - if err != nil { 322 - log.Printf("Error searching MusicBrainz: %v", err) // Log the error 323 - jsonResponse(w, http.StatusInternalServerError, map[string]string{"error": "Failed to search MusicBrainz"}) 324 - return 325 - } 326 - 327 - jsonResponse(w, http.StatusOK, recordings) 328 - } 329 - } 330 - 331 43 func main() { 332 44 config.Load() 333 45 ··· 379 91 380 92 apiKeyService := apikeyService.NewAPIKeyService(database, sessionManager) 381 93 382 - http.HandleFunc("/", session.WithPossibleAuth(home(database), sessionManager)) 383 - 384 - // OAuth Routes 385 - http.HandleFunc("/login/spotify", oauthManager.HandleLogin("spotify")) 386 - http.HandleFunc("/callback/spotify", session.WithPossibleAuth(oauthManager.HandleCallback("spotify"), sessionManager)) // Use possible auth 387 - http.HandleFunc("/login/atproto", oauthManager.HandleLogin("atproto")) 388 - http.HandleFunc("/callback/atproto", session.WithPossibleAuth(oauthManager.HandleCallback("atproto"), sessionManager)) // Use possible auth 94 + app := &application{ 95 + database: database, 96 + sessionManager: sessionManager, 97 + oauthManager: oauthManager, 98 + apiKeyService: apiKeyService, 99 + mbService: mbService, 100 + spotifyService: spotifyService, 101 + atprotoService: atprotoService, 102 + } // MusicBrainz (public?) 389 103 390 - // Authenticated Web Routes 391 - http.HandleFunc("/current-track", session.WithAuth(spotifyService.HandleCurrentTrack, sessionManager)) 392 - http.HandleFunc("/history", session.WithAuth(spotifyService.HandleTrackHistory, sessionManager)) 393 - http.HandleFunc("/api-keys", session.WithAuth(apiKeyService.HandleAPIKeyManagement, sessionManager)) 394 - http.HandleFunc("/link-lastfm", session.WithAuth(handleLinkLastfmForm(database), sessionManager)) // GET form 395 - http.HandleFunc("/link-lastfm/submit", session.WithAuth(handleLinkLastfmSubmit(database), sessionManager)) // POST submit - Changed route slightly 396 - http.HandleFunc("/logout", sessionManager.HandleLogout) 397 - http.HandleFunc("/debug/", session.WithAuth(sessionManager.HandleDebug, sessionManager)) 398 - 399 - http.HandleFunc("/api/v1/current-track", session.WithAPIAuth(apiCurrentTrack(spotifyService), sessionManager)) // Spotify Current 400 - http.HandleFunc("/api/v1/history", session.WithAPIAuth(apiTrackHistory(spotifyService), sessionManager)) // Spotify History 401 - http.HandleFunc("/api/v1/musicbrainz/search", apiMusicBrainzSearch(mbService)) // MusicBrainz (public?) 402 - 403 - serverUrlRoot := viper.GetString("server.root_url") 404 - atpClientId := viper.GetString("atproto.client_id") 405 - atpCallbackUrl := viper.GetString("atproto.callback_url") 406 - http.HandleFunc("/.well-known/client-metadata.json", func(w http.ResponseWriter, r *http.Request) { 407 - atprotoService.HandleClientMetadata(w, r, serverUrlRoot, atpClientId, atpCallbackUrl) 408 - }) 409 - http.HandleFunc("/oauth/jwks.json", atprotoService.HandleJwks) 410 104 trackerInterval := time.Duration(viper.GetInt("tracker.interval")) * time.Second 411 105 lastfmInterval := time.Duration(viper.GetInt("lastfm.interval_seconds")) * time.Second // Add config for Last.fm interval 412 106 if lastfmInterval <= 0 { ··· 421 115 go lastfmService.StartListeningTracker(lastfmInterval) 422 116 423 117 serverAddr := fmt.Sprintf("%s:%s", viper.GetString("server.host"), viper.GetString("server.port")) 118 + server := &http.Server{ 119 + Addr: serverAddr, 120 + Handler: routes(app), 121 + IdleTimeout: time.Minute, 122 + ReadTimeout: 5 * time.Second, 123 + WriteTimeout: 10 * time.Second, 124 + } 424 125 fmt.Printf("Server running at: http://%s\n", serverAddr) 425 - log.Fatal(http.ListenAndServe(serverAddr, nil)) 126 + log.Fatal(server.ListenAndServe()) 426 127 }
+43
cmd/routes.go
··· 1 + package main 2 + 3 + import ( 4 + "net/http" 5 + 6 + "github.com/spf13/viper" 7 + "github.com/teal-fm/piper/session" 8 + ) 9 + 10 + func routes(app *application) http.Handler { 11 + mux := http.NewServeMux() 12 + 13 + mux.HandleFunc("/", session.WithPossibleAuth(home(app.database), app.sessionManager)) 14 + 15 + // OAuth Routes 16 + mux.HandleFunc("/login/spotify", app.oauthManager.HandleLogin("spotify")) 17 + mux.HandleFunc("/callback/spotify", session.WithPossibleAuth(app.oauthManager.HandleCallback("spotify"), app.sessionManager)) // Use possible auth 18 + mux.HandleFunc("/login/atproto", app.oauthManager.HandleLogin("atproto")) 19 + mux.HandleFunc("/callback/atproto", session.WithPossibleAuth(app.oauthManager.HandleCallback("atproto"), app.sessionManager)) // Use possible auth 20 + 21 + // Authenticated Web Routes 22 + mux.HandleFunc("/current-track", session.WithAuth(app.spotifyService.HandleCurrentTrack, app.sessionManager)) 23 + mux.HandleFunc("/history", session.WithAuth(app.spotifyService.HandleTrackHistory, app.sessionManager)) 24 + mux.HandleFunc("/api-keys", session.WithAuth(app.apiKeyService.HandleAPIKeyManagement, app.sessionManager)) 25 + mux.HandleFunc("/link-lastfm", session.WithAuth(handleLinkLastfmForm(app.database), app.sessionManager)) // GET form 26 + mux.HandleFunc("/link-lastfm/submit", session.WithAuth(handleLinkLastfmSubmit(app.database), app.sessionManager)) // POST submit - Changed route slightly 27 + mux.HandleFunc("/logout", app.sessionManager.HandleLogout) 28 + mux.HandleFunc("/debug/", session.WithAuth(app.sessionManager.HandleDebug, app.sessionManager)) 29 + 30 + mux.HandleFunc("/api/v1/current-track", session.WithAPIAuth(apiCurrentTrack(app.spotifyService), app.sessionManager)) // Spotify Current 31 + mux.HandleFunc("/api/v1/history", session.WithAPIAuth(apiTrackHistory(app.spotifyService), app.sessionManager)) // Spotify History 32 + mux.HandleFunc("/api/v1/musicbrainz/search", apiMusicBrainzSearch(app.mbService)) 33 + 34 + serverUrlRoot := viper.GetString("server.root_url") 35 + atpClientId := viper.GetString("atproto.client_id") 36 + atpCallbackUrl := viper.GetString("atproto.callback_url") 37 + http.HandleFunc("/.well-known/client-metadata.json", func(w http.ResponseWriter, r *http.Request) { 38 + app.atprotoService.HandleClientMetadata(w, r, serverUrlRoot, atpClientId, atpCallbackUrl) 39 + }) 40 + mux.HandleFunc("/oauth/jwks.json", app.atprotoService.HandleJwks) 41 + 42 + return mux 43 + }