diff --git a/admin/account/accounthttp.go b/admin/account/accounthttp.go index a826a1b..4595cd4 100644 --- a/admin/account/accounthttp.go +++ b/admin/account/accounthttp.go @@ -20,14 +20,14 @@ func Handler(app *model.AppState) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { mux := http.NewServeMux() - mux.Handle("/", accountIndexHandler(app)) + mux.Handle("/account/", accountIndexHandler(app)) - mux.Handle("/totp-setup", totpSetupHandler(app)) - mux.Handle("/totp-confirm", totpConfirmHandler(app)) - mux.Handle("/totp-delete", totpDeleteHandler(app)) + mux.Handle("/account/totp-setup", totpSetupHandler(app)) + mux.Handle("/account/totp-confirm", totpConfirmHandler(app)) + mux.Handle("/account/totp-delete", totpDeleteHandler(app)) - mux.Handle("/password", changePasswordHandler(app)) - mux.Handle("/delete", deleteAccountHandler(app)) + mux.Handle("/account/password", changePasswordHandler(app)) + mux.Handle("/account/delete", deleteAccountHandler(app)) mux.ServeHTTP(w, r) }) diff --git a/admin/blog/blog.go b/admin/blog/blog.go index d03907a..d1a2922 100644 --- a/admin/blog/blog.go +++ b/admin/blog/blog.go @@ -15,8 +15,8 @@ func Handler(app *model.AppState) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { mux := http.NewServeMux() - mux.Handle("/{id}", serveBlogPost(app)) - mux.Handle("/", serveBlogIndex(app)) + mux.Handle("/blogs/{id}", serveBlogPost(app)) + mux.Handle("/blogs/", serveBlogIndex(app)) mux.ServeHTTP(w, r) }) diff --git a/admin/http.go b/admin/http.go index 33f017b..e913558 100644 --- a/admin/http.go +++ b/admin/http.go @@ -26,10 +26,10 @@ func Handler(app *model.AppState) http.Handler { mux.Handle("/logout", core.RequireAccount(auth.LogoutHandler(app))) mux.Handle("/logs", core.RequireAccount(logs.Handler(app))) - mux.Handle("/music/", core.RequireAccount(http.StripPrefix("/music", music.Handler(app)))) - mux.Handle("/blogs/", core.RequireAccount(http.StripPrefix("/blogs", blog.Handler(app)))) + mux.Handle("/music/", core.RequireAccount(music.Handler(app))) + mux.Handle("/blogs/", core.RequireAccount(blog.Handler(app))) - mux.Handle("/account/", core.RequireAccount(http.StripPrefix("/account", account.Handler(app)))) + mux.Handle("/account/", core.RequireAccount(account.Handler(app))) mux.Handle("/static/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/static/admin.css" { diff --git a/admin/music/musichttp.go b/admin/music/musichttp.go index 607026e..562d83f 100644 --- a/admin/music/musichttp.go +++ b/admin/music/musichttp.go @@ -9,17 +9,23 @@ func Handler(app *model.AppState) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { mux := http.NewServeMux() - mux.Handle("/releases/", http.StripPrefix("/releases", serveReleases(app))) + mux.HandleFunc("/music/releases/{id}/", func(w http.ResponseWriter, r *http.Request) { + serveEditRelease(app, r.PathValue("id")).ServeHTTP(w, r) + }) + mux.HandleFunc("/music/releases/{id}", func(w http.ResponseWriter, r *http.Request) { + serveRelease(app, r.PathValue("id")).ServeHTTP(w, r) + }) + mux.Handle("/music/releases/", serveReleases(app)) - mux.HandleFunc("/artists/{id}", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/music/artists/{id}", func(w http.ResponseWriter, r *http.Request) { serveArtist(app, r.PathValue("id")).ServeHTTP(w, r) }) - mux.Handle("/artists/", http.StripPrefix("/artists", serveArtists(app))) + mux.Handle("/music/artists/", serveArtists(app)) - mux.HandleFunc("/tracks/{id}", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/music/tracks/{id}", func(w http.ResponseWriter, r *http.Request) { serveTrack(app, r.PathValue("id")).ServeHTTP(w, r) }) - mux.Handle("/tracks/", http.StripPrefix("/tracks", serveTracks(app))) + mux.Handle("/music/tracks/", serveTracks(app)) mux.ServeHTTP(w, r) }) diff --git a/admin/music/releasehttp.go b/admin/music/releasehttp.go index bc41602..741da76 100644 --- a/admin/music/releasehttp.go +++ b/admin/music/releasehttp.go @@ -4,7 +4,6 @@ import ( "fmt" "net/http" "os" - "strings" "arimelody-web/admin/core" "arimelody-web/admin/templates" @@ -13,41 +12,7 @@ import ( ) func serveReleases(app *model.AppState) http.Handler { - mux := http.NewServeMux() - - mux.HandleFunc("/{id}/", func(w http.ResponseWriter, r *http.Request) { - releaseID := r.PathValue("id") - release, err := controller.GetRelease(app.DB, releaseID, true) - if err != nil { - if strings.Contains(err.Error(), "no rows") { - http.NotFound(w, r) - return - } - fmt.Fprintf(os.Stderr, "WARN: Failed to fetch full release data for %s: %s\n", releaseID, err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - - mux := http.NewServeMux() - - mux.Handle("/{id}/editcredits", serveEditCredits(release)) - mux.Handle("/{id}/addcredit", serveAddCredit(app, release)) - mux.Handle("/{id}/newcredit", serveNewCredit(app)) - - mux.Handle("/{id}/editlinks", serveEditLinks(release)) - - mux.Handle("/{id}/edittracks", serveEditTracks(release)) - mux.Handle("/{id}/addtrack", serveAddTrack(app, release)) - mux.Handle("/{id}/newtrack", serveNewTrack(app)) - - mux.ServeHTTP(w, r) - }) - - mux.HandleFunc("/{id}", func(w http.ResponseWriter, r *http.Request) { - serveRelease(app, r.PathValue("id")).ServeHTTP(w, r) - }) - - mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { session := r.Context().Value("session").(*model.Session) type ReleasesData struct { @@ -75,24 +40,22 @@ func serveReleases(app *model.AppState) http.Handler { return } }) - - return mux } func serveRelease(app *model.AppState, releaseID string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - session := r.Context().Value("session").(*model.Session) - release, err := controller.GetRelease(app.DB, releaseID, true) if err != nil { - if strings.Contains(err.Error(), "no rows") { - http.NotFound(w, r) - return - } fmt.Fprintf(os.Stderr, "WARN: Failed to fetch full release data for %s: %s\n", releaseID, err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } + if release == nil { + http.NotFound(w, r) + return + } + + session := r.Context().Value("session").(*model.Session) type ReleaseResponse struct { core.AdminPageData @@ -112,6 +75,35 @@ func serveRelease(app *model.AppState, releaseID string) http.Handler { }) } +func serveEditRelease(app *model.AppState, releaseID string) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + release, err := controller.GetRelease(app.DB, releaseID, true) + if err != nil { + fmt.Fprintf(os.Stderr, "WARN: Failed to fetch full release data for %s: %s\n", releaseID, err) + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + return + } + if release == nil { + http.NotFound(w, r) + return + } + + mux := http.NewServeMux() + + mux.Handle("GET /music/releases/{id}/editcredits", serveEditCredits(release)) + mux.Handle("GET /music/releases/{id}/addcredit", serveAddCredit(app, release)) + mux.Handle("GET /music/releases/{id}/newcredit/{artistID}", serveNewCredit(app)) + + mux.Handle("GET /music/releases/{id}/editlinks", serveEditLinks(release)) + + mux.Handle("GET /music/releases/{id}/edittracks", serveEditTracks(release)) + mux.Handle("GET /music/releases/{id}/addtrack", serveAddTrack(app, release)) + mux.Handle("GET /music/releases/{id}/newtrack/{trackID}", serveNewTrack(app)) + + mux.ServeHTTP(w, r) + }) +} + func serveEditCredits(release *model.Release) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") @@ -151,8 +143,7 @@ func serveAddCredit(app *model.AppState, release *model.Release) http.Handler { func serveNewCredit(app *model.AppState) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - split := strings.Split(r.URL.Path, "/") - artistID := split[len(split) - 1] + artistID := r.PathValue("artistID") artist, err := controller.GetArtist(app.DB, artistID) if err != nil { fmt.Fprintf(os.Stderr, "WARN: Failed to fetch artist %s: %s\n", artistID, err) @@ -228,8 +219,7 @@ func serveAddTrack(app *model.AppState, release *model.Release) http.Handler { func serveNewTrack(app *model.AppState) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - split := strings.Split(r.URL.Path, "/") - trackID := split[len(split) - 1] + trackID := r.PathValue("trackID") track, err := controller.GetTrack(app.DB, trackID) if err != nil { fmt.Fprintf(os.Stderr, "WARN: Failed to fetch track %s: %s\n", trackID, err) diff --git a/admin/static/edit-blog.css b/admin/static/edit-blog.css index 5e86b4b..77cfc48 100644 --- a/admin/static/edit-blog.css +++ b/admin/static/edit-blog.css @@ -32,6 +32,11 @@ input[type="text"] { margin-top: 0; } +#blogpost button#set-current-date { + margin: 0 .5em; + padding: .4em .8em; +} + #blogpost h2 { margin: 0; font-size: 2em; diff --git a/admin/static/edit-blog.js b/admin/static/edit-blog.js index 9047ee9..2c5911f 100644 --- a/admin/static/edit-blog.js +++ b/admin/static/edit-blog.js @@ -1,6 +1,7 @@ const blogID = document.getElementById("blogpost").dataset.id; const titleInput = document.getElementById("title"); const publishDateInput = document.getElementById("publish-date"); +const setCurrentDateBtn = document.getElementById("set-current-date"); const descInput = document.getElementById("description"); const mdInput = document.getElementById("markdown"); const blueskyActorInput = document.getElementById("bluesky-actor"); @@ -11,6 +12,13 @@ const visInput = document.getElementById("visibility"); const saveBtn = document.getElementById("save"); const deleteBtn = document.getElementById("delete"); +setCurrentDateBtn.addEventListener("click", () => { + let now = new Date; + now.setMinutes(now.getMinutes() - now.getTimezoneOffset()); + publishDateInput.value = now.toISOString().slice(0, 16); + saveBtn.disabled = false; +}); + saveBtn.addEventListener("click", () => { fetch("/api/v1/blog/" + blogID, { method: "PUT", diff --git a/admin/static/edit-release.css b/admin/static/edit-release.css index 8186f2c..850c1d7 100644 --- a/admin/static/edit-release.css +++ b/admin/static/edit-release.css @@ -465,7 +465,7 @@ dialog div.dialog-actions { } #edittracks .track { - background-color: var(--bg-2); + background-color: var(--bg-1); transition: transform .2s ease-out, opacity .2s; } @@ -488,7 +488,7 @@ dialog div.dialog-actions { } #edittracks .track:nth-child(even) { - background-color: var(--bg-1); + background-color: var(--bg-0); } #edittracks .track-number { @@ -510,17 +510,17 @@ dialog div.dialog-actions { padding: .5em; display: flex; gap: .5em; - background-color: var(--bg-0); + background-color: var(--bg-1); cursor: pointer; transition: background-color .1s ease-out, color .1s ease-out; } #addtrack ul li.new-track:nth-child(even) { - background: color-mix(in srgb, var(--bg-0) 95%, #fff); + background-color: var(--bg-0); } #addtrack ul li.new-track:hover { - background: color-mix(in srgb, var(--bg-0) 90%, #fff); + background-color: var(--bg-2); } @media only screen and (max-width: 1105px) { diff --git a/admin/templates/html/edit-blog.html b/admin/templates/html/edit-blog.html index eba577d..4176e85 100644 --- a/admin/templates/html/edit-blog.html +++ b/admin/templates/html/edit-blog.html @@ -28,6 +28,7 @@ +