From 494b29def329ce0ebadc18653cec829a6c29d0b3 Mon Sep 17 00:00:00 2001 From: ari melody Date: Sat, 3 Aug 2024 15:02:01 +0100 Subject: [PATCH] that's all the API create routes! + some admin UI Signed-off-by: ari melody --- admin/static/admin.css | 27 ++++++++++++- api/api.go | 20 +++++++++- api/artist.go | 6 +-- api/{credit.go => musiccredit.go} | 6 +-- api/musiclink.go | 66 +++++++++++++++++++++++++++++++ api/release.go | 60 +++++++++++++++++++++++++++- api/track.go | 2 +- music/controller/artist.go | 4 +- music/controller/release.go | 2 +- music/controller/track.go | 4 +- views/admin/index.html | 15 +++++-- 11 files changed, 193 insertions(+), 19 deletions(-) rename api/{credit.go => musiccredit.go} (90%) create mode 100644 api/musiclink.go diff --git a/admin/static/admin.css b/admin/static/admin.css index db0a020..7024a9c 100644 --- a/admin/static/admin.css +++ b/admin/static/admin.css @@ -3,7 +3,7 @@ body { width: 100%; - height: 100vh; + height: calc(100vh - 1em); margin: 0; padding: 0; @@ -76,6 +76,31 @@ a:hover { margin: 0; } +.card-title { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; +} + +.create-btn { + background: #c4ff6a; + padding: .5em .8em; + border-radius: .5em; + border: 1px solid #84b141; + text-decoration: none; +} +.create-btn:hover { + background: #fff; + border-color: #d0d0d0; + text-decoration: inherit; +} +.create-btn:active { + background: #d0d0d0; + border-color: #808080; + text-decoration: inherit; +} + .release { margin-bottom: 1em; padding: 1em; diff --git a/api/api.go b/api/api.go index add01fa..8dab6a4 100644 --- a/api/api.go +++ b/api/api.go @@ -25,7 +25,19 @@ func Handler() http.Handler { } })) - mux.Handle("/v1/music/", http.StripPrefix("/v1/music", music.ServeRelease())) + mux.Handle("/v1/music/", http.StripPrefix("/v1/music", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + music.ServeRelease().ServeHTTP(w, r) + return + case http.MethodDelete: + admin.MustAuthorise(DeleteRelease()).ServeHTTP(w, r) + return + default: + http.NotFound(w, r) + return + } + }))) mux.Handle("/v1/music", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: @@ -34,13 +46,17 @@ func Handler() http.Handler { case http.MethodPost: admin.MustAuthorise(CreateRelease()).ServeHTTP(w, r) return + case http.MethodDelete: + admin.MustAuthorise(DeleteRelease()).ServeHTTP(w, r) + return default: http.NotFound(w, r) return } })) - mux.Handle("/v1/musiccredit", CreateCredit()) + mux.Handle("/v1/musiccredit", CreateMusicCredit()) + mux.Handle("/v1/musiclink", CreateMusicLink()) mux.Handle("/v1/track", CreateTrack()) return mux diff --git a/api/artist.go b/api/artist.go index 14f329b..067f1f3 100644 --- a/api/artist.go +++ b/api/artist.go @@ -104,16 +104,16 @@ func CreateArtist() http.Handler { } if data.ID == "" { - http.Error(w, "Artist ID cannot be blank", http.StatusBadRequest) + http.Error(w, "Artist ID cannot be blank\n", http.StatusBadRequest) return } if data.Name == "" { - http.Error(w, "Artist name cannot be blank", http.StatusBadRequest) + http.Error(w, "Artist name cannot be blank\n", http.StatusBadRequest) return } if global.GetArtist(data.ID) != nil { - http.Error(w, fmt.Sprintf("Artist %s already exists", data.ID), http.StatusBadRequest) + http.Error(w, fmt.Sprintf("Artist %s already exists\n", data.ID), http.StatusBadRequest) return } diff --git a/api/credit.go b/api/musiccredit.go similarity index 90% rename from api/credit.go rename to api/musiccredit.go index f246ee3..af0c3b1 100644 --- a/api/credit.go +++ b/api/musiccredit.go @@ -10,7 +10,7 @@ import ( controller "arimelody.me/arimelody.me/music/controller" ) -func CreateCredit() http.Handler { +func CreateMusicCredit() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.NotFound(w, r) @@ -33,13 +33,13 @@ func CreateCredit() http.Handler { var release = global.GetRelease(data.Release) if release == nil { - http.Error(w, fmt.Sprintf("Release %s does not exist", data.Release), http.StatusBadRequest) + http.Error(w, fmt.Sprintf("Release %s does not exist\n", data.Release), http.StatusBadRequest) return } var artist = global.GetArtist(data.Artist) if artist == nil { - http.Error(w, fmt.Sprintf("Artist %s does not exist", data.Artist), http.StatusBadRequest) + http.Error(w, fmt.Sprintf("Artist %s does not exist\n", data.Artist), http.StatusBadRequest) return } diff --git a/api/musiclink.go b/api/musiclink.go new file mode 100644 index 0000000..95482ba --- /dev/null +++ b/api/musiclink.go @@ -0,0 +1,66 @@ +package api + +import ( + "encoding/json" + "fmt" + "net/http" + + "arimelody.me/arimelody.me/global" + "arimelody.me/arimelody.me/music/model" + controller "arimelody.me/arimelody.me/music/controller" +) + +func CreateMusicLink() http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.NotFound(w, r) + return + } + + type linkJSON struct { + Release string + Name string + URL string + } + + var data linkJSON + err := json.NewDecoder(r.Body).Decode(&data) + if err != nil { + http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) + return + } + + if data.Release == "" { + http.Error(w, "Release cannot be empty\n", http.StatusBadRequest) + return + } + if data.Name == "" { + http.Error(w, "Link name cannot be empty\n", http.StatusBadRequest) + return + } + + var release = global.GetRelease(data.Release) + if release == nil { + http.Error(w, fmt.Sprintf("Release %s does not exist\n", data.Release), http.StatusBadRequest) + return + } + + var link = model.Link{ + Name: data.Name, + URL: data.URL, + } + + err = controller.CreateLinkDB(global.DB, release.ID, &link) + if err != nil { + fmt.Printf("Failed to create link: %s\n", err) + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + return + } + + release.Links = append(release.Links, &link) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + err = json.NewEncoder(w).Encode(release.Links) + }) +} diff --git a/api/release.go b/api/release.go index cf29ce2..ea0c862 100644 --- a/api/release.go +++ b/api/release.go @@ -41,6 +41,7 @@ func CreateRelease() http.Handler { type PostReleaseBody struct { ID string `json:"id"` + Visible bool `json:"visible"` Title string `json:"title"` Description string `json:"description"` ReleaseType model.ReleaseType `json:"type"` @@ -57,13 +58,27 @@ func CreateRelease() http.Handler { return } + if data.ID == "" { + http.Error(w, "Release ID cannot be empty\n", http.StatusBadRequest) + return + } + if data.Title == "" { + http.Error(w, "Release title cannot be empty\n", http.StatusBadRequest) + return + } + if data.ReleaseDate.Unix() == 0 { + http.Error(w, "Release date cannot be empty or 0\n", http.StatusBadRequest) + return + } + if global.GetRelease(data.ID) != nil { - http.Error(w, fmt.Sprintf("Release %s already exists", data.ID), http.StatusBadRequest) + http.Error(w, fmt.Sprintf("Release %s already exists\n", data.ID), http.StatusBadRequest) return } var release = model.Release{ ID: data.ID, + Visible: data.Visible, Title: data.Title, Description: data.Description, ReleaseType: data.ReleaseType, @@ -88,5 +103,48 @@ func CreateRelease() http.Handler { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) err = json.NewEncoder(w).Encode(release) + if err != nil { + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + return + } + }) +} + +func DeleteRelease() http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodDelete { + http.NotFound(w, r) + return + } + + if r.URL.Path == "/" { + http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) + return + } + + var releaseID = r.URL.Path[1:] + var release = global.GetRelease(releaseID) + if release == nil { + http.Error(w, fmt.Sprintf("Release %s does not exist\n", releaseID), http.StatusBadRequest) + return + } + + err := controller.DeleteReleaseDB(global.DB, release) + if err != nil { + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + return + } + + global.Releases = func () []*model.Release { + var releases = []*model.Release{} + for _, r := range global.Releases { + if r.ID == releaseID { continue } + releases = append(releases, r) + } + return releases + }() + + w.WriteHeader(200) + w.Write([]byte(fmt.Sprintf("Release %s has been deleted\n", release.ID))) }) } diff --git a/api/track.go b/api/track.go index b311956..b9e8de5 100644 --- a/api/track.go +++ b/api/track.go @@ -25,7 +25,7 @@ func CreateTrack() http.Handler { } if track.Title == "" { - http.Error(w, "Track title cannot be empty", http.StatusBadRequest) + http.Error(w, "Track title cannot be empty\n", http.StatusBadRequest) return } diff --git a/music/controller/artist.go b/music/controller/artist.go index 887d1d7..433e784 100644 --- a/music/controller/artist.go +++ b/music/controller/artist.go @@ -51,11 +51,11 @@ func UpdateArtistDB(db *sqlx.DB, artist *model.Artist) error { return nil } -func DeleteArtistDB(db *sqlx.DB, artistID string) error { +func DeleteArtistDB(db *sqlx.DB, artist *model.Artist) error { _, err := db.Exec( "DELETE FROM artist "+ "WHERE id=$1", - artistID, + artist.ID, ) if err != nil { return err diff --git a/music/controller/release.go b/music/controller/release.go index 6de8aec..0a672cd 100644 --- a/music/controller/release.go +++ b/music/controller/release.go @@ -79,7 +79,7 @@ func UpdateReleaseDB(db *sqlx.DB, release *model.Release) error { return nil } -func DeleteReleaseDB(db *sqlx.DB, release model.Release) error { +func DeleteReleaseDB(db *sqlx.DB, release *model.Release) error { _, err := db.Exec( "DELETE FROM musicrelease "+ "WHERE id=$1", diff --git a/music/controller/track.go b/music/controller/track.go index c9d5685..a5f0163 100644 --- a/music/controller/track.go +++ b/music/controller/track.go @@ -55,11 +55,11 @@ func UpdateTrackDB(db *sqlx.DB, track *model.Track) error { return nil } -func DeleteTrackDB(db *sqlx.DB, trackID string) error { +func DeleteTrackDB(db *sqlx.DB, track *model.Track) error { _, err := db.Exec( "DELETE FROM musictrack "+ "WHERE id=$1", - trackID, + track.ID, ) if err != nil { return err diff --git a/views/admin/index.html b/views/admin/index.html index 47f37b1..a5bbef3 100644 --- a/views/admin/index.html +++ b/views/admin/index.html @@ -13,7 +13,10 @@
-

Releases

+
+

Releases

+ Create New +
{{range $Release := .Releases}}
@@ -36,7 +39,10 @@ {{end}}
-

Artists

+
+

Artists

+ Create New +
{{range $Artist := .Artists}}
@@ -49,7 +55,10 @@ {{end}}
-

Tracks

+
+

Tracks

+ Create New +
{{range $Track := .Tracks}}