+310
cmd/handlers.go
+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
+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
+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
+
}