package api import ( "encoding/json" "fmt" "io/fs" "net/http" "os" "path/filepath" "strings" "time" "arimelody-web/admin" "arimelody-web/global" "arimelody-web/controller" "arimelody-web/model" ) func ServeAllArtists() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var artists = []*model.Artist{} artists, err := controller.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 { ID string `json:"id"` Title string `json:"title"` ReleaseDate time.Time `json:"releaseDate" db:"release_date"` Artwork string `json:"artwork"` Role string `json:"role"` Primary bool `json:"primary"` } artistJSON struct { *model.Artist Credits map[string]creditJSON `json:"credits"` } ) show_hidden_releases := admin.GetSession(r) != nil var dbCredits []*model.Credit dbCredits, err := controller.GetArtistCredits(global.DB, artist.ID, show_hidden_releases) 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{ ID: credit.Release.ID, Title: credit.Release.Title, ReleaseDate: credit.Release.ReleaseDate, Artwork: credit.Release.Artwork, 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 = controller.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 = controller.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 := controller.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) } }) }