my god...it's finally done
This commit is contained in:
parent
2baf71214e
commit
19d76ebc47
43 changed files with 1008 additions and 550 deletions
19
api/api.go
19
api/api.go
|
@ -5,10 +5,10 @@ import (
|
|||
"net/http"
|
||||
"strings"
|
||||
|
||||
"arimelody.me/arimelody.me/admin"
|
||||
"arimelody.me/arimelody.me/global"
|
||||
"arimelody.me/arimelody.me/music/model"
|
||||
music "arimelody.me/arimelody.me/music/view"
|
||||
"arimelody-web/admin"
|
||||
"arimelody-web/global"
|
||||
music "arimelody-web/music/controller"
|
||||
musicView "arimelody-web/music/view"
|
||||
)
|
||||
|
||||
func Handler() http.Handler {
|
||||
|
@ -18,8 +18,7 @@ func Handler() http.Handler {
|
|||
|
||||
mux.Handle("/v1/artist/", http.StripPrefix("/v1/artist", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var artistID = strings.Split(r.URL.Path[1:], "/")[0]
|
||||
var artist model.Artist
|
||||
err := global.DB.Get(&artist, "SELECT * FROM artist WHERE id=$1", artistID)
|
||||
artist, err := music.GetArtist(global.DB, artistID)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "no rows") {
|
||||
http.NotFound(w, r)
|
||||
|
@ -61,8 +60,7 @@ func Handler() http.Handler {
|
|||
|
||||
mux.Handle("/v1/music/", http.StripPrefix("/v1/music", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var releaseID = strings.Split(r.URL.Path[1:], "/")[0]
|
||||
var release model.Release
|
||||
err := global.DB.Get(&release, "SELECT * FROM musicrelease WHERE id=$1", releaseID)
|
||||
release, err := music.GetRelease(global.DB, releaseID, true)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "no rows") {
|
||||
http.NotFound(w, r)
|
||||
|
@ -76,7 +74,7 @@ func Handler() http.Handler {
|
|||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
// GET /api/v1/music/{id}
|
||||
music.ServeRelease(release).ServeHTTP(w, r)
|
||||
musicView.ServeRelease(release).ServeHTTP(w, r)
|
||||
case http.MethodPut:
|
||||
// PUT /api/v1/music/{id} (admin)
|
||||
admin.MustAuthorise(UpdateRelease(release)).ServeHTTP(w, r)
|
||||
|
@ -104,8 +102,7 @@ func Handler() http.Handler {
|
|||
|
||||
mux.Handle("/v1/track/", http.StripPrefix("/v1/track", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var trackID = strings.Split(r.URL.Path[1:], "/")[0]
|
||||
var track model.Track
|
||||
err := global.DB.Get(&track, "SELECT * FROM musictrack WHERE id=$1", trackID)
|
||||
track, err := music.GetTrack(global.DB, trackID)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "no rows") {
|
||||
http.NotFound(w, r)
|
||||
|
|
105
api/artist.go
105
api/artist.go
|
@ -3,21 +3,18 @@ package api
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"arimelody.me/arimelody.me/global"
|
||||
"arimelody.me/arimelody.me/music/model"
|
||||
db "arimelody.me/arimelody.me/music/controller"
|
||||
"arimelody-web/global"
|
||||
db "arimelody-web/music/controller"
|
||||
music "arimelody-web/music/controller"
|
||||
"arimelody-web/music/model"
|
||||
)
|
||||
|
||||
type artistJSON struct {
|
||||
ID string `json:"id"`
|
||||
Name *string `json:"name"`
|
||||
Website *string `json:"website"`
|
||||
Avatar *string `json:"avatar"`
|
||||
}
|
||||
|
||||
func ServeAllArtists() http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var artists = []*model.Artist{}
|
||||
|
@ -36,7 +33,7 @@ func ServeAllArtists() http.Handler {
|
|||
})
|
||||
}
|
||||
|
||||
func ServeArtist(artist model.Artist) http.Handler {
|
||||
func ServeArtist(artist *model.Artist) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
type (
|
||||
creditJSON struct {
|
||||
|
@ -44,7 +41,7 @@ func ServeArtist(artist model.Artist) http.Handler {
|
|||
Primary bool `json:"primary"`
|
||||
}
|
||||
artistJSON struct {
|
||||
model.Artist
|
||||
*model.Artist
|
||||
Credits map[string]creditJSON `json:"credits"`
|
||||
}
|
||||
)
|
||||
|
@ -78,39 +75,23 @@ func ServeArtist(artist model.Artist) http.Handler {
|
|||
|
||||
func CreateArtist() http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var data artistJSON
|
||||
err := json.NewDecoder(r.Body).Decode(&data)
|
||||
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 data.ID == "" {
|
||||
if artist.ID == "" {
|
||||
http.Error(w, "Artist ID cannot be blank\n", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if data.Name == nil || *data.Name == "" {
|
||||
http.Error(w, "Artist name cannot be blank\n", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if artist.Name == "" { artist.Name = artist.ID }
|
||||
|
||||
var artist = model.Artist{
|
||||
ID: data.ID,
|
||||
Name: *data.Name,
|
||||
Website: *data.Website,
|
||||
Avatar: *data.Avatar,
|
||||
}
|
||||
|
||||
_, err = global.DB.Exec(
|
||||
"INSERT INTO artist (id, name, website, avatar) "+
|
||||
"VALUES ($1, $2, $3, $4)",
|
||||
artist.ID,
|
||||
artist.Name,
|
||||
artist.Website,
|
||||
artist.Avatar)
|
||||
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", data.ID), http.StatusBadRequest)
|
||||
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)
|
||||
|
@ -122,43 +103,59 @@ func CreateArtist() http.Handler {
|
|||
})
|
||||
}
|
||||
|
||||
func UpdateArtist(artist model.Artist) http.Handler {
|
||||
func UpdateArtist(artist *model.Artist) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var data artistJSON
|
||||
err := json.NewDecoder(r.Body).Decode(&data)
|
||||
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 data.ID != "" { artist.ID = data.ID }
|
||||
if data.Name != nil { artist.Name = *data.Name }
|
||||
if data.Website != nil { artist.Website = *data.Website }
|
||||
if data.Avatar != nil { artist.Avatar = *data.Avatar }
|
||||
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)
|
||||
|
||||
_, err = global.DB.Exec(
|
||||
"UPDATE artist "+
|
||||
"SET name=$2, website=$3, avatar=$4 "+
|
||||
"WHERE id=$1",
|
||||
artist.ID,
|
||||
artist.Name,
|
||||
artist.Website,
|
||||
artist.Avatar)
|
||||
// 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 {
|
||||
func DeleteArtist(artist *model.Artist) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_, err := global.DB.Exec(
|
||||
"DELETE FROM artist "+
|
||||
"WHERE id=$1",
|
||||
artist.ID)
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -10,35 +10,44 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"arimelody.me/arimelody.me/admin"
|
||||
"arimelody.me/arimelody.me/global"
|
||||
music "arimelody.me/arimelody.me/music/controller"
|
||||
"arimelody.me/arimelody.me/music/model"
|
||||
"arimelody-web/admin"
|
||||
"arimelody-web/global"
|
||||
music "arimelody-web/music/controller"
|
||||
"arimelody-web/music/model"
|
||||
)
|
||||
|
||||
func ServeCatalog() http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
releases := []*model.Release{}
|
||||
err := global.DB.Select(&releases, "SELECT * FROM musicrelease ORDER BY release_date DESC")
|
||||
releases, err := music.GetAllReleases(global.DB, false, 0, true)
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
catalog := []model.ReleaseShorthand{}
|
||||
type Release struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
ReleaseType model.ReleaseType `json:"type" db:"type"`
|
||||
ReleaseDate time.Time `json:"releaseDate" db:"release_date"`
|
||||
Artwork string `json:"artwork"`
|
||||
Buylink string `json:"buylink"`
|
||||
Copyright string `json:"copyright" db:"copyright"`
|
||||
}
|
||||
|
||||
catalog := []Release{}
|
||||
authorised := admin.GetSession(r) != nil
|
||||
for _, release := range releases {
|
||||
if !release.Visible && !authorised {
|
||||
continue
|
||||
}
|
||||
catalog = append(catalog, model.ReleaseShorthand{
|
||||
catalog = append(catalog, Release{
|
||||
ID: release.ID,
|
||||
Title: release.Title,
|
||||
ReleaseType: release.ReleaseType,
|
||||
ReleaseDate: release.ReleaseDate,
|
||||
Artwork: release.Artwork,
|
||||
Buylink: release.Buylink,
|
||||
Copyright: release.Copyright,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -85,7 +94,7 @@ func CreateRelease() http.Handler {
|
|||
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)
|
||||
fmt.Printf("FATAL: Failed to create release %s: %s\n", release.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
@ -100,7 +109,7 @@ func CreateRelease() http.Handler {
|
|||
})
|
||||
}
|
||||
|
||||
func UpdateRelease(release model.Release) http.Handler {
|
||||
func UpdateRelease(release *model.Release) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/" {
|
||||
http.NotFound(w, r)
|
||||
|
@ -157,15 +166,19 @@ func UpdateRelease(release model.Release) http.Handler {
|
|||
}
|
||||
}
|
||||
|
||||
err = music.UpdateRelease(global.DB, &release)
|
||||
err = music.UpdateRelease(global.DB, release)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "no rows") {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
fmt.Printf("FATAL: Failed to update release %s: %s\n", release.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func UpdateReleaseTracks(release model.Release) http.Handler {
|
||||
func UpdateReleaseTracks(release *model.Release) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var trackIDs = []string{}
|
||||
err := json.NewDecoder(r.Body).Decode(&trackIDs)
|
||||
|
@ -174,15 +187,19 @@ func UpdateReleaseTracks(release model.Release) http.Handler {
|
|||
return
|
||||
}
|
||||
|
||||
err = music.UpdateReleaseTracks(global.DB, &release, trackIDs)
|
||||
err = music.UpdateReleaseTracks(global.DB, release.ID, trackIDs)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to update tracks for %s: %s\n", release.ID, err)
|
||||
if strings.Contains(err.Error(), "no rows") {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
fmt.Printf("FATAL: Failed to update tracks for %s: %s\n", release.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func UpdateReleaseCredits(release model.Release) http.Handler {
|
||||
func UpdateReleaseCredits(release *model.Release) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
type creditJSON struct {
|
||||
Artist string
|
||||
|
@ -196,9 +213,9 @@ func UpdateReleaseCredits(release model.Release) http.Handler {
|
|||
return
|
||||
}
|
||||
|
||||
var credits []model.Credit
|
||||
var credits []*model.Credit
|
||||
for _, credit := range data {
|
||||
credits = append(credits, model.Credit{
|
||||
credits = append(credits, &model.Credit{
|
||||
Artist: model.Artist{
|
||||
ID: credit.Artist,
|
||||
},
|
||||
|
@ -207,19 +224,23 @@ func UpdateReleaseCredits(release model.Release) http.Handler {
|
|||
})
|
||||
}
|
||||
|
||||
err = music.UpdateReleaseCredits(global.DB, &release, credits)
|
||||
err = music.UpdateReleaseCredits(global.DB, release.ID, 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)
|
||||
if strings.Contains(err.Error(), "no rows") {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
fmt.Printf("FATAL: Failed to update links for %s: %s\n", release.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func UpdateReleaseLinks(release model.Release) http.Handler {
|
||||
func UpdateReleaseLinks(release *model.Release) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPut {
|
||||
http.NotFound(w, r)
|
||||
|
@ -233,30 +254,27 @@ func UpdateReleaseLinks(release model.Release) http.Handler {
|
|||
return
|
||||
}
|
||||
|
||||
tx := global.DB.MustBegin()
|
||||
tx.MustExec("DELETE FROM musiclink WHERE release=$1", release.ID)
|
||||
for _, link := range links {
|
||||
tx.MustExec(
|
||||
"INSERT INTO musiclink "+
|
||||
"(release, name, url) "+
|
||||
"VALUES ($1, $2, $3)",
|
||||
release.ID,
|
||||
link.Name,
|
||||
link.URL)
|
||||
}
|
||||
err = tx.Commit()
|
||||
err = music.UpdateReleaseLinks(global.DB, release.ID, links)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to update links for %s: %s\n", release.ID, err)
|
||||
if strings.Contains(err.Error(), "no rows") {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
fmt.Printf("FATAL: Failed to update links for %s: %s\n", release.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteRelease(release model.Release) http.Handler {
|
||||
func DeleteRelease(release *model.Release) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_, err := global.DB.Exec("DELETE FROM musicrelease WHERE id=$1", release.ID)
|
||||
err := music.DeleteRelease(global.DB, release.ID)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to delete release %s: %s\n", release.ID, err)
|
||||
if strings.Contains(err.Error(), "no rows") {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
fmt.Printf("FATAL: Failed to delete release %s: %s\n", release.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
}
|
||||
})
|
||||
|
|
25
api/track.go
25
api/track.go
|
@ -5,15 +5,15 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"arimelody.me/arimelody.me/global"
|
||||
music "arimelody.me/arimelody.me/music/controller"
|
||||
"arimelody.me/arimelody.me/music/model"
|
||||
"arimelody-web/global"
|
||||
music "arimelody-web/music/controller"
|
||||
"arimelody-web/music/model"
|
||||
)
|
||||
|
||||
type (
|
||||
Track struct {
|
||||
model.Track
|
||||
Releases []model.ReleaseShorthand
|
||||
*model.Track
|
||||
Releases []string `json:"releases"`
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -48,13 +48,18 @@ func ServeAllTracks() http.Handler {
|
|||
})
|
||||
}
|
||||
|
||||
func ServeTrack(track model.Track) http.Handler {
|
||||
func ServeTrack(track *model.Track) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
releases, err := music.GetTrackReleases(global.DB, track.ID)
|
||||
dbReleases, err := music.GetTrackReleases(global.DB, track.ID, false)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
releases := []string{}
|
||||
for _, release := range dbReleases {
|
||||
releases = append(releases, release.ID)
|
||||
}
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
err = json.NewEncoder(w).Encode(Track{ track, releases })
|
||||
|
@ -97,7 +102,7 @@ func CreateTrack() http.Handler {
|
|||
})
|
||||
}
|
||||
|
||||
func UpdateTrack(track model.Track) http.Handler {
|
||||
func UpdateTrack(track *model.Track) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPut || r.URL.Path == "/" {
|
||||
http.NotFound(w, r)
|
||||
|
@ -115,7 +120,7 @@ func UpdateTrack(track model.Track) http.Handler {
|
|||
return
|
||||
}
|
||||
|
||||
err = music.UpdateTrack(global.DB, &track)
|
||||
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)
|
||||
|
@ -130,7 +135,7 @@ func UpdateTrack(track model.Track) http.Handler {
|
|||
})
|
||||
}
|
||||
|
||||
func DeleteTrack(track model.Track) http.Handler {
|
||||
func DeleteTrack(track *model.Track) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodDelete || r.URL.Path == "/" {
|
||||
http.NotFound(w, r)
|
||||
|
|
|
@ -35,6 +35,8 @@ func HandleImageUpload(data *string, directory string, filename string) (string,
|
|||
}
|
||||
defer file.Close()
|
||||
|
||||
// TODO: generate compressed versions of image (512x512?)
|
||||
|
||||
buffer := bufio.NewWriter(file)
|
||||
_, err = buffer.Write(imageData)
|
||||
if err != nil {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue