package api import ( "encoding/json" "fmt" "io/fs" "net/http" "os" "path/filepath" "strings" "arimelody-web/global" db "arimelody-web/music/controller" music "arimelody-web/music/controller" "arimelody-web/music/model" ) func ServeAllArtists() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var artists = []*model.Artist{} artists, err := db.GetAllArtists(global.DB) if err != nil { fmt.Printf("FATAL: Failed to serve all artists: %s\n", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } w.Header().Add("Content-Type", "application/json") err = json.NewEncoder(w).Encode(artists) if err != nil { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } }) } func ServeArtist(artist *model.Artist) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { type ( creditJSON struct { Role string `json:"role"` Primary bool `json:"primary"` } artistJSON struct { *model.Artist Credits map[string]creditJSON `json:"credits"` } ) var dbCredits []*model.Credit dbCredits, err := db.GetArtistCredits(global.DB, artist.ID) if err != nil { fmt.Printf("FATAL: Failed to retrieve artist credits for %s: %s\n", artist.ID, err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } var credits = map[string]creditJSON{} for _, credit := range dbCredits { credits[credit.Release.ID] = creditJSON{ Role: credit.Role, Primary: credit.Primary, } } w.Header().Add("Content-Type", "application/json") err = json.NewEncoder(w).Encode(artistJSON{ Artist: artist, Credits: credits, }) if err != nil { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } }) } func CreateArtist() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var artist model.Artist err := json.NewDecoder(r.Body).Decode(&artist) if err != nil { http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return } if artist.ID == "" { http.Error(w, "Artist ID cannot be blank\n", http.StatusBadRequest) return } if artist.Name == "" { artist.Name = artist.ID } err = music.CreateArtist(global.DB, &artist) if err != nil { if strings.Contains(err.Error(), "duplicate key") { http.Error(w, fmt.Sprintf("Artist %s already exists\n", artist.ID), http.StatusBadRequest) return } fmt.Printf("FATAL: Failed to create artist %s: %s\n", artist.ID, err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } w.WriteHeader(http.StatusCreated) }) } func UpdateArtist(artist *model.Artist) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { err := json.NewDecoder(r.Body).Decode(&artist) if err != nil { fmt.Printf("FATAL: Failed to update artist: %s\n", err) http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return } if artist.Avatar == "" { artist.Avatar = "/img/default-avatar.png" } else { if strings.Contains(artist.Avatar, ";base64,") { var artworkDirectory = filepath.Join("uploads", "avatar") filename, err := HandleImageUpload(&artist.Avatar, artworkDirectory, artist.ID) // clean up files with this ID and different extensions err = filepath.Walk(artworkDirectory, func(path string, info fs.FileInfo, err error) error { if path == filepath.Join(artworkDirectory, filename) { return nil } withoutExt := strings.TrimSuffix(path, filepath.Ext(path)) if withoutExt != filepath.Join(artworkDirectory, artist.ID) { return nil } return os.Remove(path) }) if err != nil { fmt.Printf("WARN: Error while cleaning up avatar files: %s\n", err) } artist.Avatar = fmt.Sprintf("/uploads/avatar/%s", filename) } } err = music.UpdateArtist(global.DB, artist) if err != nil { if strings.Contains(err.Error(), "no rows") { http.NotFound(w, r) return } fmt.Printf("FATAL: Failed to update artist %s: %s\n", artist.ID, err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } }) } func DeleteArtist(artist *model.Artist) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { err := music.DeleteArtist(global.DB, artist.ID) if err != nil { if strings.Contains(err.Error(), "no rows") { http.NotFound(w, r) return } fmt.Printf("FATAL: Failed to delete artist %s: %s\n", artist.ID, err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } }) }