1package main
2
3import (
4 "encoding/json"
5 "fmt"
6 "log"
7 "net/http"
8 "time"
9
10 "github.com/teal-fm/piper/service/lastfm"
11 "github.com/teal-fm/piper/service/playingnow"
12
13 "github.com/spf13/viper"
14 "github.com/teal-fm/piper/config"
15 "github.com/teal-fm/piper/db"
16 "github.com/teal-fm/piper/oauth"
17 "github.com/teal-fm/piper/oauth/atproto"
18 pages "github.com/teal-fm/piper/pages"
19 apikeyService "github.com/teal-fm/piper/service/apikey"
20 "github.com/teal-fm/piper/service/musicbrainz"
21 "github.com/teal-fm/piper/service/spotify"
22 "github.com/teal-fm/piper/session"
23)
24
25type application struct {
26 database *db.DB
27 sessionManager *session.SessionManager
28 oauthManager *oauth.OAuthServiceManager
29 spotifyService *spotify.SpotifyService
30 apiKeyService *apikeyService.Service
31 mbService *musicbrainz.MusicBrainzService
32 atprotoService *atproto.ATprotoAuthService
33 playingNowService *playingnow.PlayingNowService
34 pages *pages.Pages
35}
36
37// JSON API handlers
38
39func jsonResponse(w http.ResponseWriter, statusCode int, data any) {
40 w.Header().Set("Content-Type", "application/json")
41 w.WriteHeader(statusCode)
42 if data != nil {
43 json.NewEncoder(w).Encode(data)
44 }
45}
46
47func main() {
48 config.Load()
49
50 database, err := db.New(viper.GetString("db.path"))
51 if err != nil {
52 log.Fatalf("Error connecting to database: %v", err)
53 }
54
55 if err := database.Initialize(); err != nil {
56 log.Fatalf("Error initializing database: %v", err)
57 }
58
59 sessionManager := session.NewSessionManager(database)
60
61 // --- Service Initializations ---
62
63 var newJwkPrivateKey = viper.GetString("atproto.client_secret_key")
64 if newJwkPrivateKey == "" {
65 fmt.Printf("You now have to set the ATPROTO_CLIENT_SECRET_KEY env var to a private key. This can be done via goat key generate -t P-256")
66 return
67 }
68 var clientSecretKeyId = viper.GetString("atproto.client_secret_key_id")
69 if clientSecretKeyId == "" {
70 fmt.Printf("You also now have to set the ATPROTO_CLIENT_SECRET_KEY_ID env var to a key ID. This needs to be persistent and unique. Here's one for you: %d", time.Now().Unix())
71 return
72 }
73
74 atprotoService, err := atproto.NewATprotoAuthService(
75 database,
76 sessionManager,
77 newJwkPrivateKey,
78 viper.GetString("atproto.client_id"),
79 viper.GetString("atproto.callback_url"),
80 clientSecretKeyId,
81 )
82 if err != nil {
83 log.Fatalf("Error creating ATproto auth service: %v", err)
84 }
85
86 mbService := musicbrainz.NewMusicBrainzService(database)
87 playingNowService := playingnow.NewPlayingNowService(database, atprotoService)
88 spotifyService := spotify.NewSpotifyService(database, atprotoService, mbService, playingNowService)
89 lastfmService := lastfm.NewLastFMService(database, viper.GetString("lastfm.api_key"), mbService, atprotoService, playingNowService)
90
91 oauthManager := oauth.NewOAuthServiceManager()
92
93 spotifyOAuth := oauth.NewOAuth2Service(
94 viper.GetString("spotify.client_id"),
95 viper.GetString("spotify.client_secret"),
96 viper.GetString("callback.spotify"),
97 viper.GetStringSlice("spotify.scopes"),
98 "spotify",
99 spotifyService,
100 )
101 oauthManager.RegisterService("spotify", spotifyOAuth)
102 oauthManager.RegisterService("atproto", atprotoService)
103
104 apiKeyService := apikeyService.NewAPIKeyService(database, sessionManager)
105
106 app := &application{
107 database: database,
108 sessionManager: sessionManager,
109 oauthManager: oauthManager,
110 apiKeyService: apiKeyService,
111 mbService: mbService,
112 spotifyService: spotifyService,
113 atprotoService: atprotoService,
114 playingNowService: playingNowService,
115 pages: pages.NewPages(),
116 }
117
118 trackerInterval := time.Duration(viper.GetInt("tracker.interval")) * time.Second
119 lastfmInterval := time.Duration(viper.GetInt("lastfm.interval_seconds")) * time.Second // Add config for Last.fm interval
120 if lastfmInterval <= 0 {
121 lastfmInterval = 30 * time.Second
122 }
123
124 go spotifyService.StartListeningTracker(trackerInterval)
125
126 go lastfmService.StartListeningTracker(lastfmInterval)
127
128 serverAddr := fmt.Sprintf("%s:%s", viper.GetString("server.host"), viper.GetString("server.port"))
129 server := &http.Server{
130 Addr: serverAddr,
131 Handler: app.routes(),
132 IdleTimeout: time.Minute,
133 ReadTimeout: 5 * time.Second,
134 WriteTimeout: 10 * time.Second,
135 }
136 fmt.Printf("Server running at: http://%s\n", serverAddr)
137 log.Fatal(server.ListenAndServe())
138}