From 21912d4ec21bf30275bc0f1376d7281bdd27ed24 Mon Sep 17 00:00:00 2001 From: ari melody Date: Fri, 7 Nov 2025 17:48:06 +0000 Subject: [PATCH] refactor mux path routes legend has it that if you refactor your code enough times, one day you will finally be happy --- admin/account/accounthttp.go | 18 ++- admin/music/artisthttp.go | 9 -- admin/music/musichttp.go | 14 +- admin/music/releasehttp.go | 77 +++++----- admin/music/trackhttp.go | 9 -- admin/releasehttp.go | 256 ------------------------------- admin/static/artists.js | 2 +- admin/static/releases.js | 2 +- admin/static/tracks.js | 2 +- admin/templates/html/index.html | 6 +- admin/templates/html/layout.html | 4 +- admin/views/index.html | 26 ---- admin/views/layout.html | 59 ------- api/api.go | 18 +-- main.go | 24 ++- view/blog.go | 16 +- view/music.go | 29 ++-- 17 files changed, 102 insertions(+), 469 deletions(-) delete mode 100644 admin/releasehttp.go delete mode 100644 admin/views/index.html delete mode 100644 admin/views/layout.html diff --git a/admin/account/accounthttp.go b/admin/account/accounthttp.go index d1b0d49..a826a1b 100644 --- a/admin/account/accounthttp.go +++ b/admin/account/accounthttp.go @@ -17,18 +17,20 @@ import ( ) func Handler(app *model.AppState) http.Handler { - mux := http.NewServeMux() + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + mux := http.NewServeMux() - mux.Handle("/", accountIndexHandler(app)) + mux.Handle("/", accountIndexHandler(app)) - mux.Handle("/account/totp-setup", totpSetupHandler(app)) - mux.Handle("/account/totp-confirm", totpConfirmHandler(app)) - mux.Handle("/account/totp-delete", http.StripPrefix("/totp-delete", totpDeleteHandler(app))) + mux.Handle("/totp-setup", totpSetupHandler(app)) + mux.Handle("/totp-confirm", totpConfirmHandler(app)) + mux.Handle("/totp-delete", totpDeleteHandler(app)) - mux.Handle("/account/password", changePasswordHandler(app)) - mux.Handle("/account/delete", deleteAccountHandler(app)) + mux.Handle("/password", changePasswordHandler(app)) + mux.Handle("/delete", deleteAccountHandler(app)) - return mux + mux.ServeHTTP(w, r) + }) } func accountIndexHandler(app *model.AppState) http.Handler { diff --git a/admin/music/artisthttp.go b/admin/music/artisthttp.go index 822ee72..2a04b41 100644 --- a/admin/music/artisthttp.go +++ b/admin/music/artisthttp.go @@ -4,7 +4,6 @@ import ( "fmt" "net/http" "os" - "strings" "arimelody-web/admin/core" "arimelody-web/admin/templates" @@ -16,14 +15,6 @@ func serveArtists(app *model.AppState) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { session := r.Context().Value("session").(*model.Session) - slices := strings.Split(strings.TrimPrefix(r.URL.Path, "/artists")[1:], "/") - artistID := slices[0] - - if len(artistID) > 0 { - serveArtist(app, artistID).ServeHTTP(w, r) - return - } - artists, err := controller.GetAllArtists(app.DB) if err != nil { fmt.Fprintf(os.Stderr, "WARN: Failed to fetch artists: %s\n", err) diff --git a/admin/music/musichttp.go b/admin/music/musichttp.go index cc2ef93..607026e 100644 --- a/admin/music/musichttp.go +++ b/admin/music/musichttp.go @@ -9,9 +9,17 @@ func Handler(app *model.AppState) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { mux := http.NewServeMux() - mux.Handle("/releases/", serveReleases(app)) - mux.Handle("/artists/", serveArtists(app)) - mux.Handle("/tracks/", serveTracks(app)) + mux.Handle("/releases/", http.StripPrefix("/releases", serveReleases(app))) + + mux.HandleFunc("/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.HandleFunc("/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.ServeHTTP(w, r) }) diff --git a/admin/music/releasehttp.go b/admin/music/releasehttp.go index 978dacd..bc41602 100644 --- a/admin/music/releasehttp.go +++ b/admin/music/releasehttp.go @@ -13,22 +13,43 @@ import ( ) func serveReleases(app *model.AppState) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - session := r.Context().Value("session").(*model.Session) + mux := http.NewServeMux() - slices := strings.Split(strings.TrimPrefix(r.URL.Path, "/releases")[1:], "/") - releaseID := slices[0] - - var action string = "" - if len(slices) > 1 { - action = slices[1] - } - - if len(releaseID) > 0 { - serveRelease(app, releaseID, action).ServeHTTP(w, r) + 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) { + session := r.Context().Value("session").(*model.Session) + type ReleasesData struct { core.AdminPageData Releases []*model.Release @@ -54,9 +75,11 @@ func serveReleases(app *model.AppState) http.Handler { return } }) + + return mux } -func serveRelease(app *model.AppState, releaseID string, action string) http.Handler { +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) @@ -71,34 +94,6 @@ func serveRelease(app *model.AppState, releaseID string, action string) http.Han return } - if len(action) > 0 { - switch action { - case "editcredits": - serveEditCredits(release).ServeHTTP(w, r) - return - case "addcredit": - serveAddCredit(app, release).ServeHTTP(w, r) - return - case "newcredit": - serveNewCredit(app).ServeHTTP(w, r) - return - case "editlinks": - serveEditLinks(release).ServeHTTP(w, r) - return - case "edittracks": - serveEditTracks(release).ServeHTTP(w, r) - return - case "addtrack": - serveAddTrack(app, release).ServeHTTP(w, r) - return - case "newtrack": - serveNewTrack(app).ServeHTTP(w, r) - return - } - http.NotFound(w, r) - return - } - type ReleaseResponse struct { core.AdminPageData Release *model.Release diff --git a/admin/music/trackhttp.go b/admin/music/trackhttp.go index 80d9ba5..eb4d6a6 100644 --- a/admin/music/trackhttp.go +++ b/admin/music/trackhttp.go @@ -4,7 +4,6 @@ import ( "fmt" "net/http" "os" - "strings" "arimelody-web/admin/core" "arimelody-web/admin/templates" @@ -16,14 +15,6 @@ func serveTracks(app *model.AppState) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { session := r.Context().Value("session").(*model.Session) - slices := strings.Split(strings.TrimPrefix(r.URL.Path, "/tracks")[1:], "/") - trackID := slices[0] - - if len(trackID) > 0 { - serveTrack(app, trackID).ServeHTTP(w, r) - return - } - tracks, err := controller.GetAllTracks(app.DB) if err != nil { fmt.Fprintf(os.Stderr, "WARN: Failed to fetch tracks: %s\n", err) diff --git a/admin/releasehttp.go b/admin/releasehttp.go deleted file mode 100644 index b0d6d3c..0000000 --- a/admin/releasehttp.go +++ /dev/null @@ -1,256 +0,0 @@ -package admin - -import ( - "fmt" - "net/http" - "os" - "strings" - - "arimelody-web/admin/core" - "arimelody-web/admin/templates" - "arimelody-web/controller" - "arimelody-web/model" -) - -func serveReleases(app *model.AppState) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - session := r.Context().Value("session").(*model.Session) - - slices := strings.Split(strings.TrimPrefix(r.URL.Path, "/releases")[1:], "/") - releaseID := slices[0] - - var action string = "" - if len(slices) > 1 { - action = slices[1] - } - - if len(releaseID) > 0 { - serveRelease(app, releaseID, action).ServeHTTP(w, r) - return - } - - type ReleasesData struct { - core.AdminPageData - Releases []*model.Release - } - - releases, err := controller.GetAllReleases(app.DB, false, 0, true) - if err != nil { - fmt.Fprintf(os.Stderr, "WARN: Failed to fetch releases: %s\n", err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - - err = templates.ReleasesTemplate.Execute(w, ReleasesData{ - AdminPageData: core.AdminPageData{ - Path: r.URL.Path, - Session: session, - }, - Releases: releases, - }) - if err != nil { - fmt.Fprintf(os.Stderr, "WARN: Failed to serve releases page: %s\n", err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - }) -} - -func serveRelease(app *model.AppState, releaseID string, action 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.Printf("WARN: Failed to fetch full release data for %s: %s\n", releaseID, err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - - if len(action) > 0 { - switch action { - case "editcredits": - serveEditCredits(release).ServeHTTP(w, r) - return - case "addcredit": - serveAddCredit(app, release).ServeHTTP(w, r) - return - case "newcredit": - serveNewCredit(app).ServeHTTP(w, r) - return - case "editlinks": - serveEditLinks(release).ServeHTTP(w, r) - return - case "edittracks": - serveEditTracks(release).ServeHTTP(w, r) - return - case "addtrack": - serveAddTrack(app, release).ServeHTTP(w, r) - return - case "newtrack": - serveNewTrack(app).ServeHTTP(w, r) - return - } - http.NotFound(w, r) - return - } - - type ReleaseResponse struct { - core.AdminPageData - Release *model.Release - } - - for i, track := range release.Tracks { track.Number = i + 1 } - - err = templates.EditReleaseTemplate.Execute(w, ReleaseResponse{ - AdminPageData: core.AdminPageData{ Path: r.URL.Path, Session: session }, - Release: release, - }) - if err != nil { - fmt.Printf("WARN: Failed to serve admin release page for %s: %s\n", release.ID, err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - } - }) -} - -func serveEditCredits(release *model.Release) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "text/html") - err := templates.EditCreditsTemplate.Execute(w, release) - if err != nil { - fmt.Printf("WARN: Failed to serve edit credits component for %s: %s\n", release.ID, err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - } - }) -} - -func serveAddCredit(app *model.AppState, release *model.Release) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - artists, err := controller.GetArtistsNotOnRelease(app.DB, release.ID) - if err != nil { - fmt.Printf("WARN: Failed to fetch artists not on %s: %s\n", release.ID, err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - - type response struct { - ReleaseID string; - Artists []*model.Artist - } - - w.Header().Set("Content-Type", "text/html") - err = templates.AddCreditTemplate.Execute(w, response{ - ReleaseID: release.ID, - Artists: artists, - }) - if err != nil { - fmt.Printf("WARN: Failed to serve add credits component for %s: %s\n", release.ID, err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - } - }) -} - -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] - artist, err := controller.GetArtist(app.DB, artistID) - if err != nil { - fmt.Printf("WARN: Failed to fetch artist %s: %s\n", artistID, err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - if artist == nil { - http.NotFound(w, r) - return - } - - w.Header().Set("Content-Type", "text/html") - err = templates.NewCreditTemplate.Execute(w, artist) - if err != nil { - fmt.Printf("WARN: Failed to serve new credit component for %s: %s\n", artist.ID, err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - } - }) -} - -func serveEditLinks(release *model.Release) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "text/html") - err := templates.EditLinksTemplate.Execute(w, release) - if err != nil { - fmt.Printf("WARN: Failed to serve edit links component for %s: %s\n", release.ID, err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - } - }) -} - -func serveEditTracks(release *model.Release) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "text/html") - - type editTracksData struct { Release *model.Release } - - for i, track := range release.Tracks { track.Number = i + 1 } - - err := templates.EditTracksTemplate.Execute(w, editTracksData{ Release: release }) - if err != nil { - fmt.Printf("WARN: Failed to serve edit tracks component for %s: %s\n", release.ID, err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - } - }) -} - -func serveAddTrack(app *model.AppState, release *model.Release) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - tracks, err := controller.GetTracksNotOnRelease(app.DB, release.ID) - if err != nil { - fmt.Printf("WARN: Failed to fetch tracks not on %s: %s\n", release.ID, err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - - type response struct { - ReleaseID string; - Tracks []*model.Track - } - - w.Header().Set("Content-Type", "text/html") - err = templates.AddTrackTemplate.Execute(w, response{ - ReleaseID: release.ID, - Tracks: tracks, - }) - if err != nil { - fmt.Printf("WARN: Failed to add tracks component for %s: %s\n", release.ID, err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - } - }) -} - -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] - track, err := controller.GetTrack(app.DB, trackID) - if err != nil { - fmt.Printf("WARN: Failed to fetch track %s: %s\n", trackID, err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - if track == nil { - http.NotFound(w, r) - return - } - - w.Header().Set("Content-Type", "text/html") - err = templates.NewTrackTemplate.Execute(w, track) - if err != nil { - fmt.Printf("WARN: Failed to serve new track component for %s: %s\n", track.ID, err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - } - }) -} diff --git a/admin/static/artists.js b/admin/static/artists.js index e3a2d5a..3a0e326 100644 --- a/admin/static/artists.js +++ b/admin/static/artists.js @@ -18,7 +18,7 @@ document.addEventListener("readystatechange", () => { }).then(res => { res.text().then(text => { if (res.ok) { - location = "/admin/artists/" + id; + location = "/admin/music/artists/" + id; } else { alert(text); console.error(text); diff --git a/admin/static/releases.js b/admin/static/releases.js index af12429..a606fde 100644 --- a/admin/static/releases.js +++ b/admin/static/releases.js @@ -10,7 +10,7 @@ document.addEventListener('readystatechange', () => { headers: { "Content-Type": "application/json" }, body: JSON.stringify({id}) }).then(res => { - if (res.ok) location = "/admin/releases/" + id; + if (res.ok) location = "/admin/music/releases/" + id; else { res.text().then(err => { alert(err); diff --git a/admin/static/tracks.js b/admin/static/tracks.js index bef1152..3cf7867 100644 --- a/admin/static/tracks.js +++ b/admin/static/tracks.js @@ -11,7 +11,7 @@ if (newTrackBtn) newTrackBtn.addEventListener("click", event => { }).then(res => { res.text().then(text => { if (res.ok) { - location = "/admin/tracks/" + text; + location = "/admin/music/tracks/" + text; } else { alert(text); console.error(text); diff --git a/admin/templates/html/index.html b/admin/templates/html/index.html index 292c617..af08c09 100644 --- a/admin/templates/html/index.html +++ b/admin/templates/html/index.html @@ -13,7 +13,7 @@
-

Releases ({{.ReleaseCount}} total)

+

Releases ({{.ReleaseCount}} total)

Create New
{{if .Artists}} @@ -27,7 +27,7 @@
-

Artists ({{.ArtistCount}} total)

+

Artists ({{.ArtistCount}} total)

Create New
{{if .Artists}} @@ -43,7 +43,7 @@
-

Tracks ({{.TrackCount}} total)

+

Tracks ({{.TrackCount}} total)

Create New

"Orphaned" tracks that have not yet been bound to a release.

diff --git a/admin/templates/html/layout.html b/admin/templates/html/layout.html index a940321..1e052a5 100644 --- a/admin/templates/html/layout.html +++ b/admin/templates/html/layout.html @@ -52,8 +52,8 @@
{{if .Session.Account}} -