lots of post-DB cleanup

This commit is contained in:
ari melody 2024-09-02 00:15:23 +01:00
parent 965d6f5c3e
commit c9d950d2b2
Signed by: ari
GPG key ID: CF99829C92678188
23 changed files with 412 additions and 550 deletions

View file

@ -21,8 +21,12 @@ func Handler() http.Handler {
var artist model.Artist
err := global.DB.Get(&artist, "SELECT * FROM artist WHERE id=$1", artistID)
if err != nil {
if strings.Contains(err.Error(), "no rows") {
http.NotFound(w, r)
return
}
fmt.Printf("FATAL: Error while retrieving artist %s: %s\n", artistID, err)
http.NotFound(w, r)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
@ -60,8 +64,12 @@ func Handler() http.Handler {
var release model.Release
err := global.DB.Get(&release, "SELECT * FROM musicrelease WHERE id=$1", releaseID)
if err != nil {
if strings.Contains(err.Error(), "no rows") {
http.NotFound(w, r)
return
}
fmt.Printf("FATAL: Error while retrieving release %s: %s\n", releaseID, err)
http.NotFound(w, r)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
@ -99,8 +107,12 @@ func Handler() http.Handler {
var track model.Track
err := global.DB.Get(&track, "SELECT * FROM musictrack WHERE id=$1", trackID)
if err != nil {
if strings.Contains(err.Error(), "no rows") {
http.NotFound(w, r)
return
}
fmt.Printf("FATAL: Error while retrieving track %s: %s\n", trackID, err)
http.NotFound(w, r)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}

View file

@ -8,6 +8,7 @@ import (
"arimelody.me/arimelody.me/global"
"arimelody.me/arimelody.me/music/model"
db "arimelody.me/arimelody.me/music/controller"
)
type artistJSON struct {
@ -20,7 +21,7 @@ type artistJSON struct {
func ServeAllArtists() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var artists = []*model.Artist{}
err := global.DB.Select(&artists, "SELECT * FROM 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)
@ -39,7 +40,6 @@ func ServeArtist(artist model.Artist) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
type (
creditJSON struct {
Release string `json:"release"`
Role string `json:"role"`
Primary bool `json:"primary"`
}
@ -49,14 +49,22 @@ func ServeArtist(artist model.Artist) http.Handler {
}
)
var credits = map[string]creditJSON{}
err := global.DB.Select(&credits, "SELECT release,role,is_primary FROM musiccredit WHERE id=$1", artist.ID)
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,

View file

@ -12,31 +12,12 @@ import (
"arimelody.me/arimelody.me/admin"
"arimelody.me/arimelody.me/global"
music "arimelody.me/arimelody.me/music/controller"
"arimelody.me/arimelody.me/music/model"
)
type releaseBodyJSON struct {
ID string `json:"id"`
Visible *bool `json:"visible"`
Title *string `json:"title"`
Description *string `json:"description"`
ReleaseType *model.ReleaseType `json:"type"`
ReleaseDate *string `json:"releaseDate"`
Artwork *string `json:"artwork"`
Buyname *string `json:"buyname"`
Buylink *string `json:"buylink"`
}
func ServeCatalog() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
type catalogItem struct {
ID string `json:"id"`
Title string `json:"title"`
ReleaseType model.ReleaseType `json:"type"`
ReleaseDate time.Time `json:"releaseDate"`
Artwork string `json:"artwork"`
Buylink string `json:"buylink"`
}
releases := []*model.Release{}
err := global.DB.Select(&releases, "SELECT * FROM musicrelease ORDER BY release_date DESC")
@ -45,13 +26,13 @@ func ServeCatalog() http.Handler {
return
}
catalog := []catalogItem{}
catalog := []model.ReleaseShorthand{}
authorised := admin.GetSession(r) != nil
for _, release := range releases {
if !release.Visible && !authorised {
continue
}
catalog = append(catalog, catalogItem{
catalog = append(catalog, model.ReleaseShorthand{
ID: release.ID,
Title: release.Title,
ReleaseType: release.ReleaseType,
@ -77,77 +58,31 @@ func CreateRelease() http.Handler {
return
}
var data releaseBodyJSON
err := json.NewDecoder(r.Body).Decode(&data)
var release model.Release
err := json.NewDecoder(r.Body).Decode(&release)
if err != nil {
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
if data.ID == "" {
if release.ID == "" {
http.Error(w, "Release ID cannot be empty\n", http.StatusBadRequest)
return
}
title := data.ID
if data.Title != nil && *data.Title != "" {
title = *data.Title
}
description := ""
if data.Description != nil && *data.Description != "" { description = *data.Description }
if release.Title == "" { release.Title = release.ID }
if release.ReleaseType == "" { release.ReleaseType = model.Single }
releaseType := model.Single
if data.ReleaseType != nil && *data.ReleaseType != "" { releaseType = *data.ReleaseType }
releaseDate := time.Time{}
if data.ReleaseDate != nil && *data.ReleaseDate != "" {
releaseDate, err = time.Parse("2006-01-02T15:04", *data.ReleaseDate)
if err != nil {
http.Error(w, "Invalid release date", http.StatusBadRequest)
return
}
} else {
releaseDate = time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 0, 0, 0, 0, time.UTC)
if release.ReleaseDate != time.Unix(0, 0) {
release.ReleaseDate = time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 0, 0, 0, 0, time.UTC)
}
artwork := "/img/default-cover-art.png"
if data.Artwork != nil && *data.Artwork != "" { artwork = *data.Artwork }
if release.Artwork == "" { release.Artwork = "/img/default-cover-art.png" }
buyname := ""
if data.Buyname != nil && *data.Buyname != "" { buyname = *data.Buyname }
buylink := ""
if data.Buylink != nil && *data.Buylink != "" { buylink = *data.Buylink }
var release = model.Release{
ID: data.ID,
Visible: false,
Title: title,
Description: description,
ReleaseType: releaseType,
ReleaseDate: releaseDate,
Artwork: artwork,
Buyname: buyname,
Buylink: buylink,
}
_, err = global.DB.Exec(
"INSERT INTO musicrelease "+
"(id, visible, title, description, type, release_date, artwork, buyname, buylink) "+
"VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
release.ID,
release.Visible,
release.Title,
release.Description,
release.ReleaseType,
release.ReleaseDate.Format("2006-01-02 15:04:05"),
release.Artwork,
release.Buyname,
release.Buylink)
err = music.CreateRelease(global.DB, &release)
if err != nil {
if strings.Contains(err.Error(), "duplicate key") {
http.Error(w, fmt.Sprintf("Release %s already exists\n", data.ID), http.StatusBadRequest)
http.Error(w, fmt.Sprintf("Release %s already exists\n", release.ID), http.StatusBadRequest)
return
}
fmt.Printf("Failed to create release %s: %s\n", release.ID, err)
@ -173,14 +108,6 @@ func UpdateRelease(release model.Release) http.Handler {
}
segments := strings.Split(r.URL.Path[1:], "/")
var releaseID = segments[0]
var exists int
err := global.DB.Get(&exists, "SELECT count(*) FROM musicrelease WHERE id=$1", releaseID)
if err != nil {
fmt.Printf("Failed to update release: %s\n", err)
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
if len(segments) == 2 {
switch segments[1] {
@ -199,30 +126,19 @@ func UpdateRelease(release model.Release) http.Handler {
return
}
var data releaseBodyJSON
err = json.NewDecoder(r.Body).Decode(&data)
err := json.NewDecoder(r.Body).Decode(&release)
if err != nil {
fmt.Printf("WARN: Failed to update release %s: %s\n", release.ID, err)
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
if data.ID != "" { release.ID = data.ID }
if data.Visible != nil { release.Visible = *data.Visible }
if data.Title != nil { release.Title = *data.Title }
if data.Description != nil { release.Description = *data.Description }
if data.ReleaseType != nil { release.ReleaseType = *data.ReleaseType }
if data.ReleaseDate != nil {
newDate, err := time.Parse("2006-01-02T15:04", *data.ReleaseDate)
if err != nil {
http.Error(w, "Invalid release date", http.StatusBadRequest)
return
}
release.ReleaseDate = newDate
}
if data.Artwork != nil {
if strings.Contains(*data.Artwork, ";base64,") {
if release.Artwork == "" {
release.Artwork = "/img/default-cover-art.png"
} else {
if strings.Contains(release.Artwork, ";base64,") {
var artworkDirectory = filepath.Join("uploads", "musicart")
filename, err := HandleImageUpload(data.Artwork, artworkDirectory, data.ID)
filename, err := HandleImageUpload(&release.Artwork, artworkDirectory, release.ID)
// clean up files with this ID and different extensions
err = filepath.Walk(artworkDirectory, func(path string, info fs.FileInfo, err error) error {
@ -238,27 +154,10 @@ func UpdateRelease(release model.Release) http.Handler {
}
release.Artwork = fmt.Sprintf("/uploads/musicart/%s", filename)
} else {
release.Artwork = *data.Artwork
}
}
if data.Buyname != nil { release.Buyname = *data.Buyname }
if data.Buylink != nil { release.Buylink = *data.Buylink }
_, err = global.DB.Exec(
"UPDATE musicrelease SET "+
"visible=$2, title=$3, description=$4, type=$5, release_date=$6, artwork=$7, buyname=$8, buylink=$9 "+
"WHERE id=$1",
release.ID,
release.Visible,
release.Title,
release.Description,
release.ReleaseType,
release.ReleaseDate.Format("2006-01-02 15:04:05"),
release.Artwork,
release.Buyname,
release.Buylink)
err = music.UpdateRelease(global.DB, &release)
if err != nil {
fmt.Printf("FATAL: Failed to update release %s: %s\n", release.ID, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
@ -275,18 +174,7 @@ func UpdateReleaseTracks(release model.Release) http.Handler {
return
}
tx := global.DB.MustBegin()
tx.MustExec("DELETE FROM musicreleasetrack WHERE release=$1", release.ID)
for i, trackID := range trackIDs {
tx.MustExec(
"INSERT INTO musicreleasetrack "+
"(release, track, number) "+
"VALUES ($1, $2, $3)",
release.ID,
trackID,
i)
}
err = tx.Commit()
err = music.UpdateReleaseTracks(global.DB, &release, trackIDs)
if err != nil {
fmt.Printf("Failed to update tracks for %s: %s\n", release.ID, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
@ -308,45 +196,23 @@ func UpdateReleaseCredits(release model.Release) http.Handler {
return
}
// clear duplicates
type Credit struct {
Role string
Primary bool
}
var credits = map[string]Credit{}
var credits []model.Credit
for _, credit := range data {
credits[credit.Artist] = Credit{
credits = append(credits, model.Credit{
Artist: model.Artist{
ID: credit.Artist,
},
Role: credit.Role,
Primary: credit.Primary,
}
})
}
tx := global.DB.MustBegin()
tx.MustExec("DELETE FROM musiccredit WHERE release=$1", release.ID)
for artistID := range credits {
if credits[artistID].Role == "" {
http.Error(w, fmt.Sprintf("Artist role cannot be blank (%s)", artistID), http.StatusBadRequest)
return
}
var exists int
_ = global.DB.Get(&exists, "SELECT count(*) FROM artist WHERE id=$1", artistID)
if exists == 0 {
http.Error(w, fmt.Sprintf("Artist %s does not exist\n", artistID), http.StatusBadRequest)
return
}
tx.MustExec(
"INSERT INTO musiccredit "+
"(release, artist, role, is_primary) "+
"VALUES ($1, $2, $3, $4)",
release.ID,
artistID,
credits[artistID].Role,
credits[artistID].Primary)
}
err = tx.Commit()
err = music.UpdateReleaseCredits(global.DB, &release, credits)
if err != nil {
if strings.Contains(err.Error(), "duplicate key") {
http.Error(w, "Artists may only be credited once\n", http.StatusBadRequest)
return
}
fmt.Printf("Failed to update links for %s: %s\n", release.ID, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}

View file

@ -6,23 +6,39 @@ import (
"net/http"
"arimelody.me/arimelody.me/global"
music "arimelody.me/arimelody.me/music/controller"
"arimelody.me/arimelody.me/music/model"
)
type (
Track struct {
model.Track
Releases []model.ReleaseShorthand
}
)
func ServeAllTracks() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
type track struct {
type Track struct {
ID string `json:"id"`
Title string `json:"title"`
}
var tracks = []track{}
var tracks = []Track{}
err := global.DB.Select(&tracks, "SELECT id, title FROM musictrack")
var dbTracks = []*model.Track{}
dbTracks, err := music.GetAllTracks(global.DB)
if err != nil {
fmt.Printf("FATAL: Failed to pull tracks from DB: %s\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
for _, track := range dbTracks {
tracks = append(tracks, Track{
ID: track.ID,
Title: track.Title,
})
}
w.Header().Add("Content-Type", "application/json")
err = json.NewEncoder(w).Encode(tracks)
if err != nil {
@ -34,40 +50,16 @@ func ServeAllTracks() http.Handler {
func ServeTrack(track model.Track) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
ServeAllTracks().ServeHTTP(w, r)
return
}
var trackID = r.URL.Path[1:]
var track = model.Track{}
err := global.DB.Get(&track, "SELECT * from musictrack WHERE id=$1", trackID)
if err != nil {
http.NotFound(w, r)
return
}
var releases = []*model.Release{}
err = global.DB.Select(&releases,
"SELECT * FROM musicrelease JOIN musicreleasetrack AS mrt "+
"WHERE mrt.track=$1 "+
"ORDER BY release_date",
track.ID,
)
releases, err := music.GetTrackReleases(global.DB, track.ID)
if err != nil {
fmt.Printf("FATAL: Failed to pull track releases for %s from DB: %s\n", trackID, err)
fmt.Printf("FATAL: Failed to pull track releases for %s from DB: %s\n", track.ID, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
type response struct {
model.Track
Releases []*model.Release
}
w.Header().Add("Content-Type", "application/json")
err = json.NewEncoder(w).Encode(response{ track, releases })
err = json.NewEncoder(w).Encode(Track{ track, releases })
if err != nil {
fmt.Printf("FATAL: Failed to serve track %s: %s\n", trackID, err)
fmt.Printf("FATAL: Failed to serve track %s: %s\n", track.ID, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
})
@ -92,15 +84,7 @@ func CreateTrack() http.Handler {
return
}
var trackID string
err = global.DB.Get(&trackID,
"INSERT INTO musictrack (title, description, lyrics, preview_url) "+
"VALUES ($1, $2, $3, $4) "+
"RETURNING id",
track.Title,
track.Description,
track.Lyrics,
track.PreviewURL)
id, err := music.CreateTrack(global.DB, &track)
if err != nil {
fmt.Printf("FATAL: Failed to create track: %s\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
@ -109,7 +93,7 @@ func CreateTrack() http.Handler {
w.Header().Add("Content-Type", "text/plain")
w.WriteHeader(http.StatusCreated)
w.Write([]byte(trackID))
w.Write([]byte(id))
})
}
@ -120,35 +104,18 @@ func UpdateTrack(track model.Track) http.Handler {
return
}
var update model.Track
err := json.NewDecoder(r.Body).Decode(&update)
err := json.NewDecoder(r.Body).Decode(&track)
if err != nil {
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
if update.Title == "" {
if track.Title == "" {
http.Error(w, "Track title cannot be empty\n", http.StatusBadRequest)
return
}
var trackID = r.URL.Path[1:]
var track = model.Track{}
err = global.DB.Get(&track, "SELECT * from musictrack WHERE id=$1", trackID)
if err != nil {
http.NotFound(w, r)
return
}
_, err = global.DB.Exec(
"UPDATE musictrack "+
"SET title=$2, description=$3, lyrics=$4, preview_url=$5 "+
"WHERE id=$1",
track.ID,
track.Title,
track.Description,
track.Lyrics,
track.PreviewURL)
err = music.UpdateTrack(global.DB, &track)
if err != nil {
fmt.Printf("Failed to update track %s: %s\n", track.ID, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
@ -171,10 +138,7 @@ func DeleteTrack(track model.Track) http.Handler {
}
var trackID = r.URL.Path[1:]
_, err := global.DB.Exec(
"DELETE FROM musictrack "+
"WHERE id=$1",
trackID)
err := music.DeleteTrack(global.DB, trackID)
if err != nil {
fmt.Printf("Failed to delete track %s: %s\n", trackID, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)