From d5f1fcb5e06df729a741584d693e981626c39022 Mon Sep 17 00:00:00 2001 From: ari melody Date: Mon, 20 Jan 2025 10:34:39 +0000 Subject: [PATCH] this is immensely broken but i swear i'll fix it later --- .dockerignore | 1 + .gitignore | 2 +- Dockerfile | 2 +- admin/admin.go | 8 +- admin/artisthttp.go | 8 +- admin/http.go | 44 ++++--- admin/releasehttp.go | 14 +- admin/templates.go | 5 + admin/trackhttp.go | 8 +- admin/views/create-account.html | 107 +++++++++++++++ admin/views/login.html | 72 ++++++++++- api/api.go | 11 +- api/artist.go | 15 +-- api/release.go | 114 ++++++++++++++-- api/track.go | 14 +- {music/controller => controller}/artist.go | 7 +- {music/controller => controller}/release.go | 4 +- {music/controller => controller}/track.go | 4 +- discord/discord.go | 18 +-- main.go | 4 +- {music/model => model}/artist.go | 0 {music/model => model}/credit.go | 0 {music/model => model}/link.go | 0 {music/model => model}/release.go | 0 {music/model => model}/track.go | 0 music/view/release.go | 136 -------------------- {music/view => view}/music.go | 38 +++++- views/music.html | 26 ++-- 28 files changed, 409 insertions(+), 253 deletions(-) create mode 100644 admin/views/create-account.html rename {music/controller => controller}/artist.go (94%) rename {music/controller => controller}/release.go (99%) rename {music/controller => controller}/track.go (98%) rename {music/model => model}/artist.go (100%) rename {music/model => model}/credit.go (100%) rename {music/model => model}/link.go (100%) rename {music/model => model}/release.go (100%) rename {music/model => model}/track.go (100%) delete mode 100644 music/view/release.go rename {music/view => view}/music.go (51%) diff --git a/.dockerignore b/.dockerignore index 0e34f0f..785d79e 100644 --- a/.dockerignore +++ b/.dockerignore @@ -6,6 +6,7 @@ uploads/* test/ tmp/ +res/ docker-compose.yml Dockerfile schema.sql diff --git a/.gitignore b/.gitignore index 108e0e4..781b36e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,5 @@ db/ tmp/ test/ -uploads/* +uploads/ docker-compose-test.yml diff --git a/Dockerfile b/Dockerfile index 278f01a..0e0d2a2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ RUN CGO_ENABLED=0 GOOS=linux go build -o /arimelody-web # --- -FROM build-stage AS build-release-stage +FROM scratch WORKDIR /app diff --git a/admin/admin.go b/admin/admin.go index 3c2aaae..f02274e 100644 --- a/admin/admin.go +++ b/admin/admin.go @@ -28,13 +28,7 @@ var ADMIN_BYPASS = func() bool { return false }() -var ADMIN_ID_DISCORD = func() string { - id := os.Getenv("DISCORD_ADMIN") - if id == "" { - fmt.Printf("WARN: Discord admin ID (DISCORD_ADMIN) was not provided. Admin login will be unavailable.\n") - } - return id -}() +var ADMIN_ID_DISCORD = os.Getenv("DISCORD_ADMIN") var sessions []*Session diff --git a/admin/artisthttp.go b/admin/artisthttp.go index 4f66798..ba26240 100644 --- a/admin/artisthttp.go +++ b/admin/artisthttp.go @@ -6,15 +6,15 @@ import ( "strings" "arimelody-web/global" - "arimelody-web/music/model" - "arimelody-web/music/controller" + "arimelody-web/model" + "arimelody-web/controller" ) func serveArtist() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { slices := strings.Split(r.URL.Path[1:], "/") id := slices[0] - artist, err := music.GetArtist(global.DB, id) + artist, err := controller.GetArtist(global.DB, id) if err != nil { if artist == nil { http.NotFound(w, r) @@ -25,7 +25,7 @@ func serveArtist() http.Handler { return } - credits, err := music.GetArtistCredits(global.DB, artist.ID, true) + credits, err := controller.GetArtistCredits(global.DB, artist.ID, true) if err != nil { fmt.Printf("Error rendering admin track page for %s: %s\n", id, err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) diff --git a/admin/http.go b/admin/http.go index 35ff3f6..df348db 100644 --- a/admin/http.go +++ b/admin/http.go @@ -11,8 +11,8 @@ import ( "arimelody-web/discord" "arimelody-web/global" - musicDB "arimelody-web/music/controller" - musicModel "arimelody-web/music/model" + "arimelody-web/controller" + "arimelody-web/model" ) type loginData struct { @@ -24,6 +24,7 @@ func Handler() http.Handler { mux := http.NewServeMux() mux.Handle("/login", LoginHandler()) + mux.Handle("/create-account", createAccountHandler()) mux.Handle("/logout", MustAuthorise(LogoutHandler())) mux.Handle("/static/", http.StripPrefix("/static", staticHandler())) mux.Handle("/release/", MustAuthorise(http.StripPrefix("/release", serveRelease()))) @@ -41,21 +42,21 @@ func Handler() http.Handler { return } - releases, err := musicDB.GetAllReleases(global.DB, false, 0, true) + releases, err := controller.GetAllReleases(global.DB, false, 0, true) if err != nil { fmt.Printf("FATAL: Failed to pull releases: %s\n", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } - artists, err := musicDB.GetAllArtists(global.DB) + artists, err := controller.GetAllArtists(global.DB) if err != nil { fmt.Printf("FATAL: Failed to pull artists: %s\n", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } - tracks, err := musicDB.GetOrphanTracks(global.DB) + tracks, err := controller.GetOrphanTracks(global.DB) if err != nil { fmt.Printf("FATAL: Failed to pull orphan tracks: %s\n", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) @@ -63,9 +64,9 @@ func Handler() http.Handler { } type IndexData struct { - Releases []*musicModel.Release - Artists []*musicModel.Artist - Tracks []*musicModel.Track + Releases []*model.Release + Artists []*model.Artist + Tracks []*model.Track } err = pages["index"].Execute(w, IndexData{ @@ -149,14 +150,14 @@ func GetSession(r *http.Request) *Session { func LoginHandler() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if !discord.CREDENTIALS_PROVIDED || ADMIN_ID_DISCORD == "" { - http.Error(w, http.StatusText(http.StatusServiceUnavailable), http.StatusServiceUnavailable) - return - } - - fmt.Println(discord.CLIENT_ID) - fmt.Println(discord.API_ENDPOINT) - fmt.Println(discord.REDIRECT_URI) + // if !discord.CREDENTIALS_PROVIDED || ADMIN_ID_DISCORD == "" { + // http.Error(w, http.StatusText(http.StatusServiceUnavailable), http.StatusServiceUnavailable) + // return + // } + // + // fmt.Println(discord.CLIENT_ID) + // fmt.Println(discord.API_ENDPOINT) + // fmt.Println(discord.REDIRECT_URI) code := r.URL.Query().Get("code") @@ -239,6 +240,17 @@ func LogoutHandler() http.Handler { }) } +func createAccountHandler() http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + err := pages["create-account"].Execute(w, nil) + if err != nil { + fmt.Printf("Error rendering admin crearte account page: %s\n", err) + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + return + } + }) +} + func staticHandler() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { info, err := os.Stat(filepath.Join("admin", "static", filepath.Clean(r.URL.Path))) diff --git a/admin/releasehttp.go b/admin/releasehttp.go index bacaf01..54d0fd6 100644 --- a/admin/releasehttp.go +++ b/admin/releasehttp.go @@ -6,8 +6,8 @@ import ( "strings" "arimelody-web/global" - db "arimelody-web/music/controller" - "arimelody-web/music/model" + "arimelody-web/controller" + "arimelody-web/model" ) func serveRelease() http.Handler { @@ -15,7 +15,7 @@ func serveRelease() http.Handler { slices := strings.Split(r.URL.Path[1:], "/") releaseID := slices[0] - release, err := db.GetRelease(global.DB, releaseID, true) + release, err := controller.GetRelease(global.DB, releaseID, true) if err != nil { if strings.Contains(err.Error(), "no rows") { http.NotFound(w, r) @@ -81,7 +81,7 @@ func serveEditCredits(release *model.Release) http.Handler { func serveAddCredit(release *model.Release) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - artists, err := db.GetArtistsNotOnRelease(global.DB, release.ID) + artists, err := controller.GetArtistsNotOnRelease(global.DB, release.ID) if err != nil { fmt.Printf("FATAL: Failed to pull artists not on %s: %s\n", release.ID, err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) @@ -108,7 +108,7 @@ func serveAddCredit(release *model.Release) http.Handler { func serveNewCredit() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { artistID := strings.Split(r.URL.Path, "/")[3] - artist, err := db.GetArtist(global.DB, artistID) + artist, err := controller.GetArtist(global.DB, artistID) if err != nil { fmt.Printf("FATAL: Failed to pull artists %s: %s\n", artistID, err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) @@ -152,7 +152,7 @@ func serveEditTracks(release *model.Release) http.Handler { func serveAddTrack(release *model.Release) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - tracks, err := db.GetTracksNotOnRelease(global.DB, release.ID) + tracks, err := controller.GetTracksNotOnRelease(global.DB, release.ID) if err != nil { fmt.Printf("FATAL: Failed to pull tracks not on %s: %s\n", release.ID, err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) @@ -180,7 +180,7 @@ func serveAddTrack(release *model.Release) http.Handler { func serveNewTrack() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { trackID := strings.Split(r.URL.Path, "/")[3] - track, err := db.GetTrack(global.DB, trackID) + track, err := controller.GetTrack(global.DB, trackID) if err != nil { fmt.Printf("Error rendering new track component for %s: %s\n", trackID, err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) diff --git a/admin/templates.go b/admin/templates.go index b7aaf9e..e91313a 100644 --- a/admin/templates.go +++ b/admin/templates.go @@ -18,6 +18,11 @@ var pages = map[string]*template.Template{ filepath.Join("views", "prideflag.html"), filepath.Join("admin", "views", "login.html"), )), + "create-account": template.Must(template.ParseFiles( + filepath.Join("admin", "views", "layout.html"), + filepath.Join("views", "prideflag.html"), + filepath.Join("admin", "views", "create-account.html"), + )), "logout": template.Must(template.ParseFiles( filepath.Join("admin", "views", "layout.html"), filepath.Join("views", "prideflag.html"), diff --git a/admin/trackhttp.go b/admin/trackhttp.go index 732d34a..148b7d8 100644 --- a/admin/trackhttp.go +++ b/admin/trackhttp.go @@ -6,15 +6,15 @@ import ( "strings" "arimelody-web/global" - "arimelody-web/music/model" - "arimelody-web/music/controller" + "arimelody-web/model" + "arimelody-web/controller" ) func serveTrack() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { slices := strings.Split(r.URL.Path[1:], "/") id := slices[0] - track, err := music.GetTrack(global.DB, id) + track, err := controller.GetTrack(global.DB, id) if err != nil { fmt.Printf("Error rendering admin track page for %s: %s\n", id, err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) @@ -25,7 +25,7 @@ func serveTrack() http.Handler { return } - releases, err := music.GetTrackReleases(global.DB, track.ID, true) + releases, err := controller.GetTrackReleases(global.DB, track.ID, true) if err != nil { fmt.Printf("FATAL: Failed to pull releases for %s: %s\n", id, err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) diff --git a/admin/views/create-account.html b/admin/views/create-account.html new file mode 100644 index 0000000..1976d27 --- /dev/null +++ b/admin/views/create-account.html @@ -0,0 +1,107 @@ +{{define "head"}} +Register - ari melody 💫 + + + +{{end}} + +{{define "content"}} +
+ {{if .Success}} + + +

+ {{.Message}} + You should be redirected to /admin in 5 seconds. +

+ + {{else}} + + {{if .Message}} +

{{.Message}}

+ {{end}} + +
+
+ + + + + + + + + + + +
+ + +
+ + {{end}} +
+{{end}} diff --git a/admin/views/login.html b/admin/views/login.html index f615746..2c3e9bf 100644 --- a/admin/views/login.html +++ b/admin/views/login.html @@ -10,6 +10,61 @@ p a { a.discord { color: #5865F2; } + +form { + width: 100%; + display: flex; + flex-direction: column; + align-items: center; +} + +form div { + width: 20rem; +} + +form button { + margin-top: 1rem; +} + +label { + width: 100%; + margin: 1rem 0 .5rem 0; + display: block; + color: #10101080; +} +input { + width: 100%; + margin: .5rem 0; + padding: .3rem .5rem; + display: block; + border-radius: 4px; + border: 1px solid #808080; + font-size: inherit; + font-family: inherit; + color: inherit; +} + +button { + padding: .5em .8em; + font-family: inherit; + font-size: inherit; + border-radius: .5em; + border: 1px solid #a0a0a0; + background: #f0f0f0; + color: inherit; +} +button.save { + background: #6fd7ff; + border-color: #6f9eb0; +} +button:hover { + background: #fff; + border-color: #d0d0d0; +} +button:active { + background: #d0d0d0; + border-color: #808080; +} {{end}} @@ -25,7 +80,22 @@ a.discord { {{else}} -

Log in with Discord.

+ + +
+
+ + + + + + + + +
+ + +
{{end}} diff --git a/api/api.go b/api/api.go index 699cf1c..4757a42 100644 --- a/api/api.go +++ b/api/api.go @@ -7,8 +7,7 @@ import ( "arimelody-web/admin" "arimelody-web/global" - music "arimelody-web/music/controller" - musicView "arimelody-web/music/view" + "arimelody-web/controller" ) func Handler() http.Handler { @@ -18,7 +17,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] - artist, err := music.GetArtist(global.DB, artistID) + artist, err := controller.GetArtist(global.DB, artistID) if err != nil { if strings.Contains(err.Error(), "no rows") { http.NotFound(w, r) @@ -60,7 +59,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] - release, err := music.GetRelease(global.DB, releaseID, true) + release, err := controller.GetRelease(global.DB, releaseID, true) if err != nil { if strings.Contains(err.Error(), "no rows") { http.NotFound(w, r) @@ -74,7 +73,7 @@ func Handler() http.Handler { switch r.Method { case http.MethodGet: // GET /api/v1/music/{id} - musicView.ServeRelease(release).ServeHTTP(w, r) + ServeRelease(release).ServeHTTP(w, r) case http.MethodPut: // PUT /api/v1/music/{id} (admin) admin.MustAuthorise(UpdateRelease(release)).ServeHTTP(w, r) @@ -102,7 +101,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] - track, err := music.GetTrack(global.DB, trackID) + track, err := controller.GetTrack(global.DB, trackID) if err != nil { if strings.Contains(err.Error(), "no rows") { http.NotFound(w, r) diff --git a/api/artist.go b/api/artist.go index 90bd18a..9c88bc1 100644 --- a/api/artist.go +++ b/api/artist.go @@ -12,15 +12,14 @@ import ( "arimelody-web/admin" "arimelody-web/global" - db "arimelody-web/music/controller" - music "arimelody-web/music/controller" - "arimelody-web/music/model" + "arimelody-web/controller" + "arimelody-web/model" ) func ServeAllArtists() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var artists = []*model.Artist{} - artists, err := db.GetAllArtists(global.DB) + artists, err := controller.GetAllArtists(global.DB) if err != nil { fmt.Printf("FATAL: Failed to serve all artists: %s\n", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) @@ -55,7 +54,7 @@ func ServeArtist(artist *model.Artist) http.Handler { show_hidden_releases := admin.GetSession(r) != nil var dbCredits []*model.Credit - dbCredits, err := db.GetArtistCredits(global.DB, artist.ID, show_hidden_releases) + dbCredits, err := controller.GetArtistCredits(global.DB, artist.ID, show_hidden_releases) if err != nil { fmt.Printf("FATAL: Failed to retrieve artist credits for %s: %s\n", artist.ID, err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) @@ -100,7 +99,7 @@ func CreateArtist() http.Handler { } if artist.Name == "" { artist.Name = artist.ID } - err = music.CreateArtist(global.DB, &artist) + err = controller.CreateArtist(global.DB, &artist) if err != nil { if strings.Contains(err.Error(), "duplicate key") { http.Error(w, fmt.Sprintf("Artist %s already exists\n", artist.ID), http.StatusBadRequest) @@ -148,7 +147,7 @@ func UpdateArtist(artist *model.Artist) http.Handler { } } - err = music.UpdateArtist(global.DB, artist) + err = controller.UpdateArtist(global.DB, artist) if err != nil { if strings.Contains(err.Error(), "no rows") { http.NotFound(w, r) @@ -162,7 +161,7 @@ func UpdateArtist(artist *model.Artist) http.Handler { func DeleteArtist(artist *model.Artist) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - err := music.DeleteArtist(global.DB, artist.ID) + err := controller.DeleteArtist(global.DB, artist.ID) if err != nil { if strings.Contains(err.Error(), "no rows") { http.NotFound(w, r) diff --git a/api/release.go b/api/release.go index 3027a8c..e13bc93 100644 --- a/api/release.go +++ b/api/release.go @@ -12,13 +12,109 @@ import ( "arimelody-web/admin" "arimelody-web/global" - music "arimelody-web/music/controller" - "arimelody-web/music/model" + "arimelody-web/controller" + "arimelody-web/model" ) +func ServeRelease(release *model.Release) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // only allow authorised users to view hidden releases + authorised := admin.GetSession(r) != nil + if !authorised && !release.Visible { + http.NotFound(w, r) + return + } + + type ( + Track struct { + Title string `json:"title"` + Description string `json:"description"` + Lyrics string `json:"lyrics"` + } + + Credit struct { + *model.Artist + Role string `json:"role"` + Primary bool `json:"primary"` + } + + Release struct { + *model.Release + Tracks []Track `json:"tracks"` + Credits []Credit `json:"credits"` + Links map[string]string `json:"links"` + } + ) + + response := Release{ + Release: release, + Tracks: []Track{}, + Credits: []Credit{}, + Links: make(map[string]string), + } + + if authorised || release.IsReleased() { + // get credits + credits, err := controller.GetReleaseCredits(global.DB, release.ID) + if err != nil { + fmt.Printf("FATAL: Failed to serve release %s: Credits: %s\n", release.ID, err) + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + return + } + for _, credit := range credits { + artist, err := controller.GetArtist(global.DB, credit.Artist.ID) + if err != nil { + fmt.Printf("FATAL: Failed to serve release %s: Artists: %s\n", release.ID, err) + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + return + } + + response.Credits = append(response.Credits, Credit{ + Artist: artist, + Role: credit.Role, + Primary: credit.Primary, + }) + } + + // get tracks + tracks, err := controller.GetReleaseTracks(global.DB, release.ID) + if err != nil { + fmt.Printf("FATAL: Failed to serve release %s: Tracks: %s\n", release.ID, err) + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + return + } + for _, track := range tracks { + response.Tracks = append(response.Tracks, Track{ + Title: track.Title, + Description: track.Description, + Lyrics: track.Lyrics, + }) + } + + // get links + links, err := controller.GetReleaseLinks(global.DB, release.ID) + if err != nil { + fmt.Printf("FATAL: Failed to serve release %s: Links: %s\n", release.ID, err) + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + return + } + for _, link := range links { + response.Links[link.Name] = link.URL + } + } + + w.Header().Add("Content-Type", "application/json") + err := json.NewEncoder(w).Encode(response) + if err != nil { + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + return + } + }) +} + func ServeCatalog() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - releases, err := music.GetAllReleases(global.DB, false, 0, true) + releases, err := controller.GetAllReleases(global.DB, false, 0, true) if err != nil { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return @@ -95,7 +191,7 @@ func CreateRelease() http.Handler { if release.Artwork == "" { release.Artwork = "/img/default-cover-art.png" } - err = music.CreateRelease(global.DB, &release) + err = controller.CreateRelease(global.DB, &release) if err != nil { if strings.Contains(err.Error(), "duplicate key") { http.Error(w, fmt.Sprintf("Release %s already exists\n", release.ID), http.StatusBadRequest) @@ -173,7 +269,7 @@ func UpdateRelease(release *model.Release) http.Handler { } } - err = music.UpdateRelease(global.DB, release) + err = controller.UpdateRelease(global.DB, release) if err != nil { if strings.Contains(err.Error(), "no rows") { http.NotFound(w, r) @@ -194,7 +290,7 @@ func UpdateReleaseTracks(release *model.Release) http.Handler { return } - err = music.UpdateReleaseTracks(global.DB, release.ID, trackIDs) + err = controller.UpdateReleaseTracks(global.DB, release.ID, trackIDs) if err != nil { if strings.Contains(err.Error(), "no rows") { http.NotFound(w, r) @@ -231,7 +327,7 @@ func UpdateReleaseCredits(release *model.Release) http.Handler { }) } - err = music.UpdateReleaseCredits(global.DB, release.ID, credits) + err = controller.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) @@ -261,7 +357,7 @@ func UpdateReleaseLinks(release *model.Release) http.Handler { return } - err = music.UpdateReleaseLinks(global.DB, release.ID, links) + err = controller.UpdateReleaseLinks(global.DB, release.ID, links) if err != nil { if strings.Contains(err.Error(), "no rows") { http.NotFound(w, r) @@ -275,7 +371,7 @@ func UpdateReleaseLinks(release *model.Release) http.Handler { func DeleteRelease(release *model.Release) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - err := music.DeleteRelease(global.DB, release.ID) + err := controller.DeleteRelease(global.DB, release.ID) if err != nil { if strings.Contains(err.Error(), "no rows") { http.NotFound(w, r) diff --git a/api/track.go b/api/track.go index fd4018b..71a67e9 100644 --- a/api/track.go +++ b/api/track.go @@ -6,8 +6,8 @@ import ( "net/http" "arimelody-web/global" - music "arimelody-web/music/controller" - "arimelody-web/music/model" + "arimelody-web/controller" + "arimelody-web/model" ) type ( @@ -26,7 +26,7 @@ func ServeAllTracks() http.Handler { var tracks = []Track{} var dbTracks = []*model.Track{} - dbTracks, err := music.GetAllTracks(global.DB) + dbTracks, err := controller.GetAllTracks(global.DB) if err != nil { fmt.Printf("FATAL: Failed to pull tracks from DB: %s\n", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) @@ -50,7 +50,7 @@ func ServeAllTracks() http.Handler { func ServeTrack(track *model.Track) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - dbReleases, err := music.GetTrackReleases(global.DB, track.ID, false) + dbReleases, err := controller.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) @@ -89,7 +89,7 @@ func CreateTrack() http.Handler { return } - id, err := music.CreateTrack(global.DB, &track) + id, err := controller.CreateTrack(global.DB, &track) if err != nil { fmt.Printf("FATAL: Failed to create track: %s\n", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) @@ -120,7 +120,7 @@ func UpdateTrack(track *model.Track) http.Handler { return } - err = music.UpdateTrack(global.DB, track) + err = controller.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) @@ -143,7 +143,7 @@ func DeleteTrack(track *model.Track) http.Handler { } var trackID = r.URL.Path[1:] - err := music.DeleteTrack(global.DB, trackID) + err := controller.DeleteTrack(global.DB, trackID) if err != nil { fmt.Printf("Failed to delete track %s: %s\n", trackID, err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) diff --git a/music/controller/artist.go b/controller/artist.go similarity index 94% rename from music/controller/artist.go rename to controller/artist.go index bc9e656..c52b78d 100644 --- a/music/controller/artist.go +++ b/controller/artist.go @@ -1,7 +1,7 @@ -package music +package controller import ( - "arimelody-web/music/model" + "arimelody-web/model" "github.com/jmoiron/sqlx" ) @@ -45,7 +45,7 @@ func GetArtistsNotOnRelease(db *sqlx.DB, releaseID string) ([]*model.Artist, err } func GetArtistCredits(db *sqlx.DB, artistID string, show_hidden bool) ([]*model.Credit, error) { - var query string = "SELECT release.id,release.title,release.artwork,artist.id,artist.name,artist.website,artist.avatar,role,is_primary "+ + var query string = "SELECT release.id,title,artwork,release_date,artist.id,name,website,avatar,role,is_primary "+ "FROM musiccredit "+ "JOIN musicrelease AS release ON release=release.id "+ "JOIN artist ON artist=artist.id "+ @@ -69,6 +69,7 @@ func GetArtistCredits(db *sqlx.DB, artistID string, show_hidden bool) ([]*model. &credit.Release.ID, &credit.Release.Title, &credit.Release.Artwork, + &credit.Release.ReleaseDate, &credit.Artist.ID, &credit.Artist.Name, &credit.Artist.Website, diff --git a/music/controller/release.go b/controller/release.go similarity index 99% rename from music/controller/release.go rename to controller/release.go index 2aeb236..e55fef0 100644 --- a/music/controller/release.go +++ b/controller/release.go @@ -1,10 +1,10 @@ -package music +package controller import ( "errors" "fmt" - "arimelody-web/music/model" + "arimelody-web/model" "github.com/jmoiron/sqlx" ) diff --git a/music/controller/track.go b/controller/track.go similarity index 98% rename from music/controller/track.go rename to controller/track.go index 3187553..d302045 100644 --- a/music/controller/track.go +++ b/controller/track.go @@ -1,7 +1,7 @@ -package music +package controller import ( - "arimelody-web/music/model" + "arimelody-web/model" "github.com/jmoiron/sqlx" ) diff --git a/discord/discord.go b/discord/discord.go index c3b3cec..0dbaebc 100644 --- a/discord/discord.go +++ b/discord/discord.go @@ -15,22 +15,8 @@ import ( const API_ENDPOINT = "https://discord.com/api/v10" var CREDENTIALS_PROVIDED = true -var CLIENT_ID = func() string { - id := os.Getenv("DISCORD_CLIENT") - if id == "" { - fmt.Printf("WARN: Discord client ID (DISCORD_CLIENT) was not provided. Admin login will be unavailable.\n") - CREDENTIALS_PROVIDED = false - } - return id -}() -var CLIENT_SECRET = func() string { - secret := os.Getenv("DISCORD_SECRET") - if secret == "" { - fmt.Printf("WARN: Discord secret (DISCORD_SECRET) was not provided. Admin login will be unavailable.\n") - CREDENTIALS_PROVIDED = false - } - return secret -}() +var CLIENT_ID = os.Getenv("DISCORD_CLIENT") +var CLIENT_SECRET = os.Getenv("DISCORD_SECRET") var OAUTH_CALLBACK_URI = fmt.Sprintf("%s/admin/login", global.HTTP_DOMAIN) var REDIRECT_URI = fmt.Sprintf("https://discord.com/oauth2/authorize?client_id=%s&response_type=code&redirect_uri=%s&scope=identify", CLIENT_ID, OAUTH_CALLBACK_URI) diff --git a/main.go b/main.go index c103696..f87d36e 100644 --- a/main.go +++ b/main.go @@ -12,7 +12,7 @@ import ( "arimelody-web/admin" "arimelody-web/api" "arimelody-web/global" - musicView "arimelody-web/music/view" + "arimelody-web/view" "arimelody-web/templates" "github.com/jmoiron/sqlx" @@ -49,7 +49,7 @@ func createServeMux() *http.ServeMux { mux.Handle("/admin/", http.StripPrefix("/admin", admin.Handler())) mux.Handle("/api/", http.StripPrefix("/api", api.Handler())) - mux.Handle("/music/", http.StripPrefix("/music", musicView.Handler())) + mux.Handle("/music/", http.StripPrefix("/music", view.MusicHandler())) mux.Handle("/uploads/", http.StripPrefix("/uploads", staticHandler("uploads"))) mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/" || r.URL.Path == "/index.html" { diff --git a/music/model/artist.go b/model/artist.go similarity index 100% rename from music/model/artist.go rename to model/artist.go diff --git a/music/model/credit.go b/model/credit.go similarity index 100% rename from music/model/credit.go rename to model/credit.go diff --git a/music/model/link.go b/model/link.go similarity index 100% rename from music/model/link.go rename to model/link.go diff --git a/music/model/release.go b/model/release.go similarity index 100% rename from music/model/release.go rename to model/release.go diff --git a/music/model/track.go b/model/track.go similarity index 100% rename from music/model/track.go rename to model/track.go diff --git a/music/view/release.go b/music/view/release.go deleted file mode 100644 index fcb2b29..0000000 --- a/music/view/release.go +++ /dev/null @@ -1,136 +0,0 @@ -package view - -import ( - "encoding/json" - "fmt" - "net/http" - - "arimelody-web/admin" - "arimelody-web/global" - "arimelody-web/music/model" - db "arimelody-web/music/controller" - "arimelody-web/templates" -) - -type ( - Track struct { - Title string `json:"title"` - Description string `json:"description"` - Lyrics string `json:"lyrics"` - } - - Credit struct { - *model.Artist - Role string `json:"role"` - Primary bool `json:"primary"` - } - - Release struct { - *model.Release - Tracks []Track `json:"tracks"` - Credits []Credit `json:"credits"` - Links map[string]string `json:"links"` - } -) - -func ServeRelease(release *model.Release) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // only allow authorised users to view hidden releases - authorised := admin.GetSession(r) != nil - if !authorised && !release.Visible { - http.NotFound(w, r) - return - } - - response := Release{ - Release: release, - Tracks: []Track{}, - Credits: []Credit{}, - Links: make(map[string]string), - } - - if authorised || release.IsReleased() { - // get credits - credits, err := db.GetReleaseCredits(global.DB, release.ID) - if err != nil { - fmt.Printf("FATAL: Failed to serve release %s: Credits: %s\n", release.ID, err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - for _, credit := range credits { - artist, err := db.GetArtist(global.DB, credit.Artist.ID) - if err != nil { - fmt.Printf("FATAL: Failed to serve release %s: Artists: %s\n", release.ID, err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - - response.Credits = append(response.Credits, Credit{ - Artist: artist, - Role: credit.Role, - Primary: credit.Primary, - }) - } - - // get tracks - tracks, err := db.GetReleaseTracks(global.DB, release.ID) - if err != nil { - fmt.Printf("FATAL: Failed to serve release %s: Tracks: %s\n", release.ID, err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - for _, track := range tracks { - response.Tracks = append(response.Tracks, Track{ - Title: track.Title, - Description: track.Description, - Lyrics: track.Lyrics, - }) - } - - // get links - links, err := db.GetReleaseLinks(global.DB, release.ID) - if err != nil { - fmt.Printf("FATAL: Failed to serve release %s: Links: %s\n", release.ID, err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - for _, link := range links { - response.Links[link.Name] = link.URL - } - } - - w.Header().Add("Content-Type", "application/json") - err := json.NewEncoder(w).Encode(response) - if err != nil { - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - }) -} - -func ServeGateway(release *model.Release) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // only allow authorised users to view hidden releases - authorised := admin.GetSession(r) != nil - if !authorised && !release.Visible { - http.NotFound(w, r) - return - } - - response := *release - - if authorised || release.IsReleased() { - response.Tracks = release.Tracks - response.Credits = release.Credits - response.Links = release.Links - } - - err := templates.Pages["music-gateway"].Execute(w, response) - - if err != nil { - fmt.Printf("Error rendering music gateway for %s: %s\n", release.ID, err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - }) -} diff --git a/music/view/music.go b/view/music.go similarity index 51% rename from music/view/music.go rename to view/music.go index 6297a42..f38e1c2 100644 --- a/music/view/music.go +++ b/view/music.go @@ -4,15 +4,16 @@ import ( "fmt" "net/http" + "arimelody-web/admin" + "arimelody-web/controller" "arimelody-web/global" - music "arimelody-web/music/controller" - "arimelody-web/music/model" + "arimelody-web/model" "arimelody-web/templates" ) // HTTP HANDLER METHODS -func Handler() http.Handler { +func MusicHandler() http.Handler { mux := http.NewServeMux() mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -21,7 +22,7 @@ func Handler() http.Handler { return } - release, err := music.GetRelease(global.DB, r.URL.Path[1:], true) + release, err := controller.GetRelease(global.DB, r.URL.Path[1:], true) if err != nil { http.NotFound(w, r) return @@ -35,7 +36,7 @@ func Handler() http.Handler { func ServeCatalog() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - releases, err := music.GetAllReleases(global.DB, true, 0, true) + releases, err := controller.GetAllReleases(global.DB, true, 0, true) if err != nil { fmt.Printf("FATAL: Failed to pull releases for catalog: %s\n", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) @@ -54,3 +55,30 @@ func ServeCatalog() http.Handler { } }) } + +func ServeGateway(release *model.Release) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // only allow authorised users to view hidden releases + authorised := admin.GetSession(r) != nil + if !authorised && !release.Visible { + http.NotFound(w, r) + return + } + + response := *release + + if authorised || release.IsReleased() { + response.Tracks = release.Tracks + response.Credits = release.Credits + response.Links = release.Links + } + + err := templates.Pages["music-gateway"].Execute(w, response) + + if err != nil { + fmt.Printf("Error rendering music gateway for %s: %s\n", release.ID, err) + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + return + } + }) +} diff --git a/views/music.html b/views/music.html index 7473dc2..6f84672 100644 --- a/views/music.html +++ b/views/music.html @@ -59,11 +59,11 @@

- yes! well, in most cases... + yes!* in most cases...

- from Dream (2022) onward, all of my self-released songs are - licensed under Creative Commons Attribution-ShareAlike 4.0. + all of my self-released songs are licensed under + Creative Commons Attribution-ShareAlike 4.0. anyone may use and remix these songs freely, so long as they provide credit back to me and link back to this license! please note that all derivative works must inherit this license.

@@ -71,23 +71,17 @@ a great example of some credit text would be as follows:

- music used: mellodoot - Dream
- https://arimelody.me/music/dream
- licensed under CC BY-SA 4.0. + music used: ari melody - free2play
+ https://arimelody.me/music/free2play
+ licensed under CC BY-SA 4.0.

- for any songs prior to this, they were all either released by me (in which case, i honestly - don't mind), or in collaboration with chill people who i don't see having an issue with it. - do be sure to ask them about it, though! + if the song you want to use is not released by me (i.e. under a record label), their usage rights + will likely trump whatever i'd otherwise have in mind. i'll try to negotiate some nice terms, though!

- in the event the song you want to use is released under some other label, their usage rights - will more than likely trump whatever i'd otherwise have in mind. i'll try to negotiate some - nice terms, though! ;3 -

-

- i love the idea of other creators using my songs in their work, so if you do happen to use - my stuff in a work you're particularly proud of, feel free to send it my way! + i believe that encouraging creative use of artistic works is better than stifling any use at all. + if you do happen to use my work in something you're particularly proud of, feel free to send it my way!

> ari@arimelody.me