home to your local SPACEGIRL 💫 arimelody.space
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

that's all the API create routes! + some admin UI

Signed-off-by: ari melody <ari@arimelody.me>

+193 -19
+26 -1
admin/static/admin.css
··· 3 3 4 4 body { 5 5 width: 100%; 6 - height: 100vh; 6 + height: calc(100vh - 1em); 7 7 8 8 margin: 0; 9 9 padding: 0; ··· 74 74 .card h3, 75 75 .card p { 76 76 margin: 0; 77 + } 78 + 79 + .card-title { 80 + display: flex; 81 + flex-direction: row; 82 + align-items: center; 83 + justify-content: space-between; 84 + } 85 + 86 + .create-btn { 87 + background: #c4ff6a; 88 + padding: .5em .8em; 89 + border-radius: .5em; 90 + border: 1px solid #84b141; 91 + text-decoration: none; 92 + } 93 + .create-btn:hover { 94 + background: #fff; 95 + border-color: #d0d0d0; 96 + text-decoration: inherit; 97 + } 98 + .create-btn:active { 99 + background: #d0d0d0; 100 + border-color: #808080; 101 + text-decoration: inherit; 77 102 } 78 103 79 104 .release {
+18 -2
api/api.go
··· 25 25 } 26 26 })) 27 27 28 - mux.Handle("/v1/music/", http.StripPrefix("/v1/music", music.ServeRelease())) 28 + mux.Handle("/v1/music/", http.StripPrefix("/v1/music", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 29 + switch r.Method { 30 + case http.MethodGet: 31 + music.ServeRelease().ServeHTTP(w, r) 32 + return 33 + case http.MethodDelete: 34 + admin.MustAuthorise(DeleteRelease()).ServeHTTP(w, r) 35 + return 36 + default: 37 + http.NotFound(w, r) 38 + return 39 + } 40 + }))) 29 41 mux.Handle("/v1/music", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 30 42 switch r.Method { 31 43 case http.MethodGet: ··· 34 46 case http.MethodPost: 35 47 admin.MustAuthorise(CreateRelease()).ServeHTTP(w, r) 36 48 return 49 + case http.MethodDelete: 50 + admin.MustAuthorise(DeleteRelease()).ServeHTTP(w, r) 51 + return 37 52 default: 38 53 http.NotFound(w, r) 39 54 return 40 55 } 41 56 })) 42 57 43 - mux.Handle("/v1/musiccredit", CreateCredit()) 58 + mux.Handle("/v1/musiccredit", CreateMusicCredit()) 59 + mux.Handle("/v1/musiclink", CreateMusicLink()) 44 60 mux.Handle("/v1/track", CreateTrack()) 45 61 46 62 return mux
+3 -3
api/artist.go
··· 104 104 } 105 105 106 106 if data.ID == "" { 107 - http.Error(w, "Artist ID cannot be blank", http.StatusBadRequest) 107 + http.Error(w, "Artist ID cannot be blank\n", http.StatusBadRequest) 108 108 return 109 109 } 110 110 if data.Name == "" { 111 - http.Error(w, "Artist name cannot be blank", http.StatusBadRequest) 111 + http.Error(w, "Artist name cannot be blank\n", http.StatusBadRequest) 112 112 return 113 113 } 114 114 115 115 if global.GetArtist(data.ID) != nil { 116 - http.Error(w, fmt.Sprintf("Artist %s already exists", data.ID), http.StatusBadRequest) 116 + http.Error(w, fmt.Sprintf("Artist %s already exists\n", data.ID), http.StatusBadRequest) 117 117 return 118 118 } 119 119
+3 -3
api/credit.go api/musiccredit.go
··· 10 10 controller "arimelody.me/arimelody.me/music/controller" 11 11 ) 12 12 13 - func CreateCredit() http.Handler { 13 + func CreateMusicCredit() http.Handler { 14 14 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 15 15 if r.Method != http.MethodPost { 16 16 http.NotFound(w, r) ··· 33 33 34 34 var release = global.GetRelease(data.Release) 35 35 if release == nil { 36 - http.Error(w, fmt.Sprintf("Release %s does not exist", data.Release), http.StatusBadRequest) 36 + http.Error(w, fmt.Sprintf("Release %s does not exist\n", data.Release), http.StatusBadRequest) 37 37 return 38 38 } 39 39 40 40 var artist = global.GetArtist(data.Artist) 41 41 if artist == nil { 42 - http.Error(w, fmt.Sprintf("Artist %s does not exist", data.Artist), http.StatusBadRequest) 42 + http.Error(w, fmt.Sprintf("Artist %s does not exist\n", data.Artist), http.StatusBadRequest) 43 43 return 44 44 } 45 45
+66
api/musiclink.go
··· 1 + package api 2 + 3 + import ( 4 + "encoding/json" 5 + "fmt" 6 + "net/http" 7 + 8 + "arimelody.me/arimelody.me/global" 9 + "arimelody.me/arimelody.me/music/model" 10 + controller "arimelody.me/arimelody.me/music/controller" 11 + ) 12 + 13 + func CreateMusicLink() http.Handler { 14 + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 15 + if r.Method != http.MethodPost { 16 + http.NotFound(w, r) 17 + return 18 + } 19 + 20 + type linkJSON struct { 21 + Release string 22 + Name string 23 + URL string 24 + } 25 + 26 + var data linkJSON 27 + err := json.NewDecoder(r.Body).Decode(&data) 28 + if err != nil { 29 + http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) 30 + return 31 + } 32 + 33 + if data.Release == "" { 34 + http.Error(w, "Release cannot be empty\n", http.StatusBadRequest) 35 + return 36 + } 37 + if data.Name == "" { 38 + http.Error(w, "Link name cannot be empty\n", http.StatusBadRequest) 39 + return 40 + } 41 + 42 + var release = global.GetRelease(data.Release) 43 + if release == nil { 44 + http.Error(w, fmt.Sprintf("Release %s does not exist\n", data.Release), http.StatusBadRequest) 45 + return 46 + } 47 + 48 + var link = model.Link{ 49 + Name: data.Name, 50 + URL: data.URL, 51 + } 52 + 53 + err = controller.CreateLinkDB(global.DB, release.ID, &link) 54 + if err != nil { 55 + fmt.Printf("Failed to create link: %s\n", err) 56 + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 57 + return 58 + } 59 + 60 + release.Links = append(release.Links, &link) 61 + 62 + w.Header().Add("Content-Type", "application/json") 63 + w.WriteHeader(http.StatusCreated) 64 + err = json.NewEncoder(w).Encode(release.Links) 65 + }) 66 + }
+59 -1
api/release.go
··· 41 41 42 42 type PostReleaseBody struct { 43 43 ID string `json:"id"` 44 + Visible bool `json:"visible"` 44 45 Title string `json:"title"` 45 46 Description string `json:"description"` 46 47 ReleaseType model.ReleaseType `json:"type"` ··· 57 58 return 58 59 } 59 60 61 + if data.ID == "" { 62 + http.Error(w, "Release ID cannot be empty\n", http.StatusBadRequest) 63 + return 64 + } 65 + if data.Title == "" { 66 + http.Error(w, "Release title cannot be empty\n", http.StatusBadRequest) 67 + return 68 + } 69 + if data.ReleaseDate.Unix() == 0 { 70 + http.Error(w, "Release date cannot be empty or 0\n", http.StatusBadRequest) 71 + return 72 + } 73 + 60 74 if global.GetRelease(data.ID) != nil { 61 - http.Error(w, fmt.Sprintf("Release %s already exists", data.ID), http.StatusBadRequest) 75 + http.Error(w, fmt.Sprintf("Release %s already exists\n", data.ID), http.StatusBadRequest) 62 76 return 63 77 } 64 78 65 79 var release = model.Release{ 66 80 ID: data.ID, 81 + Visible: data.Visible, 67 82 Title: data.Title, 68 83 Description: data.Description, 69 84 ReleaseType: data.ReleaseType, ··· 88 103 w.Header().Add("Content-Type", "application/json") 89 104 w.WriteHeader(http.StatusCreated) 90 105 err = json.NewEncoder(w).Encode(release) 106 + if err != nil { 107 + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 108 + return 109 + } 110 + }) 111 + } 112 + 113 + func DeleteRelease() http.Handler { 114 + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 115 + if r.Method != http.MethodDelete { 116 + http.NotFound(w, r) 117 + return 118 + } 119 + 120 + if r.URL.Path == "/" { 121 + http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) 122 + return 123 + } 124 + 125 + var releaseID = r.URL.Path[1:] 126 + var release = global.GetRelease(releaseID) 127 + if release == nil { 128 + http.Error(w, fmt.Sprintf("Release %s does not exist\n", releaseID), http.StatusBadRequest) 129 + return 130 + } 131 + 132 + err := controller.DeleteReleaseDB(global.DB, release) 133 + if err != nil { 134 + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 135 + return 136 + } 137 + 138 + global.Releases = func () []*model.Release { 139 + var releases = []*model.Release{} 140 + for _, r := range global.Releases { 141 + if r.ID == releaseID { continue } 142 + releases = append(releases, r) 143 + } 144 + return releases 145 + }() 146 + 147 + w.WriteHeader(200) 148 + w.Write([]byte(fmt.Sprintf("Release %s has been deleted\n", release.ID))) 91 149 }) 92 150 }
+1 -1
api/track.go
··· 25 25 } 26 26 27 27 if track.Title == "" { 28 - http.Error(w, "Track title cannot be empty", http.StatusBadRequest) 28 + http.Error(w, "Track title cannot be empty\n", http.StatusBadRequest) 29 29 return 30 30 } 31 31
+2 -2
music/controller/artist.go
··· 51 51 return nil 52 52 } 53 53 54 - func DeleteArtistDB(db *sqlx.DB, artistID string) error { 54 + func DeleteArtistDB(db *sqlx.DB, artist *model.Artist) error { 55 55 _, err := db.Exec( 56 56 "DELETE FROM artist "+ 57 57 "WHERE id=$1", 58 - artistID, 58 + artist.ID, 59 59 ) 60 60 if err != nil { 61 61 return err
+1 -1
music/controller/release.go
··· 79 79 return nil 80 80 } 81 81 82 - func DeleteReleaseDB(db *sqlx.DB, release model.Release) error { 82 + func DeleteReleaseDB(db *sqlx.DB, release *model.Release) error { 83 83 _, err := db.Exec( 84 84 "DELETE FROM musicrelease "+ 85 85 "WHERE id=$1",
+2 -2
music/controller/track.go
··· 55 55 return nil 56 56 } 57 57 58 - func DeleteTrackDB(db *sqlx.DB, trackID string) error { 58 + func DeleteTrackDB(db *sqlx.DB, track *model.Track) error { 59 59 _, err := db.Exec( 60 60 "DELETE FROM musictrack "+ 61 61 "WHERE id=$1", 62 - trackID, 62 + track.ID, 63 63 ) 64 64 if err != nil { 65 65 return err
+12 -3
views/admin/index.html
··· 13 13 14 14 <main> 15 15 16 - <h1>Releases</h1> 16 + <div class="card-title"> 17 + <h1>Releases</h1> 18 + <a href="/admin/createrelease" class="create-btn">Create New</a> 19 + </div> 17 20 <div class="card releases"> 18 21 {{range $Release := .Releases}} 19 22 <div class="release"> ··· 36 39 {{end}} 37 40 </div> 38 41 39 - <h1>Artists</h1> 42 + <div class="card-title"> 43 + <h1>Artists</h1> 44 + <a href="/admin/createartist" class="create-btn">Create New</a> 45 + </div> 40 46 <div class="card artists"> 41 47 {{range $Artist := .Artists}} 42 48 <div class="artist"> ··· 49 55 {{end}} 50 56 </div> 51 57 52 - <h1>Tracks</h1> 58 + <div class="card-title"> 59 + <h1>Tracks</h1> 60 + <a href="/admin/createtrack" class="create-btn">Create New</a> 61 + </div> 53 62 <div class="card tracks"> 54 63 {{range $Track := .Tracks}} 55 64 <div class="track">