diff --git a/admin/accounthttp.go b/admin/accounthttp.go
index d26e9bf..c93c8b9 100644
--- a/admin/accounthttp.go
+++ b/admin/accounthttp.go
@@ -115,7 +115,7 @@ func changePasswordHandler(app *app.AppState) http.Handler {
return
}
- app.LogService.Info(model.LOG_ACCOUNT, "\"%s\" changed password by user request. (%s)", session.Account.Username, controller.ResolveIP(app, r))
+ app.Log.Info(model.LOG_ACCOUNT, "\"%s\" changed password by user request. (%s)", session.Account.Username, controller.ResolveIP(app, r))
controller.SetSessionError(app.DB, session, "")
controller.SetSessionMessage(app.DB, session, "Password updated successfully.")
@@ -145,7 +145,7 @@ func deleteAccountHandler(app *app.AppState) http.Handler {
// check password
if err := bcrypt.CompareHashAndPassword([]byte(session.Account.Password), []byte(r.Form.Get("password"))); err != nil {
- app.LogService.Warn(model.LOG_ACCOUNT, "Account \"%s\" attempted account deletion with incorrect password. (%s)", session.Account.Username, controller.ResolveIP(app, r))
+ app.Log.Warn(model.LOG_ACCOUNT, "Account \"%s\" attempted account deletion with incorrect password. (%s)", session.Account.Username, controller.ResolveIP(app, r))
controller.SetSessionError(app.DB, session, "Incorrect password.")
http.Redirect(w, r, "/admin/account", http.StatusFound)
return
@@ -159,7 +159,7 @@ func deleteAccountHandler(app *app.AppState) http.Handler {
return
}
- app.LogService.Info(model.LOG_ACCOUNT, "Account \"%s\" deleted by user request. (%s)", session.Account.Username, controller.ResolveIP(app, r))
+ app.Log.Info(model.LOG_ACCOUNT, "Account \"%s\" deleted by user request. (%s)", session.Account.Username, controller.ResolveIP(app, r))
controller.SetSessionAccount(app.DB, session, nil)
controller.SetSessionError(app.DB, session, "")
@@ -310,7 +310,7 @@ func totpConfirmHandler(app *app.AppState) http.Handler {
return
}
- app.LogService.Info(model.LOG_ACCOUNT, "\"%s\" created TOTP method \"%s\".", session.Account.Username, totp.Name)
+ app.Log.Info(model.LOG_ACCOUNT, "\"%s\" created TOTP method \"%s\".", session.Account.Username, totp.Name)
controller.SetSessionError(app.DB, session, "")
controller.SetSessionMessage(app.DB, session, fmt.Sprintf("TOTP method \"%s\" created successfully.", totp.Name))
@@ -358,7 +358,7 @@ func totpDeleteHandler(app *app.AppState) http.Handler {
return
}
- app.LogService.Info(model.LOG_ACCOUNT, "\"%s\" deleted TOTP method \"%s\".", session.Account.Username, totp.Name)
+ app.Log.Info(model.LOG_ACCOUNT, "\"%s\" deleted TOTP method \"%s\".", session.Account.Username, totp.Name)
controller.SetSessionError(app.DB, session, "")
controller.SetSessionMessage(app.DB, session, fmt.Sprintf("TOTP method \"%s\" deleted successfully.", totp.Name))
diff --git a/admin/artisthttp.go b/admin/artisthttp.go
index 05cfbb5..ef67a4e 100644
--- a/admin/artisthttp.go
+++ b/admin/artisthttp.go
@@ -6,6 +6,7 @@ import (
"strings"
"arimelody-web/admin/templates"
+ "arimelody-web/controller"
"arimelody-web/model"
"arimelody-web/model/app"
)
@@ -22,7 +23,7 @@ func serveArtists(app *app.AppState) http.Handler {
return
}
- artists, err := app.MusicService.GetAllArtists()
+ artists, err := controller.GetAllArtists(app.DB)
if err != nil {
fmt.Printf("WARN: Failed to fetch artists: %s\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
@@ -49,7 +50,7 @@ func serveArtist(app *app.AppState, artistID string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
session := r.Context().Value("session").(*model.Session)
- artist, err := app.MusicService.GetArtistByID(artistID)
+ artist, err := controller.GetArtist(app.DB, artistID)
if err != nil {
if artist == nil {
http.NotFound(w, r)
@@ -60,7 +61,7 @@ func serveArtist(app *app.AppState, artistID string) http.Handler {
return
}
- credits, err := app.MusicService.GetArtistCredits(artistID, true)
+ credits, err := controller.GetArtistCredits(app.DB, artist.ID, true)
if err != nil {
fmt.Printf("WARN: Failed to serve admin artist page for %s: %s\n", artistID, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
diff --git a/admin/http.go b/admin/http.go
index 66e66f1..10dc3f6 100644
--- a/admin/http.go
+++ b/admin/http.go
@@ -83,39 +83,39 @@ func AdminIndexHandler(app *app.AppState) http.Handler {
session := r.Context().Value("session").(*model.Session)
- releases, err := app.MusicService.GetAllReleases(false, 3)
+ releases, err := controller.GetAllReleases(app.DB, false, 3, true)
if err != nil {
fmt.Fprintf(os.Stderr, "WARN: Failed to pull releases: %s\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
- releaseCount, err := app.MusicService.GetReleaseCount(false)
+ releaseCount, err := controller.GetReleaseCount(app.DB, false)
if err != nil {
fmt.Fprintf(os.Stderr, "WARN: Failed to pull releases count: %s\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
- artists, err := app.MusicService.GetAllArtists()
+ artists, err := controller.GetAllArtists(app.DB)
if err != nil {
fmt.Fprintf(os.Stderr, "WARN: Failed to pull artists: %s\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
- artistCount, err := app.MusicService.GetArtistCount()
+ artistCount, err := controller.GetArtistCount(app.DB)
if err != nil {
fmt.Fprintf(os.Stderr, "WARN: Failed to pull artist count: %s\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
- tracks, err := app.MusicService.GetOrphanTracks()
+ tracks, err := controller.GetOrphanTracks(app.DB)
if err != nil {
fmt.Fprintf(os.Stderr, "WARN: Failed to pull orphan tracks: %s\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
- trackCount, err := app.MusicService.GetTrackCount()
+ trackCount, err := controller.GetTrackCount(app.DB)
if err != nil {
fmt.Fprintf(os.Stderr, "WARN: Failed to pull track count: %s\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
@@ -241,7 +241,7 @@ func registerAccountHandler(app *app.AppState) http.Handler {
return
}
- app.LogService.Info(
+ app.Log.Info(
model.LOG_ACCOUNT,
"Account \"%s\" (%s) created using invite \"%s\". (%s)",
credentials.Username,
@@ -252,7 +252,7 @@ func registerAccountHandler(app *app.AppState) http.Handler {
err = controller.DeleteInvite(app.DB, invite.Code)
if err != nil {
- app.LogService.Warn(model.LOG_ACCOUNT, "Failed to delete expired invite \"%s\": %v", invite.Code, err)
+ app.Log.Warn(model.LOG_ACCOUNT, "Failed to delete expired invite \"%s\": %v", invite.Code, err)
}
// registration success!
@@ -332,7 +332,7 @@ func loginHandler(app *app.AppState) http.Handler {
err = bcrypt.CompareHashAndPassword([]byte(account.Password), []byte(password))
if err != nil {
- app.LogService.Warn(model.LOG_ACCOUNT, "\"%s\" attempted login with incorrect password. (%s)", account.Username, controller.ResolveIP(app, r))
+ app.Log.Warn(model.LOG_ACCOUNT, "\"%s\" attempted login with incorrect password. (%s)", account.Username, controller.ResolveIP(app, r))
if locked := handleFailedLogin(app, account, r); locked {
controller.SetSessionError(app.DB, session, "Too many failed attempts. This account is now locked.")
} else {
@@ -366,8 +366,8 @@ func loginHandler(app *app.AppState) http.Handler {
// login success!
// TODO: log login activity to user
- app.LogService.Info(model.LOG_ACCOUNT, "\"%s\" logged in. (%s)", account.Username, controller.ResolveIP(app, r))
- app.LogService.Warn(model.LOG_ACCOUNT, "\"%s\" does not have any TOTP methods assigned.", account.Username)
+ app.Log.Info(model.LOG_ACCOUNT, "\"%s\" logged in. (%s)", account.Username, controller.ResolveIP(app, r))
+ app.Log.Warn(model.LOG_ACCOUNT, "\"%s\" does not have any TOTP methods assigned.", account.Username)
err = controller.SetSessionAccount(app.DB, session, account)
if err != nil {
@@ -420,7 +420,7 @@ func loginTOTPHandler(app *app.AppState) http.Handler {
totpCode := r.FormValue("totp")
if len(totpCode) != controller.TOTP_CODE_LENGTH {
- app.LogService.Warn(model.LOG_ACCOUNT, "\"%s\" failed login (Invalid TOTP). (%s)", session.AttemptAccount.Username, controller.ResolveIP(app, r))
+ app.Log.Warn(model.LOG_ACCOUNT, "\"%s\" failed login (Invalid TOTP). (%s)", session.AttemptAccount.Username, controller.ResolveIP(app, r))
controller.SetSessionError(app.DB, session, "Invalid TOTP.")
render()
return
@@ -434,7 +434,7 @@ func loginTOTPHandler(app *app.AppState) http.Handler {
return
}
if totpMethod == nil {
- app.LogService.Warn(model.LOG_ACCOUNT, "\"%s\" failed login (Incorrect TOTP). (%s)", session.AttemptAccount.Username, controller.ResolveIP(app, r))
+ app.Log.Warn(model.LOG_ACCOUNT, "\"%s\" failed login (Incorrect TOTP). (%s)", session.AttemptAccount.Username, controller.ResolveIP(app, r))
if locked := handleFailedLogin(app, session.AttemptAccount, r); locked {
controller.SetSessionError(app.DB, session, "Too many failed attempts. This account is now locked.")
controller.SetSessionAttemptAccount(app.DB, session, nil)
@@ -446,7 +446,7 @@ func loginTOTPHandler(app *app.AppState) http.Handler {
return
}
- app.LogService.Info(model.LOG_ACCOUNT, "\"%s\" logged in with TOTP method \"%s\". (%s)", session.AttemptAccount.Username, totpMethod.Name, controller.ResolveIP(app, r))
+ app.Log.Info(model.LOG_ACCOUNT, "\"%s\" logged in with TOTP method \"%s\". (%s)", session.AttemptAccount.Username, totpMethod.Name, controller.ResolveIP(app, r))
err = controller.SetSessionAccount(app.DB, session, session.AttemptAccount)
if err != nil {
@@ -574,7 +574,7 @@ func handleFailedLogin(app *app.AppState, account *model.Account, r *http.Reques
account.Username,
err,
)
- app.LogService.Warn(
+ app.Log.Warn(
model.LOG_ACCOUNT,
"Failed to lock account \"%s\"",
account.Username,
@@ -588,7 +588,7 @@ func handleFailedLogin(app *app.AppState, account *model.Account, r *http.Reques
model.MAX_LOGIN_FAIL_ATTEMPTS,
controller.ResolveIP(app, r),
)
- app.LogService.Warn(
+ app.Log.Warn(
model.LOG_ACCOUNT,
"Account \"%s\" was locked: %d failed login attempts (IP: %s)",
account.Username,
@@ -606,7 +606,7 @@ func handleFailedLogin(app *app.AppState, account *model.Account, r *http.Reques
account.Username,
err,
)
- app.LogService.Warn(
+ app.Log.Warn(
model.LOG_ACCOUNT,
"Failed to increment login failures for \"%s\"",
account.Username,
diff --git a/admin/logshttp.go b/admin/logshttp.go
index b17095d..70c9627 100644
--- a/admin/logshttp.go
+++ b/admin/logshttp.go
@@ -43,7 +43,7 @@ func logsHandler(app *app.AppState) http.Handler {
}
}
- logs, err := app.LogService.Search(levelFilter, typeFilter, query, 100, 0)
+ logs, err := app.Log.Search(levelFilter, typeFilter, query, 100, 0)
if err != nil {
fmt.Fprintf(os.Stderr, "WARN: Failed to fetch audit logs: %v\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
diff --git a/admin/releasehttp.go b/admin/releasehttp.go
index 72586ed..d16528e 100644
--- a/admin/releasehttp.go
+++ b/admin/releasehttp.go
@@ -7,9 +7,9 @@ import (
"strings"
"arimelody-web/admin/templates"
+ "arimelody-web/controller"
"arimelody-web/model"
"arimelody-web/model/app"
- "arimelody-web/errors"
)
func serveReleases(app *app.AppState) http.Handler {
@@ -34,7 +34,7 @@ func serveReleases(app *app.AppState) http.Handler {
Releases []*model.Release
}
- releases, err := app.MusicService.GetAllFullReleases(false, 0)
+ 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)
@@ -60,13 +60,9 @@ func serveRelease(app *app.AppState, releaseID string, action string) http.Handl
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
session := r.Context().Value("session").(*model.Session)
- release, err := app.MusicService.GetFullReleaseByID(releaseID)
+ release, err := controller.GetRelease(app.DB, releaseID, true)
if err != nil {
- if errors.IsValidationError(err) {
- http.Error(w, err.Error(), http.StatusBadRequest)
- return
- }
- if errors.IsNotExistError(err) {
+ if strings.Contains(err.Error(), "no rows") {
http.NotFound(w, r)
return
}
@@ -134,7 +130,7 @@ func serveEditCredits(release *model.Release) http.Handler {
func serveAddCredit(app *app.AppState, release *model.Release) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- artists, err := app.MusicService.GetArtistsNotOnRelease(release.ID)
+ 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)
@@ -162,7 +158,7 @@ func serveNewCredit(app *app.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 := app.MusicService.GetArtistByID(artistID)
+ 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)
@@ -211,7 +207,7 @@ func serveEditTracks(release *model.Release) http.Handler {
func serveAddTrack(app *app.AppState, release *model.Release) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- tracks, err := app.MusicService.GetTracksNotOnRelease(release.ID)
+ 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)
@@ -239,7 +235,7 @@ func serveNewTrack(app *app.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 := app.MusicService.GetTrackByID(trackID)
+ 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)
diff --git a/admin/trackhttp.go b/admin/trackhttp.go
index 8c4554b..2122748 100644
--- a/admin/trackhttp.go
+++ b/admin/trackhttp.go
@@ -6,6 +6,7 @@ import (
"strings"
"arimelody-web/admin/templates"
+ "arimelody-web/controller"
"arimelody-web/model"
"arimelody-web/model/app"
)
@@ -22,7 +23,7 @@ func serveTracks(app *app.AppState) http.Handler {
return
}
- tracks, err := app.MusicService.GetAllTracks()
+ tracks, err := controller.GetAllTracks(app.DB)
if err != nil {
fmt.Printf("WARN: Failed to fetch tracks: %s\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
@@ -49,7 +50,7 @@ func serveTrack(app *app.AppState, trackID string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
session := r.Context().Value("session").(*model.Session)
- track, err := app.MusicService.GetTrackByID(trackID)
+ track, err := controller.GetTrack(app.DB, trackID)
if err != nil {
fmt.Printf("WARN: Failed to serve admin track page for %s: %s\n", trackID, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
@@ -60,7 +61,7 @@ func serveTrack(app *app.AppState, trackID string) http.Handler {
return
}
- releases, err := app.MusicService.GetTrackReleases(trackID)
+ releases, err := controller.GetTrackReleases(app.DB, track.ID, true)
if err != nil {
fmt.Printf("WARN: Failed to fetch releases for track %s: %s\n", trackID, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
diff --git a/api/api.go b/api/api.go
index f23fcbd..84b7f8a 100644
--- a/api/api.go
+++ b/api/api.go
@@ -10,7 +10,6 @@ import (
"arimelody-web/controller"
"arimelody-web/model"
"arimelody-web/model/app"
- "arimelody-web/errors"
)
func Handler(app *app.AppState) http.Handler {
@@ -22,9 +21,9 @@ func Handler(app *app.AppState) 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 := app.MusicService.GetArtistByID(artistID)
+ artist, err := controller.GetArtist(app.DB, artistID)
if err != nil {
- if errors.IsNotExistError(err) {
+ if strings.Contains(err.Error(), "no rows") {
http.NotFound(w, r)
return
}
@@ -64,9 +63,9 @@ func Handler(app *app.AppState) 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 := app.MusicService.GetFullReleaseByID(releaseID)
+ release, err := controller.GetRelease(app.DB, releaseID, true)
if err != nil {
- if errors.IsNotExistError(err) {
+ if strings.Contains(err.Error(), "no rows") {
http.NotFound(w, r)
return
}
@@ -106,9 +105,9 @@ func Handler(app *app.AppState) 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 := app.MusicService.GetTrackByID(trackID)
+ track, err := controller.GetTrack(app.DB, trackID)
if err != nil {
- if errors.IsNotExistError(err) {
+ if strings.Contains(err.Error(), "no rows") {
http.NotFound(w, r)
return
}
@@ -188,10 +187,7 @@ func getSession(app *app.AppState, r *http.Request) (*model.Session, error) {
// fetch existing session
session, err := controller.GetSession(app, token)
- if errors.IsValidationError(err) {
- return nil, err
- }
- if errors.IsNotExistError(err) {
+ if err != nil && !strings.Contains(err.Error(), "no rows") {
return nil, fmt.Errorf("Failed to retrieve session: %v\n", err)
}
diff --git a/api/artist.go b/api/artist.go
index 61cb207..d5f8a31 100644
--- a/api/artist.go
+++ b/api/artist.go
@@ -10,15 +10,15 @@ import (
"strings"
"time"
+ "arimelody-web/controller"
"arimelody-web/model"
"arimelody-web/model/app"
- "arimelody-web/errors"
)
func ServeAllArtists(app *app.AppState) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var artists = []*model.Artist{}
- artists, err := app.MusicService.GetAllArtists()
+ artists, err := controller.GetAllArtists(app.DB)
if err != nil {
fmt.Printf("WARN: Failed to serve all artists: %s\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
@@ -53,9 +53,9 @@ func ServeArtist(app *app.AppState, artist *model.Artist) http.Handler {
)
session := r.Context().Value("session").(*model.Session)
- showHiddenReleases := session != nil && session.Account != nil
+ show_hidden_releases := session != nil && session.Account != nil
- dbCredits, err := app.MusicService.GetArtistCredits(artist.ID, showHiddenReleases)
+ dbCredits, err := controller.GetArtistCredits(app.DB, artist.ID, show_hidden_releases)
if err != nil {
fmt.Printf("WARN: Failed to retrieve artist credits for %s: %v\n", artist.ID, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
@@ -91,34 +91,31 @@ func CreateArtist(app *app.AppState) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
session := r.Context().Value("session").(*model.Session)
- type CreateArtistDTO struct {
- ID string `json:"id"`
- Name string `json:"name"`
- }
- dto := &CreateArtistDTO{}
-
- err := json.NewDecoder(r.Body).Decode(dto)
+ var artist model.Artist
+ err := json.NewDecoder(r.Body).Decode(&artist)
if err != nil {
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
- err = app.MusicService.CreateArtist(dto.ID, dto.Name, "", "")
+ if artist.ID == "" {
+ http.Error(w, "Artist ID cannot be blank\n", http.StatusBadRequest)
+ return
+ }
+ if artist.Name == "" { artist.Name = artist.ID }
+
+ err = controller.CreateArtist(app.DB, &artist)
if err != nil {
- if errors.IsValidationError(err) {
- http.Error(w, err.Error(), http.StatusBadRequest)
- return
- }
if strings.Contains(err.Error(), "duplicate key") {
- http.Error(w, fmt.Sprintf("Artist %s already exists\n", dto.ID), http.StatusBadRequest)
+ http.Error(w, fmt.Sprintf("Artist %s already exists\n", artist.ID), http.StatusBadRequest)
return
}
- fmt.Printf("WARN: Failed to create artist %s: %s\n", dto.ID, err)
+ fmt.Printf("WARN: Failed to create artist %s: %s\n", artist.ID, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
- app.LogService.Info(model.LOG_ARTIST, "Artist \"%s\" created by \"%s\".", dto.Name, session.Account.Username)
+ app.Log.Info(model.LOG_ARTIST, "Artist \"%s\" created by \"%s\".", artist.Name, session.Account.Username)
w.WriteHeader(http.StatusCreated)
})
@@ -159,13 +156,9 @@ func UpdateArtist(app *app.AppState, artist *model.Artist) http.Handler {
}
}
- err = app.MusicService.UpdateArtist(artist)
+ err = controller.UpdateArtist(app.DB, artist)
if err != nil {
- if errors.IsValidationError(err) {
- http.Error(w, err.Error(), http.StatusBadRequest)
- return
- }
- if errors.IsNotExistError(err) {
+ if strings.Contains(err.Error(), "no rows") {
http.NotFound(w, r)
return
}
@@ -173,7 +166,7 @@ func UpdateArtist(app *app.AppState, artist *model.Artist) http.Handler {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
- app.LogService.Info(model.LOG_ARTIST, "Artist \"%s\" updated by \"%s\".", artist.Name, session.Account.Username)
+ app.Log.Info(model.LOG_ARTIST, "Artist \"%s\" updated by \"%s\".", artist.Name, session.Account.Username)
})
}
@@ -181,13 +174,9 @@ func DeleteArtist(app *app.AppState, artist *model.Artist) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
session := r.Context().Value("session").(*model.Session)
- err := app.MusicService.DeleteArtist(artist.ID)
+ err := controller.DeleteArtist(app.DB, artist.ID)
if err != nil {
- if errors.IsValidationError(err) {
- http.Error(w, err.Error(), http.StatusBadRequest)
- return
- }
- if errors.IsNotExistError(err) {
+ if strings.Contains(err.Error(), "no rows") {
http.NotFound(w, r)
return
}
@@ -195,6 +184,6 @@ func DeleteArtist(app *app.AppState, artist *model.Artist) http.Handler {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
- app.LogService.Info(model.LOG_ARTIST, "Artist \"%s\" deleted by \"%s\".", artist.Name, session.Account.Username)
+ app.Log.Info(model.LOG_ARTIST, "Artist \"%s\" deleted by \"%s\".", artist.Name, session.Account.Username)
})
}
diff --git a/api/release.go b/api/release.go
index e97eb16..71bac93 100644
--- a/api/release.go
+++ b/api/release.go
@@ -13,7 +13,6 @@ import (
"arimelody-web/controller"
"arimelody-web/model"
"arimelody-web/model/app"
- "arimelody-web/errors"
)
func ServeRelease(app *app.AppState, release *model.Release) http.Handler {
@@ -40,50 +39,50 @@ func ServeRelease(app *app.AppState, release *model.Release) http.Handler {
}
type (
- TrackDTO struct {
+ Track struct {
Title string `json:"title"`
Description string `json:"description"`
Lyrics string `json:"lyrics"`
}
- CreditDTO struct {
+ Credit struct {
*model.Artist
Role string `json:"role"`
Primary bool `json:"primary"`
}
- ReleaseDTO struct {
+ Release struct {
*model.Release
- Tracks []TrackDTO `json:"tracks"`
- Credits []CreditDTO `json:"credits"`
+ Tracks []Track `json:"tracks"`
+ Credits []Credit `json:"credits"`
Links map[string]string `json:"links"`
}
)
- response := ReleaseDTO{
+ response := Release{
Release: release,
- Tracks: []TrackDTO{},
- Credits: []CreditDTO{},
+ Tracks: []Track{},
+ Credits: []Credit{},
Links: make(map[string]string),
}
if release.IsReleased() || privileged {
// get credits
- credits, err := app.MusicService.GetReleaseCredits(release.ID)
+ credits, err := controller.GetReleaseCredits(app.DB, release.ID)
if err != nil {
fmt.Printf("WARN: 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 := app.MusicService.GetArtistByID(credit.Artist.ID)
+ artist, err := controller.GetArtist(app.DB, credit.Artist.ID)
if err != nil {
fmt.Printf("WARN: 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, CreditDTO{
+ response.Credits = append(response.Credits, Credit{
Artist: artist,
Role: credit.Role,
Primary: credit.Primary,
@@ -91,14 +90,14 @@ func ServeRelease(app *app.AppState, release *model.Release) http.Handler {
}
// get tracks
- tracks, err := app.MusicService.GetReleaseTracks(release.ID)
+ tracks, err := controller.GetReleaseTracks(app.DB, release.ID)
if err != nil {
fmt.Printf("WARN: 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, TrackDTO{
+ response.Tracks = append(response.Tracks, Track{
Title: track.Title,
Description: track.Description,
Lyrics: track.Lyrics,
@@ -106,7 +105,7 @@ func ServeRelease(app *app.AppState, release *model.Release) http.Handler {
}
// get links
- links, err := app.MusicService.GetReleaseLinks(release.ID)
+ links, err := controller.GetReleaseLinks(app.DB, release.ID)
if err != nil {
fmt.Printf("WARN: Failed to serve release %s: Links: %s\n", release.ID, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
@@ -130,7 +129,7 @@ func ServeRelease(app *app.AppState, release *model.Release) http.Handler {
func ServeCatalog(app *app.AppState) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- releases, err := app.MusicService.GetAllFullReleases(false, 0)
+ releases, err := controller.GetAllReleases(app.DB, false, 0, true)
if err != nil {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
@@ -193,56 +192,47 @@ func CreateRelease(app *app.AppState) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
session := r.Context().Value("session").(*model.Session)
- type CreateReleaseDTO struct {
- ID string `json:"id"`
- Title string `json:"title"`
- ReleaseType string `json:"type"`
- ReleaseDate time.Time `json:"release_date"`
- Artwork string `json:"artwork"`
- }
- var dto CreateReleaseDTO
- err := json.NewDecoder(r.Body).Decode(&dto)
+ var release model.Release
+ err := json.NewDecoder(r.Body).Decode(&release)
if err != nil {
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
- if dto.ReleaseType == "" { dto.ReleaseType = string(model.Single) }
- if dto.ReleaseDate != time.Unix(0, 0) {
- dto.ReleaseDate = time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 0, 0, 0, 0, time.UTC)
+ if release.ID == "" {
+ http.Error(w, "Release ID cannot be empty\n", http.StatusBadRequest)
+ return
+ }
+
+ if release.Title == "" { release.Title = release.ID }
+ if release.ReleaseType == "" { release.ReleaseType = model.Single }
+
+ if release.ReleaseDate != time.Unix(0, 0) {
+ release.ReleaseDate = time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 0, 0, 0, 0, time.UTC)
}
- if dto.Artwork == "" { dto.Artwork = model.DEFAULT_RELEASE_ARTWORK_URL }
- err = app.MusicService.CreateRelease(
- dto.ID,
- dto.Title,
- dto.ReleaseType,
- dto.ReleaseDate,
- dto.Artwork,
- )
+ if release.Artwork == "" { release.Artwork = "/img/default-cover-art.png" }
+
+ err = controller.CreateRelease(app.DB, &release)
if err != nil {
- if errors.IsValidationError(err) {
- http.Error(w, err.Error(), http.StatusBadRequest)
- return
- }
if strings.Contains(err.Error(), "duplicate key") {
- http.Error(w, fmt.Sprintf("Release %s already exists\n", dto.ID), http.StatusBadRequest)
+ http.Error(w, fmt.Sprintf("Release %s already exists\n", release.ID), http.StatusBadRequest)
return
}
- fmt.Printf("WARN: Failed to create release %s: %s\n", dto.ID, err)
+ fmt.Printf("WARN: Failed to create release %s: %s\n", release.ID, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
- app.LogService.Info(model.LOG_MUSIC, "Release \"%s\" created by \"%s\".", dto.ID, session.Account.Username)
+ app.Log.Info(model.LOG_MUSIC, "Release \"%s\" created by \"%s\".", release.ID, session.Account.Username)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
encoder := json.NewEncoder(w)
encoder.SetIndent("", "\t")
- err = encoder.Encode(dto)
+ err = encoder.Encode(release)
if err != nil {
- fmt.Printf("WARN: Release %s created, but failed to send JSON response: %s\n", dto.ID, err)
+ fmt.Printf("WARN: Release %s created, but failed to send JSON response: %s\n", release.ID, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
})
@@ -307,13 +297,9 @@ func UpdateRelease(app *app.AppState, release *model.Release) http.Handler {
}
}
- err = app.MusicService.UpdateRelease(release)
+ err = controller.UpdateRelease(app.DB, release)
if err != nil {
- if errors.IsValidationError(err) {
- http.Error(w, err.Error(), http.StatusBadRequest)
- return
- }
- if errors.IsNotExistError(err) {
+ if strings.Contains(err.Error(), "no rows") {
http.NotFound(w, r)
return
}
@@ -321,7 +307,7 @@ func UpdateRelease(app *app.AppState, release *model.Release) http.Handler {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
- app.LogService.Info(model.LOG_MUSIC, "Release \"%s\" updated by \"%s\".", release.ID, session.Account.Username)
+ app.Log.Info(model.LOG_MUSIC, "Release \"%s\" updated by \"%s\".", release.ID, session.Account.Username)
})
}
@@ -329,32 +315,28 @@ func UpdateReleaseTracks(app *app.AppState, release *model.Release) http.Handler
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
session := r.Context().Value("session").(*model.Session)
- var newTrackIDs = []string{}
- err := json.NewDecoder(r.Body).Decode(&newTrackIDs)
+ var trackIDs = []string{}
+ err := json.NewDecoder(r.Body).Decode(&trackIDs)
if err != nil {
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
- err = app.MusicService.UpdateReleaseTracks(release.ID, newTrackIDs)
+ err = controller.UpdateReleaseTracks(app.DB, release.ID, trackIDs)
if err != nil {
- if errors.IsValidationError(err) {
- http.Error(w, err.Error(), http.StatusBadRequest)
- return
- }
- if errors.IsNotExistError(err) {
- http.NotFound(w, r)
- return
- }
if strings.Contains(err.Error(), "duplicate key") {
http.Error(w, "Release cannot have duplicate tracks", http.StatusBadRequest)
return
}
+ if strings.Contains(err.Error(), "no rows") {
+ http.NotFound(w, r)
+ return
+ }
fmt.Printf("WARN: Failed to update tracks for %s: %s\n", release.ID, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
- app.LogService.Info(model.LOG_MUSIC, "Release \"%s\" tracklist updated by \"%s\".", release.ID, session.Account.Username)
+ app.Log.Info(model.LOG_MUSIC, "Tracklist for release \"%s\" updated by \"%s\".", release.ID, session.Account.Username)
})
}
@@ -362,22 +344,22 @@ func UpdateReleaseCredits(app *app.AppState, release *model.Release) http.Handle
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
session := r.Context().Value("session").(*model.Session)
- type CreditDTO struct {
+ type creditJSON struct {
Artist string
Role string
Primary bool
}
- var dto []CreditDTO
- err := json.NewDecoder(r.Body).Decode(&dto)
+ var data []creditJSON
+ err := json.NewDecoder(r.Body).Decode(&data)
if err != nil {
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
- var newCredits []*model.Credit
- for _, credit := range dto {
- newCredits = append(newCredits, &model.Credit{
- Artist: &model.Artist{
+ var credits []*model.Credit
+ for _, credit := range data {
+ credits = append(credits, &model.Credit{
+ Artist: model.Artist{
ID: credit.Artist,
},
Role: credit.Role,
@@ -385,25 +367,21 @@ func UpdateReleaseCredits(app *app.AppState, release *model.Release) http.Handle
})
}
- err = app.MusicService.UpdateReleaseCredits(release.ID, newCredits)
+ err = controller.UpdateReleaseCredits(app.DB, release.ID, credits)
if err != nil {
- if errors.IsValidationError(err) {
- http.Error(w, err.Error(), http.StatusBadRequest)
- return
- }
- if errors.IsNotExistError(err) {
- http.NotFound(w, r)
- return
- }
if strings.Contains(err.Error(), "duplicate key") {
http.Error(w, "Artists may only be credited once", http.StatusBadRequest)
return
}
+ if strings.Contains(err.Error(), "no rows") {
+ http.NotFound(w, r)
+ return
+ }
fmt.Printf("WARN: Failed to update credits for %s: %s\n", release.ID, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
- app.LogService.Info(model.LOG_MUSIC, "Release \"%s\" credits updated by \"%s\".", release.ID, session.Account.Username)
+ app.Log.Info(model.LOG_MUSIC, "Credits for release \"%s\" updated by \"%s\".", release.ID, session.Account.Username)
})
}
@@ -411,41 +389,28 @@ func UpdateReleaseLinks(app *app.AppState, release *model.Release) http.Handler
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
session := r.Context().Value("session").(*model.Session)
- type LinkDTO struct {
- Name string `json:"name"`
- URL string `json:"url"`
- }
- var dto = []LinkDTO{}
- err := json.NewDecoder(r.Body).Decode(&dto)
+ var links = []*model.Link{}
+ err := json.NewDecoder(r.Body).Decode(&links)
if err != nil {
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
- links := []*model.Link{}
- for _, link := range dto {
- links = append(links, &model.Link{ Name: link.Name, URL: link.URL })
- }
-
- err = app.MusicService.UpdateReleaseLinks(release.ID, links)
+ err = controller.UpdateReleaseLinks(app.DB, release.ID, links)
if err != nil {
- if errors.IsValidationError(err) {
- http.Error(w, err.Error(), http.StatusBadRequest)
- return
- }
- if errors.IsNotExistError(err) {
- http.NotFound(w, r)
- return
- }
if strings.Contains(err.Error(), "duplicate key") {
http.Error(w, "Release cannot have duplicate link names", http.StatusBadRequest)
return
}
+ if strings.Contains(err.Error(), "no rows") {
+ http.NotFound(w, r)
+ return
+ }
fmt.Printf("WARN: Failed to update links for %s: %s\n", release.ID, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
- app.LogService.Info(model.LOG_MUSIC, "Release \"%s\" links updated by \"%s\".", release.ID, session.Account.Username)
+ app.Log.Info(model.LOG_MUSIC, "Links for release \"%s\" updated by \"%s\".", release.ID, session.Account.Username)
})
}
@@ -453,13 +418,9 @@ func DeleteRelease(app *app.AppState, release *model.Release) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
session := r.Context().Value("session").(*model.Session)
- err := app.MusicService.DeleteRelease(release.ID)
+ err := controller.DeleteRelease(app.DB, release.ID)
if err != nil {
- if errors.IsValidationError(err) {
- http.Error(w, err.Error(), http.StatusBadRequest)
- return
- }
- if errors.IsNotExistError(err) {
+ if strings.Contains(err.Error(), "no rows") {
http.NotFound(w, r)
return
}
@@ -467,6 +428,6 @@ func DeleteRelease(app *app.AppState, release *model.Release) http.Handler {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
- app.LogService.Info(model.LOG_MUSIC, "Release \"%s\" deleted by \"%s\".", release.ID, session.Account.Username)
+ app.Log.Info(model.LOG_MUSIC, "Release \"%s\" deleted by \"%s\".", release.ID, session.Account.Username)
})
}
diff --git a/api/track.go b/api/track.go
index 749c66e..8d81ea6 100644
--- a/api/track.go
+++ b/api/track.go
@@ -5,7 +5,7 @@ import (
"fmt"
"net/http"
- "arimelody-web/errors"
+ "arimelody-web/controller"
"arimelody-web/model"
"arimelody-web/model/app"
)
@@ -26,7 +26,7 @@ func ServeAllTracks(app *app.AppState) http.Handler {
var tracks = []Track{}
var dbTracks = []*model.Track{}
- dbTracks, err := app.MusicService.GetAllTracks()
+ dbTracks, err := controller.GetAllTracks(app.DB)
if err != nil {
fmt.Printf("WARN: Failed to pull tracks from DB: %s\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
@@ -52,7 +52,7 @@ func ServeAllTracks(app *app.AppState) http.Handler {
func ServeTrack(app *app.AppState, track *model.Track) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- dbReleases, err := app.MusicService.GetTrackReleases(track.ID)
+ dbReleases, err := controller.GetTrackReleases(app.DB, track.ID, false)
if err != nil {
fmt.Printf("WARN: Failed to pull track releases for %s from DB: %s\n", track.ID, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
@@ -78,30 +78,26 @@ func CreateTrack(app *app.AppState) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
session := r.Context().Value("session").(*model.Session)
- type CreateTrackDTO struct {
- Title string `json:"title"`
- Description string `json:"description"`
- Lyrics string `json:"lyrics"`
- }
- var dto CreateTrackDTO
- err := json.NewDecoder(r.Body).Decode(&dto)
+ var track model.Track
+ err := json.NewDecoder(r.Body).Decode(&track)
if err != nil {
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
- id, err := app.MusicService.CreateTrack(dto.Title, dto.Description, dto.Lyrics, "")
+ if track.Title == "" {
+ http.Error(w, "Track title cannot be empty\n", http.StatusBadRequest)
+ return
+ }
+
+ id, err := controller.CreateTrack(app.DB, &track)
if err != nil {
- if errors.IsValidationError(err) {
- http.Error(w, err.Error(), http.StatusBadRequest)
- return
- }
fmt.Printf("WARN: Failed to create track: %s\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
- app.LogService.Info(model.LOG_MUSIC, "Track \"%s\" (%s) created by \"%s\".", dto.Title, id, session.Account.Username)
+ app.Log.Info(model.LOG_MUSIC, "Track \"%s\" (%s) created by \"%s\".", track.Title, track.ID, session.Account.Username)
w.Header().Add("Content-Type", "text/plain")
w.WriteHeader(http.StatusCreated)
@@ -118,34 +114,25 @@ func UpdateTrack(app *app.AppState, track *model.Track) http.Handler {
session := r.Context().Value("session").(*model.Session)
- type UpdateTrackDTO struct {
- Title string `json:"title"`
- Description string `json:"description"`
- Lyrics string `json:"lyrics"`
- }
- var dto UpdateTrackDTO
- err := json.NewDecoder(r.Body).Decode(&dto)
+ err := json.NewDecoder(r.Body).Decode(&track)
if err != nil {
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
- track.Title = dto.Title
- track.Description = dto.Description
- track.Lyrics = dto.Lyrics
+ if track.Title == "" {
+ http.Error(w, "Track title cannot be empty\n", http.StatusBadRequest)
+ return
+ }
- err = app.MusicService.UpdateTrack(track)
+ err = controller.UpdateTrack(app.DB, track)
if err != nil {
- if errors.IsValidationError(err) {
- http.Error(w, err.Error(), http.StatusBadRequest)
- return
- }
fmt.Printf("WARN: Failed to update track %s: %s\n", track.ID, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
- app.LogService.Info(model.LOG_MUSIC, "Track \"%s\" (%s) updated by \"%s\".", track.Title, track.ID, session.Account.Username)
+ app.Log.Info(model.LOG_MUSIC, "Track \"%s\" (%s) updated by \"%s\".", track.Title, track.ID, session.Account.Username)
w.Header().Add("Content-Type", "application/json")
encoder := json.NewEncoder(w)
@@ -167,16 +154,12 @@ func DeleteTrack(app *app.AppState, track *model.Track) http.Handler {
session := r.Context().Value("session").(*model.Session)
var trackID = r.URL.Path[1:]
- err := app.MusicService.DeleteTrack(trackID)
+ err := controller.DeleteTrack(app.DB, trackID)
if err != nil {
- if errors.IsNotExistError(err) {
- http.NotFound(w, r)
- return
- }
fmt.Printf("WARN: Failed to delete track %s: %s\n", trackID, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
- app.LogService.Info(model.LOG_MUSIC, "Track \"%s\" (%s) deleted by \"%s\".", track.Title, track.ID, session.Account.Username)
+ app.Log.Info(model.LOG_MUSIC, "Track \"%s\" (%s) deleted by \"%s\".", track.Title, track.ID, session.Account.Username)
})
}
diff --git a/api/uploads.go b/api/uploads.go
index fb40f0f..27731c2 100644
--- a/api/uploads.go
+++ b/api/uploads.go
@@ -50,7 +50,7 @@ func HandleImageUpload(app *app.AppState, data *string, directory string, filena
return "", nil
}
- app.LogService.Info(model.LOG_FILES, "\"%s\" created.", imagePath)
+ app.Log.Info(model.LOG_FILES, "\"%s\" created.", imagePath)
return filename, nil
}
diff --git a/repository/music/artist_postgres.go b/controller/artist.go
similarity index 50%
rename from repository/music/artist_postgres.go
rename to controller/artist.go
index aa86da9..adcdbc5 100644
--- a/repository/music/artist_postgres.go
+++ b/controller/artist.go
@@ -1,29 +1,17 @@
-package music
+package controller
import (
"arimelody-web/model"
+
+ "github.com/jmoiron/sqlx"
)
-func (repo *MusicRepositoryPostgres) GetAllArtists() ([]*model.Artist, error) {
- var artists = []*model.Artist{}
+// DATABASE
- err := repo.db.Select(&artists, "SELECT * FROM artist")
- if err != nil {
- return nil, err
- }
-
- return artists, nil
-}
-func (repo *MusicRepositoryPostgres) GetArtistCount() (int, error) {
- var count int
- err := repo.db.Get(&count, "SELECT count(*) FROM artist")
- return count, err
-}
-
-func (repo *MusicRepositoryPostgres) GetArtistByID(id string) (*model.Artist, error) {
+func GetArtist(db *sqlx.DB, id string) (*model.Artist, error) {
var artist = model.Artist{}
- err := repo.db.Get(&artist, "SELECT * FROM artist WHERE id=$1", id)
+ err := db.Get(&artist, "SELECT * FROM artist WHERE id=$1", id)
if err != nil {
return nil, err
}
@@ -31,10 +19,26 @@ func (repo *MusicRepositoryPostgres) GetArtistByID(id string) (*model.Artist, er
return &artist, nil
}
-func (repo *MusicRepositoryPostgres) GetArtistsNotOnRelease(releaseID string) ([]*model.Artist, error) {
+func GetAllArtists(db *sqlx.DB) ([]*model.Artist, error) {
var artists = []*model.Artist{}
- err := repo.db.Select(&artists,
+ err := db.Select(&artists, "SELECT * FROM artist")
+ if err != nil {
+ return nil, err
+ }
+
+ return artists, nil
+}
+func GetArtistCount(db *sqlx.DB) (int, error) {
+ var count int
+ err := db.Get(&count, "SELECT count(*) FROM artist")
+ return count, err
+}
+
+func GetArtistsNotOnRelease(db *sqlx.DB, releaseID string) ([]*model.Artist, error) {
+ var artists = []*model.Artist{}
+
+ err := db.Select(&artists,
"SELECT * FROM artist "+
"WHERE id NOT IN "+
"(SELECT artist FROM musiccredit WHERE release=$1)",
@@ -46,15 +50,15 @@ func (repo *MusicRepositoryPostgres) GetArtistsNotOnRelease(releaseID string) ([
return artists, nil
}
-func (repo *MusicRepositoryPostgres) GetArtistCredits(artistID string, showHidden bool) ([]*model.Credit, error) {
+func GetArtistCredits(db *sqlx.DB, artistID string, show_hidden bool) ([]*model.Credit, error) {
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 "+
"WHERE artist=$1 "
- if !showHidden { query += "AND visible=true " }
+ if !show_hidden { query += "AND visible=true " }
query += "ORDER BY release_date DESC"
- rows, err := repo.db.Query(query, artistID)
+ rows, err := db.Query(query, artistID)
if err != nil {
return nil, err
}
@@ -66,10 +70,7 @@ func (repo *MusicRepositoryPostgres) GetArtistCredits(artistID string, showHidde
}
var credits []*model.Credit
for rows.Next() {
- credit := &model.Credit{
- Release: &model.Release{},
- Artist: &model.Artist{},
- }
+ var credit model.Credit
err = rows.Scan(
&credit.Release.ID,
&credit.Release.Title,
@@ -84,42 +85,44 @@ func (repo *MusicRepositoryPostgres) GetArtistCredits(artistID string, showHidde
)
otherArtists := []NamePrimary{}
- err = repo.db.Select(&otherArtists,
+ err = db.Select(&otherArtists,
"SELECT name,is_primary FROM artist "+
"JOIN musiccredit ON artist=id "+
"WHERE release=$1",
credit.Release.ID)
for _, otherCredit := range otherArtists {
credit.Release.Credits = append(credit.Release.Credits, &model.Credit{
- Artist: &model.Artist{
+ Artist: model.Artist{
Name: otherCredit.Name,
},
Primary: otherCredit.Primary,
})
}
- credits = append(credits, credit)
+ credits = append(credits, &credit)
}
return credits, nil
}
-func (repo *MusicRepositoryPostgres) CreateArtist(
- id string,
- name string,
- website string,
- avatar string,
-) error {
- _, err := repo.db.Exec(
+func CreateArtist(db *sqlx.DB, artist *model.Artist) error {
+ _, err := db.Exec(
"INSERT INTO artist (id, name, website, avatar) "+
"VALUES ($1, $2, $3, $4)",
- id, name, website, avatar,
+ artist.ID,
+ artist.Name,
+ artist.Website,
+ artist.Avatar,
)
- return err
+ if err != nil {
+ return err
+ }
+
+ return nil
}
-func (repo *MusicRepositoryPostgres) UpdateArtist(artist *model.Artist) error {
- _, err := repo.db.Exec(
+func UpdateArtist(db *sqlx.DB, artist *model.Artist) error {
+ _, err := db.Exec(
"UPDATE artist "+
"SET name=$2, website=$3, avatar=$4 "+
"WHERE id=$1",
@@ -128,27 +131,22 @@ func (repo *MusicRepositoryPostgres) UpdateArtist(artist *model.Artist) error {
artist.Website,
artist.Avatar,
)
- return err
-}
-func (repo *MusicRepositoryPostgres) UpdateArtistID(oldID string, newID string) error {
- _, err := repo.db.Exec("UPDATE artist SET id=$2 WHERE id=$1", oldID, newID)
- return err
-}
-func (repo *MusicRepositoryPostgres) UpdateArtistName(id string, name string) error {
- _, err := repo.db.Exec("UPDATE artist SET name=$2 WHERE id=$1", id, name)
- return err
-}
-func (repo *MusicRepositoryPostgres) UpdateArtistWebsite(id string, website string) error {
- _, err := repo.db.Exec("UPDATE artist SET website=$2 WHERE id=$1", id, website)
- return err
-}
-func (repo *MusicRepositoryPostgres) UpdateArtistAvatar(id string, avatar string) error {
- _, err := repo.db.Exec("UPDATE artist SET avatar=$2 WHERE id=$1", id, avatar)
- return err
+ if err != nil {
+ return err
+ }
+
+ return nil
}
-func (repo *MusicRepositoryPostgres) DeleteArtist(id string) (string, error) {
- var deletedID string
- err := repo.db.Get(&deletedID, "DELETE FROM artist WHERE id=$1", id)
- return deletedID, err
+func DeleteArtist(db *sqlx.DB, artistID string) error {
+ _, err := db.Exec(
+ "DELETE FROM artist "+
+ "WHERE id=$1",
+ artistID,
+ )
+ if err != nil {
+ return err
+ }
+
+ return nil
}
diff --git a/controller/release.go b/controller/release.go
new file mode 100644
index 0000000..a22d157
--- /dev/null
+++ b/controller/release.go
@@ -0,0 +1,327 @@
+package controller
+
+import (
+ "fmt"
+
+ "arimelody-web/model"
+
+ "github.com/jmoiron/sqlx"
+)
+
+func GetRelease(db *sqlx.DB, id string, full bool) (*model.Release, error) {
+ var release = model.Release{}
+
+ err := db.Get(&release, "SELECT * FROM musicrelease WHERE id=$1", id)
+ if err != nil {
+ return nil, err
+ }
+
+ if full {
+ // get credits
+ credits, err := GetReleaseCredits(db, id)
+ if err != nil {
+ return nil, fmt.Errorf("Credits: %s", err)
+ }
+ for _, credit := range credits {
+ release.Credits = append(release.Credits, credit)
+ }
+
+ // get tracks
+ tracks, err := GetReleaseTracks(db, id)
+ if err != nil {
+ return nil, fmt.Errorf("Tracks: %s", err)
+ }
+ for _, track := range tracks {
+ release.Tracks = append(release.Tracks, track)
+ }
+
+ // get links
+ links, err := GetReleaseLinks(db, id)
+ if err != nil {
+ return nil, fmt.Errorf("Links: %s", err)
+ }
+ for _, link := range links {
+ release.Links = append(release.Links, link)
+ }
+ }
+
+ return &release, nil
+}
+
+func GetAllReleases(db *sqlx.DB, onlyVisible bool, limit int, full bool) ([]*model.Release, error) {
+ var releases = []*model.Release{}
+
+ query := "SELECT * FROM musicrelease"
+ if onlyVisible {
+ query += " WHERE visible=true"
+ }
+ query += " ORDER BY release_date DESC"
+ var err error
+ if limit > 0 {
+ err = db.Select(&releases, query + " LIMIT $1", limit)
+ } else {
+ err = db.Select(&releases, query)
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ for _, release := range releases {
+ // get credits
+ credits, err := GetReleaseCredits(db, release.ID)
+ if err != nil {
+ return nil, fmt.Errorf("Credits: %s", err)
+ }
+ for _, credit := range credits {
+ release.Credits = append(release.Credits, credit)
+ }
+
+ if full {
+ // get tracks
+ tracks, err := GetReleaseTracks(db, release.ID)
+ if err != nil {
+ return nil, fmt.Errorf("Tracks: %s", err)
+ }
+ for _, track := range tracks {
+ release.Tracks = append(release.Tracks, track)
+ }
+
+ // get links
+ links, err := GetReleaseLinks(db, release.ID)
+ if err != nil {
+ return nil, fmt.Errorf("Links: %s", err)
+ }
+ for _, link := range links {
+ release.Links = append(release.Links, link)
+ }
+ }
+ }
+
+ return releases, nil
+}
+func GetReleaseCount(db *sqlx.DB, onlyVisible bool) (int, error) {
+ query := "SELECT count(*) FROM musicrelease"
+ if onlyVisible {
+ query += " WHERE visible=true"
+ }
+
+ var count int
+ err := db.Get(&count, query)
+
+ return count, err
+}
+
+func CreateRelease(db *sqlx.DB, release *model.Release) error {
+ _, err := db.Exec(
+ "INSERT INTO musicrelease "+
+ "(id, visible, title, description, type, release_date, artwork, buyname, buylink, copyright, copyrighturl) "+
+ "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)",
+ release.ID,
+ release.Visible,
+ release.Title,
+ release.Description,
+ release.ReleaseType,
+ release.ReleaseDate.Format("2006-01-02 15:04:05"),
+ release.Artwork,
+ release.Buyname,
+ release.Buylink,
+ release.Copyright,
+ release.CopyrightURL,
+ )
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func UpdateRelease(db *sqlx.DB, release *model.Release) error {
+ _, err := db.Exec(
+ "UPDATE musicrelease SET "+
+ "visible=$2, title=$3, description=$4, type=$5, release_date=$6, artwork=$7, buyname=$8, buylink=$9, copyright=$10, copyrighturl=$11 "+
+ "WHERE id=$1",
+ release.ID,
+ release.Visible,
+ release.Title,
+ release.Description,
+ release.ReleaseType,
+ release.ReleaseDate.Format("2006-01-02 15:04:05"),
+ release.Artwork,
+ release.Buyname,
+ release.Buylink,
+ release.Copyright,
+ release.CopyrightURL,
+ )
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func UpdateReleaseTracks(db *sqlx.DB, releaseID string, new_tracks []string) error {
+ tx, err := db.Begin()
+ if err != nil {
+ return err
+ }
+
+ _, err = tx.Exec("DELETE FROM musicreleasetrack WHERE release=$1", releaseID)
+ if err != nil {
+ return err
+ }
+ for i, trackID := range new_tracks {
+ _, err = tx.Exec(
+ "INSERT INTO musicreleasetrack "+
+ "(release, track, number) "+
+ "VALUES ($1, $2, $3)",
+ releaseID,
+ trackID,
+ i)
+ if err != nil {
+ return err
+ }
+ }
+
+ err = tx.Commit()
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func UpdateReleaseCredits(db *sqlx.DB, releaseID string, new_credits []*model.Credit) error {
+ tx, err := db.Begin()
+ if err != nil {
+ return err
+ }
+
+ _, err = tx.Exec("DELETE FROM musiccredit WHERE release=$1", releaseID)
+ if err != nil {
+ return err
+ }
+ for _, credit := range new_credits {
+ _, err = tx.Exec(
+ "INSERT INTO musiccredit "+
+ "(release, artist, role, is_primary) "+
+ "VALUES ($1, $2, $3, $4)",
+ releaseID,
+ credit.Artist.ID,
+ credit.Role,
+ credit.Primary,
+ )
+ if err != nil {
+ return err
+ }
+ }
+
+ err = tx.Commit()
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func UpdateReleaseLinks(db *sqlx.DB, releaseID string, new_links []*model.Link) error {
+ tx, err := db.Begin()
+ if err != nil {
+ return err
+ }
+
+ _, err = tx.Exec("DELETE FROM musiclink WHERE release=$1", releaseID)
+ if err != nil {
+ return err
+ }
+ for _, link := range new_links {
+ _, err := tx.Exec(
+ "INSERT INTO musiclink "+
+ "(release, name, url) "+
+ "VALUES ($1, $2, $3)",
+ releaseID,
+ link.Name,
+ link.URL,
+ )
+ if err != nil {
+ return err
+ }
+ }
+
+ err = tx.Commit()
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func DeleteRelease(db *sqlx.DB, releaseID string) error {
+ _, err := db.Exec(
+ "DELETE FROM musicrelease "+
+ "WHERE id=$1",
+ releaseID,
+ )
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func GetReleaseTracks(db *sqlx.DB, releaseID string) ([]*model.Track, error) {
+ var tracks = []*model.Track{}
+
+ err := db.Select(&tracks,
+ "SELECT musictrack.* FROM musictrack "+
+ "JOIN musicreleasetrack ON track=id "+
+ "WHERE release=$1 "+
+ "ORDER BY number ASC",
+ releaseID,
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ return tracks, nil
+}
+
+func GetReleaseCredits(db *sqlx.DB, releaseID string) ([]*model.Credit, error) {
+ rows, err := db.Query(
+ "SELECT artist.id,artist.name,artist.website,artist.avatar,role,is_primary "+
+ "FROM musiccredit "+
+ "JOIN artist ON artist=artist.id "+
+ "JOIN musicrelease ON release=musicrelease.id "+
+ "WHERE musicrelease.id=$1 "+
+ "ORDER BY is_primary DESC",
+ releaseID,
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ var credits []*model.Credit
+ for rows.Next() {
+ credit := model.Credit{}
+ rows.Scan(
+ &credit.Artist.ID,
+ &credit.Artist.Name,
+ &credit.Artist.Website,
+ &credit.Artist.Avatar,
+ &credit.Role,
+ &credit.Primary)
+ credits = append(credits, &credit)
+ }
+
+ return credits, nil
+}
+
+func GetReleaseLinks(db *sqlx.DB, releaseID string) ([]*model.Link, error) {
+ var links = []*model.Link{}
+
+ err := db.Select(&links, "SELECT name,url FROM musiclink WHERE release=$1", releaseID)
+ if err != nil {
+ return nil, err
+ }
+
+ return links, nil
+}
diff --git a/controller/session.go b/controller/session.go
index 08b6bdb..875d50f 100644
--- a/controller/session.go
+++ b/controller/session.go
@@ -38,10 +38,10 @@ func GetSessionFromRequest(app *app.AppState, r *http.Request) (*model.Session,
account, _ := app.AccountService.GetByID(session.Account.ID)
msg += " (Account \"" + account.Username + "\")"
}
- app.LogService.Warn(model.LOG_ACCOUNT, msg)
+ app.Log.Warn(model.LOG_ACCOUNT, msg)
err = DeleteSession(app.DB, session.Token)
if err != nil {
- app.LogService.Warn(model.LOG_ACCOUNT, "Failed to delete affected session")
+ app.Log.Warn(model.LOG_ACCOUNT, "Failed to delete affected session")
}
return nil, nil
}
diff --git a/controller/track.go b/controller/track.go
new file mode 100644
index 0000000..27f4afc
--- /dev/null
+++ b/controller/track.go
@@ -0,0 +1,181 @@
+package controller
+
+import (
+ "arimelody-web/model"
+
+ "github.com/jmoiron/sqlx"
+)
+
+// DATABASE
+
+func GetTrack(db *sqlx.DB, id string) (*model.Track, error) {
+ var track = model.Track{}
+
+ stmt, _ := db.Preparex("SELECT * FROM musictrack WHERE id=$1")
+ err := stmt.Get(&track, id)
+ if err != nil {
+ return nil, err
+ }
+ return &track, nil
+}
+
+func GetAllTracks(db *sqlx.DB) ([]*model.Track, error) {
+ var tracks = []*model.Track{}
+
+ err := db.Select(&tracks, "SELECT * FROM musictrack")
+ if err != nil {
+ return nil, err
+ }
+
+ return tracks, nil
+}
+func GetTrackCount(db *sqlx.DB) (int, error) {
+ var count int
+ err := db.Get(&count, "SELECT count(*) FROM musictrack")
+ return count, err
+}
+
+func GetOrphanTracks(db *sqlx.DB) ([]*model.Track, error) {
+ var tracks = []*model.Track{}
+
+ err := db.Select(&tracks, "SELECT * FROM musictrack WHERE id NOT IN (SELECT track FROM musicreleasetrack)")
+ if err != nil {
+ return nil, err
+ }
+
+ return tracks, nil
+}
+
+func GetTracksNotOnRelease(db *sqlx.DB, releaseID string) ([]*model.Track, error) {
+ var tracks = []*model.Track{}
+
+ err := db.Select(&tracks,
+ "SELECT * FROM musictrack "+
+ "WHERE id NOT IN "+
+ "(SELECT track FROM musicreleasetrack WHERE release=$1)",
+ releaseID)
+ if err != nil {
+ return nil, err
+ }
+
+ return tracks, nil
+}
+
+func GetTrackReleases(db *sqlx.DB, trackID string, full bool) ([]*model.Release, error) {
+ var releases = []*model.Release{}
+
+ err := db.Select(&releases,
+ "SELECT id,title,type,release_date,artwork,buylink "+
+ "FROM musicrelease "+
+ "JOIN musicreleasetrack ON release=id "+
+ "WHERE track=$1 "+
+ "ORDER BY release_date",
+ trackID,
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ type NamePrimary struct {
+ Name string `json:"name"`
+ Primary bool `json:"primary" db:"is_primary"`
+ }
+ for _, release := range releases {
+ // get artists
+ credits := []NamePrimary{}
+ err := db.Select(&credits,
+ "SELECT name,is_primary FROM artist "+
+ "JOIN musiccredit ON artist=artist.id "+
+ "JOIN musicrelease ON release=musicrelease.id "+
+ "WHERE musicrelease.id=$1", release.ID)
+ if err != nil {
+ return nil, err
+ }
+ for _, credit := range credits {
+ release.Credits = append(release.Credits, &model.Credit{
+ Artist: model.Artist{
+ Name: credit.Name,
+ },
+ Primary: credit.Primary,
+ })
+ }
+
+ // get tracks
+ tracks := []string{}
+ err = db.Select(&tracks, "SELECT track FROM musicreleasetrack WHERE release=$1", release.ID)
+ if err != nil {
+ return nil, err
+ }
+ for _, trackID := range tracks {
+ release.Tracks = append(release.Tracks, &model.Track{
+ ID: trackID,
+ })
+ }
+ }
+
+ return releases, nil
+}
+
+func PullOrphanTracks(db *sqlx.DB) ([]*model.Track, error) {
+ var tracks = []*model.Track{}
+
+ err := db.Select(&tracks,
+ "SELECT id, title, description, lyrics, preview_url FROM musictrack "+
+ "WHERE id NOT IN "+
+ "(SELECT track FROM musicreleasetrack)",
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ return tracks, nil
+}
+
+func CreateTrack(db *sqlx.DB, track *model.Track) (string, error) {
+ var trackID string
+ err := db.QueryRow(
+ "INSERT INTO musictrack (title, description, lyrics, preview_url) "+
+ "VALUES ($1, $2, $3, $4) "+
+ "RETURNING id",
+ track.Title,
+ track.Description,
+ track.Lyrics,
+ track.PreviewURL,
+ ).Scan(&trackID)
+ if err != nil {
+ return "", err
+ }
+
+ return trackID, nil
+}
+
+func UpdateTrack(db *sqlx.DB, track *model.Track) error {
+ _, err := db.Exec(
+ "UPDATE musictrack "+
+ "SET title=$2, description=$3, lyrics=$4, preview_url=$5 "+
+ "WHERE id=$1",
+ track.ID,
+ track.Title,
+ track.Description,
+ track.Lyrics,
+ track.PreviewURL,
+ )
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func DeleteTrack(db *sqlx.DB, trackID string) error {
+ _, err := db.Exec(
+ "DELETE FROM musictrack "+
+ "WHERE id=$1",
+ trackID,
+ )
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/errors/notexist.go b/errors/notexist.go
deleted file mode 100644
index 01b0fc2..0000000
--- a/errors/notexist.go
+++ /dev/null
@@ -1,16 +0,0 @@
-package errors
-
-type NotExistError struct {
- query string
-}
-func NewNotExistError(query string) *NotExistError {
- return &NotExistError{ query: query }
-}
-func (err *NotExistError) Error() string {
- return err.query
-}
-func IsNotExistError(err error) bool {
- _, ok := err.(*NotExistError)
- return ok
-}
-
diff --git a/errors/notexist_test.go b/errors/notexist_test.go
deleted file mode 100644
index fe61771..0000000
--- a/errors/notexist_test.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package errors_test
-
-import (
- "arimelody-web/errors"
- goErrors "errors"
- "testing"
-
- "gotest.tools/v3/assert"
-)
-
-func Test_NotExistError(t *testing.T) {
- var err error
- message := "entity does not exist"
-
- t.Run("can create error", func(t *testing.T) {
- err = errors.NewNotExistError(message)
- assert.Error(t, err, message)
- })
-
- t.Run("validator returns true for valid error", func(t *testing.T) {
- assert.Equal(t, errors.IsNotExistError(err), true)
- })
- t.Run("validator returns false for invalid error", func(t *testing.T) {
- assert.Equal(t, errors.IsNotExistError(goErrors.New("other error")), false)
- })
-}
diff --git a/errors/validation.go b/errors/validation.go
deleted file mode 100644
index 7309694..0000000
--- a/errors/validation.go
+++ /dev/null
@@ -1,15 +0,0 @@
-package errors
-
-type ValidationError struct {
- message string
-}
-func NewValidationError(message string) *ValidationError {
- return &ValidationError{ message: message }
-}
-func (err *ValidationError) Error() string {
- return err.message
-}
-func IsValidationError(err error) bool {
- _, ok := err.(*ValidationError)
- return ok
-}
diff --git a/errors/validation_test.go b/errors/validation_test.go
deleted file mode 100644
index 8a6783c..0000000
--- a/errors/validation_test.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package errors_test
-
-import (
- "arimelody-web/errors"
- goErrors "errors"
- "testing"
-
- "gotest.tools/v3/assert"
-)
-
-func Test_ValidationError(t *testing.T) {
- var err error
- message := "invalid input"
-
- t.Run("can create error", func(t *testing.T) {
- err = errors.NewValidationError(message)
- assert.Error(t, err, message)
- })
-
- t.Run("validator returns true for valid error", func(t *testing.T) {
- assert.Equal(t, errors.IsValidationError(err), true)
- })
- t.Run("validator returns false for invalid error", func(t *testing.T) {
- assert.Equal(t, errors.IsValidationError(goErrors.New("other error")), false)
- })
-}
diff --git a/main.go b/main.go
index 7086265..b662cc8 100644
--- a/main.go
+++ b/main.go
@@ -24,16 +24,12 @@ import (
"arimelody-web/cursor"
"arimelody-web/model"
"arimelody-web/model/app"
- "arimelody-web/view"
-
accountRepo "arimelody-web/repository/account"
logRepo "arimelody-web/repository/log"
- musicRepo "arimelody-web/repository/music"
-
repo "arimelody-web/repository/postgres"
accountService "arimelody-web/service/account"
logService "arimelody-web/service/log"
- musicService "arimelody-web/service/music"
+ "arimelody-web/view"
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq"
@@ -51,7 +47,6 @@ var publicFS embed.FS
func main() {
// TODO: switch to a new logger. this one kinda sucks
- // i'll be so forreal i might write my own
logger := log.New(os.Stderr, "main", model.DEFAULT_LOG_FLAGS)
logger.Print("made with <3 by ari melody\n\n")
@@ -97,7 +92,7 @@ func main() {
app.DB = psqlDB
logRepo := logRepo.NewLogRepositoryPostgres(psqlDB)
- app.LogService = logService.NewLogService(
+ app.Log = logService.NewLogService(
logRepo,
log.New(os.Stderr, "logger", model.DEFAULT_LOG_FLAGS),
)
@@ -108,12 +103,6 @@ func main() {
log.New(os.Stderr, "account-repo", model.DEFAULT_LOG_FLAGS),
)
- musicRepo := musicRepo.NewMusicRepositoryPostgres(psqlDB)
- app.MusicService = musicService.NewMusicService(
- musicRepo,
- log.New(os.Stderr, "music-repo", model.DEFAULT_LOG_FLAGS),
- )
-
// handle command arguments
if len(os.Args) > 1 {
arg := os.Args[1]
@@ -150,7 +139,7 @@ func main() {
logger.Fatalf("FATAL: Failed to create TOTP method: %v\n", err)
}
- app.LogService.Info(model.LOG_ACCOUNT, "TOTP method \"%s\" for \"%s\" created via config utility.", totp.Name, account.Username)
+ app.Log.Info(model.LOG_ACCOUNT, "TOTP method \"%s\" for \"%s\" created via config utility.", totp.Name, account.Username)
url := controller.GenerateTOTPURI(account.Username, totp.Secret)
logger.Printf("%s\n", url)
return
@@ -176,7 +165,7 @@ func main() {
logger.Fatalf("FATAL: Failed to create TOTP method: %v\n", err)
}
- app.LogService.Info(model.LOG_ACCOUNT, "TOTP method \"%s\" for \"%s\" deleted via config utility.", totpName, account.Username)
+ app.Log.Info(model.LOG_ACCOUNT, "TOTP method \"%s\" for \"%s\" deleted via config utility.", totpName, account.Username)
logger.Printf("TOTP method \"%s\" deleted.\n", totpName)
return
@@ -242,7 +231,7 @@ func main() {
if err != nil {
logger.Fatalf("FATAL: Failed to clean up TOTP methods: %v\n", err)
}
- app.LogService.Info(model.LOG_ACCOUNT, "TOTP methods pruned via config utility.")
+ app.Log.Info(model.LOG_ACCOUNT, "TOTP methods pruned via config utility.")
logger.Printf("Cleaned up dangling TOTP methods successfully.\n")
return
@@ -253,7 +242,7 @@ func main() {
logger.Fatalf("FATAL: Failed to create invite code: %v\n", err)
}
- app.LogService.Info(model.LOG_ACCOUNT, "Invite generted via config utility (%s).", invite.Code)
+ app.Log.Info(model.LOG_ACCOUNT, "Invite generted via config utility (%s).", invite.Code)
logger.Printf(
"Here you go! This code expires in %d hours: %s\n",
int(math.Ceil(invite.ExpiresAt.Sub(invite.CreatedAt).Hours())),
@@ -268,7 +257,7 @@ func main() {
logger.Fatalf("FATAL: Failed to delete invites: %v\n", err)
}
- app.LogService.Info(model.LOG_ACCOUNT, "Invites purged via config utility.")
+ app.Log.Info(model.LOG_ACCOUNT, "Invites purged via config utility.")
logger.Printf("Invites deleted successfully.\n")
return
@@ -321,7 +310,7 @@ func main() {
logger.Fatalf("FATAL: Failed to update password: %v\n", err)
}
- app.LogService.Info(model.LOG_ACCOUNT, "Password for '%s' updated via config utility.", account.Username)
+ app.Log.Info(model.LOG_ACCOUNT, "Password for '%s' updated via config utility.", account.Username)
logger.Printf("Password for \"%s\" updated successfully.\n", account.Username)
return
@@ -353,7 +342,7 @@ func main() {
logger.Fatalf("FATAL: Failed to delete account: %v\n", err)
}
- app.LogService.Info(model.LOG_ACCOUNT, "Account '%s' deleted via config utility.", account.Username)
+ app.Log.Info(model.LOG_ACCOUNT, "Account '%s' deleted via config utility.", account.Username)
logger.Printf("Account \"%s\" deleted successfully.\n", account.Username)
return
@@ -378,7 +367,7 @@ func main() {
logger.Fatalf("FATAL: Failed to lock account: %v\n", err)
}
- app.LogService.Info(model.LOG_ACCOUNT, "Account '%s' locked via config utility.", account.Username)
+ app.Log.Info(model.LOG_ACCOUNT, "Account '%s' locked via config utility.", account.Username)
logger.Printf("Account \"%s\" locked successfully.\n", account.Username)
return
@@ -403,13 +392,13 @@ func main() {
logger.Fatalf("FATAL: Failed to unlock account: %v\n", err)
}
- app.LogService.Info(model.LOG_ACCOUNT, "Account '%s' unlocked via config utility.", account.Username)
+ app.Log.Info(model.LOG_ACCOUNT, "Account '%s' unlocked via config utility.", account.Username)
logger.Printf("Account \"%s\" unlocked successfully.\n", account.Username)
return
case "logs":
// TODO: add log search parameters
- logs, err := app.LogService.Search([]model.LogLevel{}, []string{}, "", 100, 0)
+ logs, err := app.Log.Search([]model.LogLevel{}, []string{}, "", 100, 0)
if err != nil {
logger.Fatalf("FATAL: Failed to fetch logs: %v\n", err)
}
diff --git a/model/account.go b/model/account.go
index 80e0a92..67424b7 100644
--- a/model/account.go
+++ b/model/account.go
@@ -16,8 +16,8 @@ type (
Email sql.NullString `json:"email" db:"email"`
AvatarURL sql.NullString `json:"avatar_url" db:"avatar_url"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
- FailAttempts int `json:"fail_attempts" db:"fail_attempts"`
- Locked bool `json:"locked" db:"locked"`
+ FailAttempts int `json:"fail_attempts" db:"fail_attempts"`
+ Locked bool `json:"locked" db:"locked"`
Privileges []AccountPrivilege `json:"privileges"`
}
diff --git a/model/app/appstate.go b/model/app/appstate.go
index 80a84d4..bc8d69b 100644
--- a/model/app/appstate.go
+++ b/model/app/appstate.go
@@ -6,12 +6,8 @@ import (
"github.com/jmoiron/sqlx"
"arimelody-web/model/twitch"
-
- logService "arimelody-web/service/log"
- //inviteService "arimelody-web/service/invite"
accountService "arimelody-web/service/account"
- //sessionService "arimelody-web/service/session"
- musicService "arimelody-web/service/music"
+ "arimelody-web/service/log"
)
type (
@@ -49,13 +45,10 @@ type (
AppState struct {
DB *sqlx.DB
Config Config
+ Log *log.LogService
Twitch *twitch.State
PublicFS embed.FS
- LogService *logService.LogService
- //InviteService *inviteService.InviteService
AccountService *accountService.AccountService
- //SesisonService *sessionService.SessionService
- MusicService *musicService.MusicService
}
)
diff --git a/model/artist.go b/model/artist.go
index b9f940b..746a7dd 100644
--- a/model/artist.go
+++ b/model/artist.go
@@ -9,11 +9,9 @@ type (
}
)
-const DEFAULT_AVATAR_URL = "/img/default-avatar.png"
-
func (artist Artist) GetAvatar() string {
if artist.Avatar == "" {
- return DEFAULT_AVATAR_URL
+ return "/img/default-avatar.png"
}
return artist.Avatar
}
diff --git a/model/artist_test.go b/model/artist_test.go
index 8e39479..feb9a18 100644
--- a/model/artist_test.go
+++ b/model/artist_test.go
@@ -1,22 +1,21 @@
-package model_test
+package model
import (
- "arimelody-web/model"
- "testing"
+ "testing"
)
func Test_Artist_GetAvatar(t *testing.T) {
want := "testavatar.png"
- artist := model.Artist{ Avatar: want }
+ artist := Artist{ Avatar: want }
got := artist.GetAvatar()
if want != got {
t.Errorf(`correct value not returned when avatar is populated (want "%s", got "%s")`, want, got)
}
- artist = model.Artist{}
+ artist = Artist{}
- want = model.DEFAULT_AVATAR_URL
+ want = "/img/default-avatar.png"
got = artist.GetAvatar()
if want != got {
t.Errorf(`default value not returned when avatar is empty (want "%s", got "%s")`, want, got)
diff --git a/model/credit.go b/model/credit.go
index f5f8feb..2080d83 100644
--- a/model/credit.go
+++ b/model/credit.go
@@ -2,9 +2,9 @@ package model
type (
Credit struct {
- Release *Release `json:"release"`
- Artist *Artist `json:"artist"`
- Role string `json:"role"`
- Primary bool `json:"primary" db:"is_primary"`
+ Release Release `json:"release"`
+ Artist Artist `json:"artist"`
+ Role string `json:"role"`
+ Primary bool `json:"primary" db:"is_primary"`
}
)
diff --git a/model/link_test.go b/model/link_test.go
index 2458dac..b368094 100644
--- a/model/link_test.go
+++ b/model/link_test.go
@@ -1,12 +1,11 @@
-package model_test
+package model
import (
- "arimelody-web/model"
- "testing"
+ "testing"
)
func Test_Link_NormaliseName(t *testing.T) {
- link := model.Link{
+ link := Link{
Name: "!c@o#o$l%-^a&w*e(s)o_m=e+-[l{i]n}k-0123456789ABCDEF",
}
diff --git a/model/release.go b/model/release.go
index 3f00d4d..46f4460 100644
--- a/model/release.go
+++ b/model/release.go
@@ -34,8 +34,6 @@ const (
EP ReleaseType = "EP"
Compilation ReleaseType = "compilation"
Upcoming ReleaseType = "upcoming"
-
- DEFAULT_RELEASE_ARTWORK_URL = "/img/default-cover-art.png"
)
// GETTERS
@@ -54,7 +52,7 @@ func (release Release) PrintReleaseDate() string {
func (release Release) GetArtwork() string {
if release.Artwork == "" {
- return DEFAULT_RELEASE_ARTWORK_URL
+ return "/img/default-cover-art.png"
}
return release.Artwork
}
@@ -95,20 +93,3 @@ func (release Release) PrintArtists(only_primary bool, ampersand bool) string {
return strings.Join(names[:], ", ")
}
}
-
-func ValidReleaseType(releaseType string) (ReleaseType, bool) {
- switch releaseType {
- case "single":
- return Single, true
- case "album":
- return Album, true
- case "EP":
- return EP, true
- case "compilation":
- return Compilation, true
- case "upcoming":
- return Upcoming, true
- default:
- return "", false
- }
-}
diff --git a/model/release_test.go b/model/release_test.go
index 37ef775..fc0e221 100644
--- a/model/release_test.go
+++ b/model/release_test.go
@@ -1,7 +1,6 @@
-package model_test
+package model
import (
- "arimelody-web/model"
"strings"
"testing"
"time"
@@ -11,7 +10,7 @@ import (
func Test_Release(t *testing.T) {
t.Run("prints correct description HTML", func(t *testing.T) {
- release := model.Release{
+ release := Release{
Description: "this is\na test\ndescription!",
}
@@ -25,7 +24,7 @@ func Test_Release(t *testing.T) {
})
t.Run("prints correct release date", func(t *testing.T) {
- release := model.Release{
+ release := Release{
ReleaseDate: time.Date(2025, time.July, 26, 16, 0, 0, 0, time.UTC),
}
@@ -35,37 +34,37 @@ func Test_Release(t *testing.T) {
t.Run("returns correct artwork", func(t *testing.T) {
artwork := "testartwork.png"
- release := model.Release{ Artwork: artwork }
+ release := Release{ Artwork: artwork }
assert.Equal(t, release.GetArtwork(), artwork)
})
t.Run("returns placeholder artwork when empty", func(t *testing.T) {
- release := model.Release{}
- assert.Equal(t, release.GetArtwork(), model.DEFAULT_RELEASE_ARTWORK_URL)
+ release := Release{}
+ assert.Equal(t, release.GetArtwork(), "/img/default-cover-art.png")
})
t.Run("singles", func(t *testing.T) {
- release := model.Release{
- Tracks: []*model.Track{},
+ release := Release{
+ Tracks: []*Track{},
}
t.Run("false when no tracks are present", func(t *testing.T) {
assert.Equal(t, release.IsSingle(), false)
})
- release.Tracks = append(release.Tracks, &model.Track{})
+ release.Tracks = append(release.Tracks, &Track{})
t.Run("true when one track is present", func(t *testing.T) {
assert.Equal(t, release.IsSingle(), true)
})
- release.Tracks = append(release.Tracks, &model.Track{})
+ release.Tracks = append(release.Tracks, &Track{})
t.Run("false when >1 tracks are present", func(t *testing.T) {
assert.Equal(t, release.IsSingle(), false)
})
})
t.Run("released", func(t *testing.T) {
- release := model.Release {
+ release := Release {
ReleaseDate: time.Now(),
}
@@ -85,23 +84,23 @@ func Test_Release(t *testing.T) {
artist3 := "idk"
artist4 := "guest"
- release := model.Release{}
+ release := Release{}
t.Run("prints \"Unknown Artist\" when release has no credits", func(t *testing.T) {
assert.Equal(t, release.PrintArtists(false, true), "Unknown Artist")
})
release.Credits = append(
release.Credits,
- &model.Credit{ Artist: &model.Artist{ Name: artist1 }, Primary: true },
+ &Credit{ Artist: Artist{ Name: artist1 }, Primary: true },
)
t.Run("prints ONLY first artist name when release has one credit", func(t *testing.T) {
assert.Equal(t, release.PrintArtists(false, true), artist1)
})
- release.Credits = append(release.Credits, []*model.Credit{
- { Artist: &model.Artist{ Name: artist2 }, Primary: true },
- { Artist: &model.Artist{ Name: artist3 }, Primary: false },
- { Artist: &model.Artist{ Name: artist4 }, Primary: true },
+ release.Credits = append(release.Credits, []*Credit{
+ { Artist: Artist{ Name: artist2 }, Primary: true },
+ { Artist: Artist{ Name: artist3 }, Primary: false },
+ { Artist: Artist{ Name: artist4 }, Primary: true },
}...)
t.Run("can get only unique primary artist names", func(t *testing.T) {
assert.Equal(
@@ -147,37 +146,4 @@ func Test_Release(t *testing.T) {
)
})
})
-
- t.Run("validating release types", func(t *testing.T) {
- t.Run("single", func(t *testing.T) {
- releaseType, ok := model.ValidReleaseType("single")
- assert.Equal(t, ok, true)
- assert.Equal(t, releaseType, model.Single)
- })
- t.Run("album", func(t *testing.T) {
- releaseType, ok := model.ValidReleaseType("album")
- assert.Equal(t, ok, true)
- assert.Equal(t, releaseType, model.Album)
- })
- t.Run("EP", func(t *testing.T) {
- releaseType, ok := model.ValidReleaseType("EP")
- assert.Equal(t, ok, true)
- assert.Equal(t, releaseType, model.EP)
- })
- t.Run("compilation", func(t *testing.T) {
- releaseType, ok := model.ValidReleaseType("compilation")
- assert.Equal(t, ok, true)
- assert.Equal(t, releaseType, model.Compilation)
- })
- t.Run("upcoming", func(t *testing.T) {
- releaseType, ok := model.ValidReleaseType("upcoming")
- assert.Equal(t, ok, true)
- assert.Equal(t, releaseType, model.Upcoming)
- })
- t.Run("invalid", func(t *testing.T) {
- releaseType, ok := model.ValidReleaseType("invalid")
- assert.Equal(t, ok, false)
- assert.Equal(t, string(releaseType), "")
- })
- })
}
diff --git a/model/track_test.go b/model/track_test.go
index 091deff..eb64b10 100644
--- a/model/track_test.go
+++ b/model/track_test.go
@@ -1,12 +1,11 @@
-package model_test
+package model
import (
- "arimelody-web/model"
- "testing"
+ "testing"
)
func Test_Track_DescriptionHTML(t *testing.T) {
- track := model.Track{
+ track := Track{
Description: "this is\na test\ndescription!",
}
@@ -20,7 +19,7 @@ func Test_Track_DescriptionHTML(t *testing.T) {
}
func Test_Track_LyricsHTML(t *testing.T) {
- track := model.Track{
+ track := Track{
Lyrics: "these are\ntest\nlyrics!",
}
diff --git a/repository/account/interface.go b/repository/account/interface.go
index ba90073..fda2b1a 100644
--- a/repository/account/interface.go
+++ b/repository/account/interface.go
@@ -5,8 +5,6 @@ import "arimelody-web/model"
type AccountRepository interface {
GetAll() ([]*model.Account, error)
GetCount() (int, error)
- // Fetches an account by ID, returning an error if one was encountered.
- // If the account does not exist, both response fields are nil.
GetByID(id string) (*model.Account, error)
GetByUsername(username string) (*model.Account, error)
GetByEmail(email string) (*model.Account, error)
@@ -18,20 +16,21 @@ type AccountRepository interface {
// Create an account, returning the new account ID.
Create(username string, password string, email *string, avatarURL *string) (string, error)
- // Deprecated in favour of more specialised Update* and Remove* functions.
+ // Deprecated in favour of more specialised Change* and Remove* functions.
Update(id string, username string, password string, email *string, avatarUrl *string) error
- UpdateUsername(id string, username string) error
- UpdatePassword(id string, password string) error
- UpdateEmail(id string, email string) error
+ ChangeUsername(id string, username string) error
+ ChangePassword(id string, password string) error
+ ChangeEmail(id string, email string) error
RemoveEmail(id string) error
- UpdateAvatarURL(id string, avatarURL string) error
+ ChangeAvatarURL(id string, avatarURL string) error
RemoveAvatar(id string) error
+ Delete(id string) error
+
// Increment the number of account login failure attempts,
// returning the current fail count.
IncrementFails(id string) (int, error)
ResetFails(id string) error
- SetLocked(id string, lock bool) error
-
- Delete(id string) (string, error)
+ Lock(id string) error
+ Unlock(id string) error
}
diff --git a/repository/account/memory.go b/repository/account/memory.go
index 7f886d1..1b94167 100644
--- a/repository/account/memory.go
+++ b/repository/account/memory.go
@@ -1,21 +1,22 @@
package account
import (
- "arimelody-web/errors"
"arimelody-web/model"
"database/sql"
- "slices"
+ "errors"
"strconv"
)
-type AccountRepositoryMemory struct {
- accounts []*model.Account
-}
+type (
+ AccountRepositoryMemory struct {
+ accounts []*model.Account
+ }
+)
var _ AccountRepository = new(AccountRepositoryMemory)
-func NewAccountRepositoryMemory(accounts []*model.Account) *AccountRepositoryMemory {
- return &AccountRepositoryMemory{ accounts: accounts }
+func NewAccountRepositoryMemory() *AccountRepositoryMemory {
+ return &AccountRepositoryMemory{ accounts: make([]*model.Account, 0) }
}
func (repo *AccountRepositoryMemory) GetAll() ([]*model.Account, error) {
@@ -25,11 +26,10 @@ func (repo *AccountRepositoryMemory) GetCount() (int, error) {
return len(repo.accounts), nil
}
func (repo *AccountRepositoryMemory) GetByID(id string) (*model.Account, error) {
- index := slices.IndexFunc(repo.accounts, func(account *model.Account) bool {
- return account.ID == id
- })
- if index == -1 { return nil, nil }
- return repo.accounts[index], nil
+ for _, account := range repo.accounts {
+ if account.ID == id { return account, nil }
+ }
+ return nil, nil
}
func (repo *AccountRepositoryMemory) GetByUsername(username string) (*model.Account, error) {
for _, account := range repo.accounts {
@@ -49,9 +49,9 @@ func (repo *AccountRepositoryMemory) GetByEmail(email string) (*model.Account, e
// Create an account, returning the new account ID.
func (repo *AccountRepositoryMemory) Create(username string, password string, email *string, avatarURL *string) (string, error) {
if account, err := repo.GetByUsername(username); err != nil {
- return "", errors.NewNotExistError("Failed to fetch other acccounts by username")
+ return "", errors.New("Failed to fetch other acccounts by username")
} else if account != nil {
- return "", errors.NewNotExistError("Account with this username already exists")
+ return "", errors.New("Account with this username already exists")
}
emailRef := ""
@@ -73,12 +73,12 @@ func (repo *AccountRepositoryMemory) Create(username string, password string, em
}
// Intended for large profile updates. For smaller adjusments,
-// more specialised Update* and Remove* functions should be used.
+// more specialised Change* and Remove* functions should be used.
func (repo *AccountRepositoryMemory) Update(id string, username string, password string, email *string, avatarUrl *string) error {
if account, err := repo.GetByUsername(username); err != nil {
- return errors.NewNotExistError("Failed to fetch other acccounts by username")
+ return errors.New("Failed to fetch other acccounts by username")
} else if account != nil && account.ID != id {
- return errors.NewNotExistError("Account with this username already exists")
+ return errors.New("Account with this username already exists")
}
account, err := repo.GetByID(id)
@@ -93,32 +93,32 @@ func (repo *AccountRepositoryMemory) Update(id string, username string, password
return nil
}
-func (repo *AccountRepositoryMemory) UpdateUsername(id string, username string) error {
+func (repo *AccountRepositoryMemory) ChangeUsername(id string, username string) error {
if account, err := repo.GetByUsername(username); err != nil {
- return errors.NewNotExistError("Failed to fetch other acccounts by username")
+ return errors.New("Failed to fetch other acccounts by username")
} else if account != nil && account.ID != id {
- return errors.NewNotExistError("Account with this username already exists")
+ return errors.New("Account with this username already exists")
}
account, err := repo.GetByID(id)
if err != nil { return err }
- if account == nil { return errors.NewNotExistError("Account does not exist") }
+ if account == nil { return errors.New("Account does not exist") }
account.Username = username
return nil
}
-func (repo *AccountRepositoryMemory) UpdatePassword(id string, password string) error {
+func (repo *AccountRepositoryMemory) ChangePassword(id string, password string) error {
account, err := repo.GetByID(id)
if err != nil { return err }
- if account == nil { return errors.NewNotExistError("Account does not exist") }
+ if account == nil { return errors.New("Account does not exist") }
account.Password = password
return nil
}
-func (repo *AccountRepositoryMemory) UpdateEmail(id string, email string) error {
+func (repo *AccountRepositoryMemory) ChangeEmail(id string, email string) error {
account, err := repo.GetByID(id)
if err != nil { return err }
- if account == nil { return errors.NewNotExistError("Account does not exist") }
+ if account == nil { return errors.New("Account does not exist") }
account.Email.Valid = true
account.Email.String = email
@@ -127,16 +127,16 @@ func (repo *AccountRepositoryMemory) UpdateEmail(id string, email string) error
func (repo *AccountRepositoryMemory) RemoveEmail(id string) error {
account, err := repo.GetByID(id)
if err != nil { return err }
- if account == nil { return errors.NewNotExistError("Account does not exist") }
+ if account == nil { return errors.New("Account does not exist") }
account.Email.Valid = false
account.Email.String = ""
return nil
}
-func (repo *AccountRepositoryMemory) UpdateAvatarURL(id string, avatarURL string) error {
+func (repo *AccountRepositoryMemory) ChangeAvatarURL(id string, avatarURL string) error {
account, err := repo.GetByID(id)
if err != nil { return err }
- if account == nil { return errors.NewNotExistError("Account does not exist") }
+ if account == nil { return errors.New("Account does not exist") }
account.AvatarURL.Valid = true
account.AvatarURL.String = avatarURL
@@ -145,13 +145,31 @@ func (repo *AccountRepositoryMemory) UpdateAvatarURL(id string, avatarURL string
func (repo *AccountRepositoryMemory) RemoveAvatar(id string) error {
account, err := repo.GetByID(id)
if err != nil { return err }
- if account == nil { return errors.NewNotExistError("Account does not exist") }
+ if account == nil { return errors.New("Account does not exist") }
account.AvatarURL.Valid = false
account.AvatarURL.String = ""
return nil
}
+func (repo *AccountRepositoryMemory) Delete(id string) error {
+ accountIndex := -1
+ for index, account := range repo.accounts {
+ if account.ID == id {
+ accountIndex = index
+ break
+ }
+ }
+ if accountIndex == -1 { return nil }
+
+ repo.accounts = append(
+ repo.accounts[:accountIndex],
+ repo.accounts[accountIndex+1:]...,
+ )
+
+ return nil
+}
+
// Increment the number of account login failure attempts,
// returning the current fail count.
func (repo *AccountRepositoryMemory) IncrementFails(id string) (int, error) {
@@ -166,23 +184,15 @@ func (repo *AccountRepositoryMemory) ResetFails(id string) error {
account.FailAttempts = 0
return nil
}
-func (repo *AccountRepositoryMemory) SetLocked(id string, locked bool) error {
+func (repo *AccountRepositoryMemory) Lock(id string) error {
account, err := repo.GetByID(id)
if err != nil { return err }
- account.Locked = locked
+ account.Locked = true
return nil
}
-
-func (repo *AccountRepositoryMemory) Delete(id string) (string, error) {
- var deletedID string
- newAccounts := []*model.Account{}
- for _, account := range repo.accounts {
- if account.ID == id {
- deletedID = id
- continue
- }
- newAccounts = append(newAccounts, account)
- }
- repo.accounts = newAccounts
- return deletedID, nil
+func (repo *AccountRepositoryMemory) Unlock(id string) error {
+ account, err := repo.GetByID(id)
+ if err != nil { return err }
+ account.Locked = false
+ return nil
}
diff --git a/repository/account/postgres.go b/repository/account/postgres.go
index 03d797f..74ac4c3 100644
--- a/repository/account/postgres.go
+++ b/repository/account/postgres.go
@@ -8,9 +8,11 @@ import (
_ "github.com/lib/pq"
)
-type AccountRepositoryPostgres struct {
- db *sqlx.DB
-}
+type (
+ AccountRepositoryPostgres struct {
+ db *sqlx.DB
+ }
+)
var _ AccountRepository = new(AccountRepositoryPostgres)
@@ -120,21 +122,21 @@ func (repo *AccountRepositoryPostgres) Update(
return err
}
-func (repo *AccountRepositoryPostgres) UpdateUsername(id string, username string) error {
+func (repo *AccountRepositoryPostgres) ChangeUsername(id string, username string) error {
_, err := repo.db.Exec(
"UPDATE account SET username=$2 WHERE id=$1",
id, username,
)
return err
}
-func (repo *AccountRepositoryPostgres) UpdatePassword(id string, password string) error {
+func (repo *AccountRepositoryPostgres) ChangePassword(id string, password string) error {
_, err := repo.db.Exec(
"UPDATE account SET password=$2 WHERE id=$1",
id, password,
)
return err
}
-func (repo *AccountRepositoryPostgres) UpdateEmail(id string, email string) error {
+func (repo *AccountRepositoryPostgres) ChangeEmail(id string, email string) error {
_, err := repo.db.Exec(
"UPDATE account SET email=$2 WHERE id=$1",
id, email,
@@ -145,7 +147,7 @@ func (repo *AccountRepositoryPostgres) RemoveEmail(id string) error {
_, err := repo.db.Exec("UPDATE account SET email=NULL WHERE id=$1", id)
return err
}
-func (repo *AccountRepositoryPostgres) UpdateAvatarURL(id string, avatarURL string) error {
+func (repo *AccountRepositoryPostgres) ChangeAvatarURL(id string, avatarURL string) error {
_, err := repo.db.Exec(
"UPDATE account SET avatar_url=$2 WHERE id=$1",
id, avatarURL,
@@ -157,6 +159,11 @@ func (repo *AccountRepositoryPostgres) RemoveAvatar(id string) error {
return err
}
+func (repo *AccountRepositoryPostgres) Delete(id string) error {
+ _, err := repo.db.Exec("DELETE FROM account WHERE id=$1", id)
+ return err
+}
+
// Increment the number of account login failure attempts,
// returning the current fail count.
func (repo *AccountRepositoryPostgres) IncrementFails(id string) (int, error) {
@@ -170,13 +177,12 @@ func (repo *AccountRepositoryPostgres) ResetFails(id string) error {
return err
}
-func (repo *AccountRepositoryPostgres) SetLocked(id string, locked bool) error {
- _, err := repo.db.Exec("UPDATE account SET locked = $2 WHERE id=$1", id, locked)
+func (repo *AccountRepositoryPostgres) Lock(id string) error {
+ _, err := repo.db.Exec("UPDATE account SET locked = true WHERE id=$1", id)
return err
}
-func (repo *AccountRepositoryPostgres) Delete(id string) (string, error) {
- var deletedID string
- err := repo.db.Get(&deletedID, "DELETE FROM account WHERE id=$1", id)
- return deletedID, err
+func (repo *AccountRepositoryPostgres) Unlock(id string) error {
+ _, err := repo.db.Exec("UPDATE account SET locked = false, fail_attempts = 0 WHERE id=$1", id)
+ return err
}
diff --git a/repository/log/postgres.go b/repository/log/postgres.go
index b813017..ba1246a 100644
--- a/repository/log/postgres.go
+++ b/repository/log/postgres.go
@@ -8,9 +8,11 @@ import (
_ "github.com/lib/pq"
)
-type LogRepositoryPostgres struct {
- db *sqlx.DB
-}
+type (
+ LogRepositoryPostgres struct {
+ db *sqlx.DB
+ }
+)
var _ LogRepository = new(LogRepositoryPostgres)
diff --git a/repository/music/artist_memory.go b/repository/music/artist_memory.go
deleted file mode 100644
index ff61eee..0000000
--- a/repository/music/artist_memory.go
+++ /dev/null
@@ -1,135 +0,0 @@
-package music
-
-import (
- "arimelody-web/errors"
- "arimelody-web/model"
- "slices"
-)
-
-func (repo *MusicRepositoryMemory) GetAllArtists() ([]*model.Artist, error) {
- return repo.artists, nil
-}
-func (repo *MusicRepositoryMemory) GetArtistCount() (int, error) {
- return len(repo.artists), nil
-}
-
-func (repo *MusicRepositoryMemory) GetArtistByID(id string) (*model.Artist, error) {
- index := slices.IndexFunc(repo.artists, func(artist *model.Artist) bool {
- return artist.ID == id
- })
- if index == -1 { return nil, nil }
- return repo.artists[index], nil
-}
-
-func (repo *MusicRepositoryMemory) GetArtistsNotOnRelease(releaseID string) ([]*model.Artist, error) {
- release, err := repo.GetReleaseByID(releaseID)
- if err != nil { return nil, err }
- artists, err := repo.GetAllArtists()
- if err != nil { return nil, err }
-
- artistsNotOnRelease := []*model.Artist{}
- for _, artist := range artists {
- if !slices.ContainsFunc(release.Credits, func(credit *model.Credit) bool {
- return credit.Artist.ID == artist.ID
- }) {
- artistsNotOnRelease = append(artistsNotOnRelease, artist)
- }
- }
-
- return artistsNotOnRelease, nil
-}
-
-func (repo *MusicRepositoryMemory) GetArtistCredits(artistID string, showHidden bool) ([]*model.Credit, error) {
- releases, err := repo.GetAllReleases(!showHidden, 0)
- if err != nil { return nil, err }
-
- credits := []*model.Credit{}
- for _, release := range releases {
- credits = append(credits, slices.DeleteFunc(
- release.Credits,
- func(credit *model.Credit) bool {
- return credit.Artist.ID != artistID
- },
- )...)
- }
-
- return credits, nil
-}
-
-func (repo *MusicRepositoryMemory) CreateArtist(
- id string,
- name string,
- website string,
- avatar string,
-) error {
- if artist, err := repo.GetArtistByID(id); err != nil {
- if !errors.IsNotExistError(err) { return err }
- } else {
- if artist != nil { return errors.NewValidationError("Artist with this ID already exists") }
- }
-
- repo.artists = append(repo.artists, &model.Artist{
- ID: id,
- Name: name,
- Website: website,
- Avatar: avatar,
- })
- return nil
-}
-
-func (repo *MusicRepositoryMemory) UpdateArtist(artist *model.Artist) error {
- repoArtist, err := repo.GetArtistByID(artist.ID)
- if err != nil { return err }
- if repoArtist == nil { return errors.NewNotExistError("Artist does not exist") }
-
- repoArtist.Name = artist.Name
- repoArtist.Website = artist.Website
- repoArtist.Avatar = artist.Avatar
- return nil
-}
-func (repo *MusicRepositoryMemory) UpdateArtistID(oldID string, newID string) error {
- artist, err := repo.GetArtistByID(oldID)
- if err != nil { return err }
- if artist == nil { return errors.NewNotExistError("Artist does not exist") }
-
- artist.ID = newID
- return nil
-}
-func (repo *MusicRepositoryMemory) UpdateArtistName(id string, name string) error {
- artist, err := repo.GetArtistByID(id)
- if err != nil { return err }
- if artist == nil { return errors.NewNotExistError("Artist does not exist") }
-
- artist.Name = name
- return nil
-}
-func (repo *MusicRepositoryMemory) UpdateArtistWebsite(id string, website string) error {
- artist, err := repo.GetArtistByID(id)
- if err != nil { return err }
- if artist == nil { return errors.NewNotExistError("Artist does not exist") }
-
- artist.Website = website
- return nil
-}
-func (repo *MusicRepositoryMemory) UpdateArtistAvatar(id string, avatar string) error {
- artist, err := repo.GetArtistByID(id)
- if err != nil { return err }
- if artist == nil { return errors.NewNotExistError("Artist does not exist") }
-
- artist.Avatar = avatar
- return nil
-}
-
-func (repo *MusicRepositoryMemory) DeleteArtist(id string) (string, error) {
- var deletedID string
- newArtists := []*model.Artist{}
- for _, artist := range repo.artists {
- if artist.ID == id {
- deletedID = id
- continue
- }
- newArtists = append(newArtists, artist)
- }
- repo.artists = newArtists
- return deletedID, nil
-}
diff --git a/repository/music/interface.go b/repository/music/interface.go
deleted file mode 100644
index 5fdc3ac..0000000
--- a/repository/music/interface.go
+++ /dev/null
@@ -1,110 +0,0 @@
-package music
-
-import (
- "arimelody-web/model"
- "time"
-
- "github.com/jmoiron/sqlx"
-)
-
-type MusicRepository interface {
- // artists
-
- GetAllArtists() ([]*model.Artist, error)
- GetArtistCount() (int, error)
- // Fetches an artist by ID, returning an error if one was encountered.
- // If the artist does not exist, both response fields are nil.
- GetArtistByID(id string) (*model.Artist, error)
- GetArtistsNotOnRelease(releaseID string) ([]*model.Artist, error)
- GetArtistCredits(artistID string, showHidden bool) ([]*model.Credit, error)
-
- CreateArtist(id string, name string, website string, avatar string) error
-
- UpdateArtist(artist *model.Artist) error
- UpdateArtistID(oldID string, newID string) error
- UpdateArtistName(id string, name string) error
- UpdateArtistWebsite(id string, website string) error
- UpdateArtistAvatar(id string, avatar string) error
-
- DeleteArtist(id string) (string, error)
-
- // releases
-
- // Fetch all releases.
- // Filters to visible releases if `onlyVisible = true`.
- // If `limit > 0`, limits the number of results.
- GetAllReleases(onlyVisible bool, limit int) ([]*model.Release, error)
- GetReleaseCount(onlyVisible bool) (int, error)
- GetReleaseByID(id string) (*model.Release, error)
-
- GetReleaseTracks(id string) ([]*model.Track, error)
- GetReleaseCredits(id string) ([]*model.Credit, error)
- GetReleaseLinks(id string) ([]*model.Link, error)
-
- CreateRelease(id string, title string, releaseType model.ReleaseType, releaseDate time.Time, artworkURL string) error
-
- UpdateRelease(release *model.Release) error
- UpdateReleaseID(oldID string, newID string) error
- UpdateReleaseVisibility(id string, visible bool) error
- UpdateReleaseTitle(id string, title string) error
- UpdateReleaseDescription(id string, description string) error
- UpdateReleaseType(id string, releaseType model.ReleaseType) error
- UpdateReleaseDate(id string, releaseDate time.Time) error
- UpdateReleaseArtwork(id string, artwork string) error
- UpdateReleaseBuyInfo(id string, buyName string, buyLink string) error
- UpdateReleaseCopyright(id string, copyright string, url string) error
- UpdateReleaseTracks(id string, newTrackIDs []string) error
- UpdateReleaseCredits(id string, newCredits []*model.Credit) error
- UpdateReleaseLinks(id string, newLinks []*model.Link) error
-
- DeleteRelease(id string) (string, error)
-
- // tracks
-
- GetAllTracks() ([]*model.Track, error)
- GetTrackCount() (int, error)
- GetTrackByID(id string) (*model.Track, error)
- GetOrphanTracks() ([]*model.Track, error)
- GetTracksNotOnRelease(releaseID string) ([]*model.Track, error)
- GetTrackReleases(trackID string) ([]*model.Release, error)
-
- CreateTrack(title string, description string, lyrics string, previewURL string) (string, error)
-
- UpdateTrack(track *model.Track) error
- UpdateTrackTitle(id string, title string) error
- UpdateTrackDescription(id string, description string) error
- UpdateTrackLyrics(id string, lyrics string) error
- UpdateTrackPreviewURL(id string, previewURL string) error
-
- DeleteTrack(id string) (string, error)
-}
-
-type (
- MusicRepositoryPostgres struct {
- db *sqlx.DB
- }
-
- MusicRepositoryMemory struct {
- artists []*model.Artist
- releases []*model.Release
- tracks []*model.Track
- }
-)
-
-var _ MusicRepository = new(MusicRepositoryPostgres)
-func NewMusicRepositoryPostgres(db *sqlx.DB) *MusicRepositoryPostgres {
- return &MusicRepositoryPostgres{ db: db }
-}
-
-var _ MusicRepository = new(MusicRepositoryMemory)
-func NewMusicRepositoryMemory(
- artists []*model.Artist,
- releases []*model.Release,
- tracks []*model.Track,
-) *MusicRepositoryMemory {
- return &MusicRepositoryMemory{
- artists: artists,
- releases: releases,
- tracks: tracks,
- }
-}
diff --git a/repository/music/release_memory.go b/repository/music/release_memory.go
deleted file mode 100644
index 3e99612..0000000
--- a/repository/music/release_memory.go
+++ /dev/null
@@ -1,228 +0,0 @@
-package music
-
-import (
- "arimelody-web/errors"
- "arimelody-web/model"
- "fmt"
- "slices"
- "time"
-)
-
-func (repo *MusicRepositoryMemory) GetAllReleases(onlyVisible bool, limit int) ([]*model.Release, error) {
- releases := []*model.Release{}
- for _, release := range repo.releases {
- if !onlyVisible || release.Visible {
- releases = append(releases, release)
- }
- }
- return releases, nil
-}
-func (repo *MusicRepositoryMemory) GetReleaseCount(onlyVisible bool) (int, error) {
- releaseCount := 0
- for _, release := range repo.releases {
- if !onlyVisible || release.Visible {
- releaseCount++
- }
- }
- return releaseCount, nil
-}
-
-func (repo *MusicRepositoryMemory) GetReleaseByID(id string) (*model.Release, error) {
- index := slices.IndexFunc(repo.releases, func(release *model.Release) bool {
- return release.ID == id
- })
- if index == -1 { return nil, nil }
- return repo.releases[index], nil
-}
-
-func (repo *MusicRepositoryMemory) GetReleaseTracks(id string) ([]*model.Track, error) {
- release, err := repo.GetReleaseByID(id)
- if err != nil { return nil, err }
- return release.Tracks, nil
-}
-
-func (repo *MusicRepositoryMemory) GetReleaseCredits(id string) ([]*model.Credit, error) {
- release, err := repo.GetReleaseByID(id)
- if err != nil { return nil, err }
- return release.Credits, nil
-}
-
-func (repo *MusicRepositoryMemory) GetReleaseLinks(id string) ([]*model.Link, error) {
- release, err := repo.GetReleaseByID(id)
- if err != nil { return nil, err }
- return release.Links, nil
-}
-
-func (repo *MusicRepositoryMemory) CreateRelease(
- id string,
- title string,
- releaseType model.ReleaseType,
- releaseDate time.Time,
- artworkURL string,
-) error {
- if release, err := repo.GetReleaseByID(id); err != nil {
- if !errors.IsNotExistError(err) { return err }
- } else {
- if release != nil { return errors.NewValidationError("Release with this ID already exists") }
- }
-
- repo.releases = append(repo.releases, &model.Release{
- ID: id,
- Title: title,
- ReleaseType: releaseType,
- ReleaseDate: releaseDate,
- Artwork: artworkURL,
- })
- return nil
-}
-
-func (repo *MusicRepositoryMemory) UpdateRelease(release *model.Release) error {
- repoRelease, err := repo.GetReleaseByID(release.ID)
- if err != nil { return err }
- if repoRelease == nil { return errors.NewNotExistError("Release does not exist") }
-
- repoRelease.Visible = release.Visible
- repoRelease.Title = release.Title
- repoRelease.Description = release.Description
- repoRelease.ReleaseType = release.ReleaseType
- repoRelease.ReleaseDate = release.ReleaseDate
- repoRelease.Artwork = release.Artwork
- repoRelease.Buyname = release.Buyname
- repoRelease.Buylink = release.Buylink
- repoRelease.Copyright = release.Copyright
- repoRelease.CopyrightURL = release.CopyrightURL
-
- return nil
-}
-
-func (repo *MusicRepositoryMemory) UpdateReleaseID(oldID string, newID string) error {
- release, err := repo.GetReleaseByID(oldID)
- if err != nil { return err }
- if release == nil { return errors.NewNotExistError("Release does not exist") }
- release.ID = newID
- return nil
-}
-func (repo *MusicRepositoryMemory) UpdateReleaseVisibility(id string, visible bool) error {
- release, err := repo.GetReleaseByID(id)
- if err != nil { return err }
- if release == nil { return errors.NewNotExistError("Release does not exist") }
- release.Visible = visible
- return nil
-}
-func (repo *MusicRepositoryMemory) UpdateReleaseTitle(id string, title string) error {
- release, err := repo.GetReleaseByID(id)
- if err != nil { return err }
- if release == nil { return errors.NewNotExistError("Release does not exist") }
- release.Title = title
- return nil
-}
-func (repo *MusicRepositoryMemory) UpdateReleaseDescription(id string, description string) error {
- release, err := repo.GetReleaseByID(id)
- if err != nil { return err }
- if release == nil { return errors.NewNotExistError("Release does not exist") }
- release.Description = description
- return nil
-}
-func (repo *MusicRepositoryMemory) UpdateReleaseType(id string, releaseType model.ReleaseType) error {
- release, err := repo.GetReleaseByID(id)
- if err != nil { return err }
- if release == nil { return errors.NewNotExistError("Release does not exist") }
- release.ReleaseType = releaseType
- return nil
-}
-func (repo *MusicRepositoryMemory) UpdateReleaseDate(id string, releaseDate time.Time) error {
- release, err := repo.GetReleaseByID(id)
- if err != nil { return err }
- if release == nil { return errors.NewNotExistError("Release does not exist") }
- release.ReleaseDate = releaseDate
- return nil
-}
-func (repo *MusicRepositoryMemory) UpdateReleaseArtwork(id string, artwork string) error {
- release, err := repo.GetReleaseByID(id)
- if err != nil { return err }
- if release == nil { return errors.NewNotExistError("Release does not exist") }
- release.Artwork = artwork
- return nil
-}
-func (repo *MusicRepositoryMemory) UpdateReleaseBuyInfo(id string, buyName string, buyLink string) error {
- release, err := repo.GetReleaseByID(id)
- if err != nil { return err }
- if release == nil { return errors.NewNotExistError("Release does not exist") }
- release.Buyname = buyName
- release.Buylink = buyLink
- return nil
-}
-func (repo *MusicRepositoryMemory) UpdateReleaseCopyright(id string, copyright string, url string) error {
- release, err := repo.GetReleaseByID(id)
- if err != nil { return err }
- if release == nil { return errors.NewNotExistError("Release does not exist") }
- release.Copyright = copyright
- release.CopyrightURL = url
- return nil
-}
-
-func (repo *MusicRepositoryMemory) UpdateReleaseTracks(id string, newTrackIDs []string) error {
- release, err := repo.GetReleaseByID(id)
- if err != nil { return err }
- if release == nil { return errors.NewNotExistError("Release does not exist") }
-
- tracks := []*model.Track{}
- for _, trackID := range newTrackIDs {
- track, err := repo.GetTrackByID(trackID)
- if err != nil {
- if errors.IsNotExistError(err) {
- return errors.NewNotExistError(fmt.Sprintf("Track %s does not exist", trackID))
- }
- return err
- }
- tracks = append(tracks, track)
- }
- release.Tracks = tracks
-
- return nil
-}
-
-func (repo *MusicRepositoryMemory) UpdateReleaseCredits(id string, newCredits []*model.Credit) error {
- release, err := repo.GetReleaseByID(id)
- if err != nil { return err }
- if release == nil { return errors.NewNotExistError("Release does not exist") }
-
- for _, credit := range newCredits {
- if credit.Artist == nil { return errors.NewValidationError("Credit artist cannot be empty") }
- if len(credit.Artist.ID) == 0 { return errors.NewValidationError("Credit artist ID cannot be empty") }
- if artist, err := repo.GetArtistByID(credit.Artist.ID); err != nil {
- return err
- } else if artist == nil {
- return errors.NewNotExistError(fmt.Sprintf("Artist '%s' does not exist", credit.Artist.ID))
- } else {
- credit.Release = release
- credit.Artist = artist
- }
- }
-
- release.Credits = newCredits
-
- return nil
-}
-
-func (repo *MusicRepositoryMemory) UpdateReleaseLinks(id string, newLinks []*model.Link) error {
- release, err := repo.GetReleaseByID(id)
- if err != nil { return err }
- if release == nil { return errors.NewNotExistError("Release does not exist") }
- release.Links = newLinks
- return nil
-}
-
-func (repo *MusicRepositoryMemory) DeleteRelease(id string) (string, error) {
- var deletedID string
- newReleases := []*model.Release{}
- for _, release := range repo.releases {
- if release.ID == id {
- deletedID = id
- continue
- }
- newReleases = append(newReleases, release)
- }
- repo.releases = newReleases
- return deletedID, nil
-}
diff --git a/repository/music/release_postgres.go b/repository/music/release_postgres.go
deleted file mode 100644
index e98a165..0000000
--- a/repository/music/release_postgres.go
+++ /dev/null
@@ -1,303 +0,0 @@
-package music
-
-import (
- "arimelody-web/model"
- "time"
-)
-
-func (repo *MusicRepositoryPostgres) GetAllReleases(onlyVisible bool, limit int) ([]*model.Release, error) {
- var releases = []*model.Release{}
-
- query := "SELECT * FROM musicrelease"
- if onlyVisible {
- query += " WHERE visible=true"
- }
- query += " ORDER BY release_date DESC"
- var err error
- if limit > 0 {
- err = repo.db.Select(&releases, query + " LIMIT $1", limit)
- } else {
- err = repo.db.Select(&releases, query)
- }
- if err != nil {
- return nil, err
- }
-
- return releases, nil
-}
-func (repo *MusicRepositoryPostgres) GetReleaseCount(onlyVisible bool) (int, error) {
- query := "SELECT count(*) FROM musicrelease"
- if onlyVisible {
- query += " WHERE visible=true"
- }
-
- var count int
- err := repo.db.Get(&count, query)
-
- return count, err
-}
-
-func (repo *MusicRepositoryPostgres) GetReleaseByID(id string) (*model.Release, error) {
- var release = model.Release{}
- err := repo.db.Get(&release, "SELECT * FROM musicrelease WHERE id=$1", id)
- if err != nil { return nil, err }
- return &release, nil
-}
-
-func (repo *MusicRepositoryPostgres) GetReleaseTracks(releaseID string) ([]*model.Track, error) {
- var tracks = []*model.Track{}
-
- err := repo.db.Select(&tracks,
- "SELECT musictrack.* FROM musictrack "+
- "JOIN musicreleasetrack ON track=id "+
- "WHERE release=$1 "+
- "ORDER BY number ASC",
- releaseID,
- )
- if err != nil {
- return nil, err
- }
-
- return tracks, nil
-}
-
-func (repo *MusicRepositoryPostgres) GetReleaseCredits(releaseID string) ([]*model.Credit, error) {
- rows, err := repo.db.Query(
- "SELECT artist.id,artist.name,artist.website,artist.avatar,role,is_primary "+
- "FROM musiccredit "+
- "JOIN artist ON artist=artist.id "+
- "JOIN musicrelease ON release=musicrelease.id "+
- "WHERE musicrelease.id=$1 "+
- "ORDER BY is_primary DESC",
- releaseID,
- )
- if err != nil {
- return nil, err
- }
-
- var credits []*model.Credit
- for rows.Next() {
- credit := &model.Credit{
- Artist: &model.Artist{},
- }
- rows.Scan(
- &credit.Artist.ID,
- &credit.Artist.Name,
- &credit.Artist.Website,
- &credit.Artist.Avatar,
- &credit.Role,
- &credit.Primary)
- credits = append(credits, credit)
- }
-
- return credits, nil
-}
-
-func (repo *MusicRepositoryPostgres) GetReleaseLinks(releaseID string) ([]*model.Link, error) {
- var links = []*model.Link{}
-
- err := repo.db.Select(&links, "SELECT name,url FROM musiclink WHERE release=$1", releaseID)
- if err != nil {
- return nil, err
- }
-
- return links, nil
-}
-
-func (repo *MusicRepositoryPostgres) CreateRelease(
- id string,
- title string,
- releaseType model.ReleaseType,
- releaseDate time.Time,
- artworkURL string,
-) error {
- _, err := repo.db.Exec(
- "INSERT INTO musicrelease "+
- "(id, title, type, release_date, artwork) "+
- "VALUES ($1, $2, $3, $4, $5)",
- id,
- title,
- releaseType,
- releaseDate.Format("2006-01-02 15:04:05"),
- artworkURL,
- )
- if err != nil {
- return err
- }
-
- return nil
-}
-
-func (repo *MusicRepositoryPostgres) UpdateRelease(release *model.Release) error {
- _, err := repo.db.Exec(
- "UPDATE musicrelease SET "+
- "visible=$2, title=$3, description=$4, type=$5, release_date=$6, artwork=$7, buyname=$8, buylink=$9, copyright=$10, copyrighturl=$11 "+
- "WHERE id=$1",
- release.ID,
- release.Visible,
- release.Title,
- release.Description,
- release.ReleaseType,
- release.ReleaseDate.Format("2006-01-02 15:04:05"),
- release.Artwork,
- release.Buyname,
- release.Buylink,
- release.Copyright,
- release.CopyrightURL,
- )
- if err != nil {
- return err
- }
-
- return nil
-}
-
-func (repo *MusicRepositoryPostgres) UpdateReleaseID(oldID string, newID string) error {
- _, err := repo.db.Exec("UPDATE musicrelease SET id=$2 WHERE id=$1", oldID, newID)
- return err
-}
-
-func (repo *MusicRepositoryPostgres) UpdateReleaseVisibility(id string, visible bool) error {
- _, err := repo.db.Exec("UPDATE musicrelease SET visible=$2 WHERE id=$1", id, visible)
- return err
-}
-func (repo *MusicRepositoryPostgres) UpdateReleaseTitle(id string, title string) error {
- _, err := repo.db.Exec("UPDATE musicrelease SET title=$2 WHERE id=$1", id, title)
- return err
-}
-func (repo *MusicRepositoryPostgres) UpdateReleaseDescription(id string, description string) error {
- _, err := repo.db.Exec("UPDATE musicrelease SET description=$2 WHERE id=$1", id, description)
- return err
-}
-func (repo *MusicRepositoryPostgres) UpdateReleaseType(id string, releaseType model.ReleaseType) error {
- _, err := repo.db.Exec("UPDATE musicrelease SET type=$2 WHERE id=$1", id, releaseType)
- return err
-}
-func (repo *MusicRepositoryPostgres) UpdateReleaseDate(id string, releaseDate time.Time) error {
- _, err := repo.db.Exec(
- "UPDATE musicrelease SET release_date=$2 WHERE id=$1",
- id,
- releaseDate.Format("2006-01-02 15:04:05"),
- )
- return err
-}
-func (repo *MusicRepositoryPostgres) UpdateReleaseArtwork(id string, artwork string) error {
- _, err := repo.db.Exec("UPDATE musicrelease SET artwork=$2 WHERE id=$1", id, artwork)
- return err
-}
-func (repo *MusicRepositoryPostgres) UpdateReleaseBuyInfo(id string, buyName string, buyLink string) error {
- _, err := repo.db.Exec(
- "UPDATE musicrelease SET buyname=$2,buylink=$3 WHERE id=$1",
- id, buyName, buyLink,
- )
- return err
-}
-func (repo *MusicRepositoryPostgres) UpdateReleaseCopyright(id string, copyright string, url string) error {
- _, err := repo.db.Exec(
- "UPDATE musicrelease SET copyright=$2,copyrighturl=$3 WHERE id=$1",
- id, copyright, url,
- )
- return err
-}
-
-func (repo *MusicRepositoryPostgres) UpdateReleaseTracks(releaseID string, newTrackIDs []string) error {
- tx, err := repo.db.Begin()
- if err != nil {
- return err
- }
-
- _, err = tx.Exec("DELETE FROM musicreleasetrack WHERE release=$1", releaseID)
- if err != nil {
- return err
- }
- for i, trackID := range newTrackIDs {
- _, err = tx.Exec(
- "INSERT INTO musicreleasetrack "+
- "(release, track, number) "+
- "VALUES ($1, $2, $3)",
- releaseID,
- trackID,
- i)
- if err != nil {
- return err
- }
- }
-
- err = tx.Commit()
- if err != nil {
- return err
- }
-
- return nil
-}
-
-func (repo *MusicRepositoryPostgres) UpdateReleaseCredits(releaseID string, newCredits []*model.Credit) error {
- tx, err := repo.db.Begin()
- if err != nil {
- return err
- }
-
- _, err = tx.Exec("DELETE FROM musiccredit WHERE release=$1", releaseID)
- if err != nil {
- return err
- }
- for _, credit := range newCredits {
- _, err = tx.Exec(
- "INSERT INTO musiccredit "+
- "(release, artist, role, is_primary) "+
- "VALUES ($1, $2, $3, $4)",
- releaseID,
- credit.Artist.ID,
- credit.Role,
- credit.Primary,
- )
- if err != nil {
- return err
- }
- }
-
- err = tx.Commit()
- if err != nil {
- return err
- }
-
- return nil
-}
-
-func (repo *MusicRepositoryPostgres) UpdateReleaseLinks(releaseID string, newLinks []*model.Link) error {
- tx, err := repo.db.Begin()
- if err != nil {
- return err
- }
-
- _, err = tx.Exec("DELETE FROM musiclink WHERE release=$1", releaseID)
- if err != nil {
- return err
- }
- for _, link := range newLinks {
- _, err := tx.Exec(
- "INSERT INTO musiclink "+
- "(release, name, url) "+
- "VALUES ($1, $2, $3)",
- releaseID,
- link.Name,
- link.URL,
- )
- if err != nil {
- return err
- }
- }
-
- err = tx.Commit()
- if err != nil {
- return err
- }
-
- return nil
-}
-
-func (repo *MusicRepositoryPostgres) DeleteRelease(id string) (string, error) {
- var deletedID string
- err := repo.db.Get(&deletedID, "DELETE FROM musicrelease WHERE id=$1", id)
- return deletedID, err
-}
diff --git a/repository/music/track_memory.go b/repository/music/track_memory.go
deleted file mode 100644
index 7ad574b..0000000
--- a/repository/music/track_memory.go
+++ /dev/null
@@ -1,123 +0,0 @@
-package music
-
-import (
- "arimelody-web/errors"
- "arimelody-web/model"
- "slices"
- "strconv"
-)
-
-func (repo *MusicRepositoryMemory) GetAllTracks() ([]*model.Track, error) {
- return repo.tracks, nil
-}
-func (repo *MusicRepositoryMemory) GetTrackCount() (int, error) {
- return len(repo.tracks), nil
-}
-
-func (repo *MusicRepositoryMemory) GetTrackByID(id string) (*model.Track, error) {
- index := slices.IndexFunc(repo.tracks, func(track *model.Track) bool {
- return track.ID == id
- })
- if index == -1 { return nil, nil }
- return repo.tracks[index], nil
-}
-
-func (repo *MusicRepositoryMemory) GetOrphanTracks() ([]*model.Track, error) {
- return slices.DeleteFunc(repo.tracks, func(track *model.Track) bool {
- return slices.ContainsFunc(repo.releases, func(release *model.Release) bool {
- return slices.ContainsFunc(release.Tracks, func(releaseTrack *model.Track) bool {
- return releaseTrack.ID == track.ID
- })
- })
- }), nil
-}
-
-func (repo *MusicRepositoryMemory) GetTracksNotOnRelease(releaseID string) ([]*model.Track, error) {
- release, err := repo.GetReleaseByID(releaseID)
- if err != nil { return nil, err }
-
- return slices.DeleteFunc(repo.tracks, func(track *model.Track) bool {
- return slices.ContainsFunc(release.Tracks, func(releaseTrack *model.Track) bool {
- return releaseTrack.ID == track.ID
- })
- }), nil
-}
-
-func (repo *MusicRepositoryMemory) GetTrackReleases(trackID string) ([]*model.Release, error) {
- return slices.DeleteFunc(repo.releases, func(release *model.Release) bool {
- return !slices.ContainsFunc(release.Tracks, func(track *model.Track) bool {
- return track.ID == trackID
- })
- }), nil
-}
-
-func (repo *MusicRepositoryMemory) CreateTrack(
- title string,
- description string,
- lyrics string,
- previewURL string,
-) (string, error) {
- id := strconv.Itoa(len(repo.tracks))
- repo.tracks = append(repo.tracks, &model.Track{
- ID: id,
- Title: title,
- Description: description,
- Lyrics: lyrics,
- PreviewURL: previewURL,
- })
- return id, nil
-}
-
-func (repo *MusicRepositoryMemory) UpdateTrack(track *model.Track) error {
- repoTrack, err := repo.GetTrackByID(track.ID)
- if err != nil { return err }
- if repoTrack == nil { return errors.NewNotExistError("Track does not exist") }
-
- repoTrack.Title = track.Title
- repoTrack.Description = track.Description
- repoTrack.Lyrics = track.Lyrics
- repoTrack.PreviewURL = track.PreviewURL
- return nil
-}
-func (repo *MusicRepositoryMemory) UpdateTrackTitle(id string, title string) error {
- repoTrack, err := repo.GetTrackByID(id)
- if err != nil { return err }
- if repoTrack == nil { return errors.NewNotExistError("Track does not exist") }
- repoTrack.Title = title
- return nil
-}
-func (repo *MusicRepositoryMemory) UpdateTrackDescription(id string, description string) error {
- repoTrack, err := repo.GetTrackByID(id)
- if err != nil { return err }
- if repoTrack == nil { return errors.NewNotExistError("Track does not exist") }
- repoTrack.Description = description
- return nil
-}
-func (repo *MusicRepositoryMemory) UpdateTrackLyrics(id string, lyrics string) error {
- repoTrack, err := repo.GetTrackByID(id)
- if err != nil { return err }
- if repoTrack == nil { return errors.NewNotExistError("Track does not exist") }
- repoTrack.Lyrics = lyrics
- return nil
-}
-func (repo *MusicRepositoryMemory) UpdateTrackPreviewURL(id string, previewURL string) error {
- repoTrack, err := repo.GetTrackByID(id)
- if err != nil { return err }
- if repoTrack == nil { return errors.NewNotExistError("Track does not exist") }
- repoTrack.PreviewURL = previewURL
- return nil
-}
-
-func (repo *MusicRepositoryMemory) DeleteTrack(id string) (string, error) {
- var deletedID string
- newTracks := []*model.Track{}
- for _, track := range repo.tracks {
- if track.ID == id {
- deletedID = id
- continue
- }
- newTracks = append(newTracks, track)
- }
- repo.tracks = newTracks
- return deletedID, nil
-}
diff --git a/repository/music/track_postgres.go b/repository/music/track_postgres.go
deleted file mode 100644
index b0b0946..0000000
--- a/repository/music/track_postgres.go
+++ /dev/null
@@ -1,137 +0,0 @@
-package music
-
-import (
- "arimelody-web/model"
-)
-
-func (repo *MusicRepositoryPostgres) GetAllTracks() ([]*model.Track, error) {
- var tracks = []*model.Track{}
-
- err := repo.db.Select(&tracks, "SELECT * FROM musictrack")
- if err != nil {
- return nil, err
- }
-
- return tracks, nil
-}
-func (repo *MusicRepositoryPostgres) GetTrackCount() (int, error) {
- var count int
- err := repo.db.Get(&count, "SELECT count(*) FROM musictrack")
- return count, err
-}
-
-func (repo *MusicRepositoryPostgres) GetTrackByID(id string) (*model.Track, error) {
- var track = model.Track{}
-
- stmt, _ := repo.db.Preparex("SELECT * FROM musictrack WHERE id=$1")
- err := stmt.Get(&track, id)
- if err != nil {
- return nil, err
- }
- return &track, nil
-}
-
-func (repo *MusicRepositoryPostgres) GetOrphanTracks() ([]*model.Track, error) {
- var tracks = []*model.Track{}
-
- err := repo.db.Select(&tracks, "SELECT * FROM musictrack WHERE id NOT IN (SELECT track FROM musicreleasetrack)")
- if err != nil {
- return nil, err
- }
-
- return tracks, nil
-}
-
-func (repo *MusicRepositoryPostgres) GetTracksNotOnRelease(releaseID string) ([]*model.Track, error) {
- var tracks = []*model.Track{}
-
- err := repo.db.Select(&tracks,
- "SELECT * FROM musictrack "+
- "WHERE id NOT IN "+
- "(SELECT track FROM musicreleasetrack WHERE release=$1)",
- releaseID)
- if err != nil {
- return nil, err
- }
-
- return tracks, nil
-}
-
-func (repo *MusicRepositoryPostgres) GetTrackReleases(trackID string) ([]*model.Release, error) {
- var releases = []*model.Release{}
-
- err := repo.db.Select(&releases,
- "SELECT id,title,type,release_date,artwork,buylink "+
- "FROM musicrelease "+
- "JOIN musicreleasetrack ON release=id "+
- "WHERE track=$1 "+
- "ORDER BY release_date",
- trackID,
- )
- if err != nil { return nil, err }
-
- return releases, nil
-}
-
-func (repo *MusicRepositoryPostgres) CreateTrack(
- title string,
- description string,
- lyrics string,
- previewURL string,
-) (string, error) {
- var trackID string
- err := repo.db.QueryRow(
- "INSERT INTO musictrack (title, description, lyrics, preview_url) "+
- "VALUES ($1, $2, $3, $4) "+
- "RETURNING id",
- title,
- description,
- lyrics,
- previewURL,
- ).Scan(&trackID)
- if err != nil {
- return "", err
- }
-
- return trackID, nil
-}
-
-func (repo *MusicRepositoryPostgres) UpdateTrack(track *model.Track) error {
- _, err := repo.db.Exec(
- "UPDATE musictrack "+
- "SET title=$2, description=$3, lyrics=$4, preview_url=$5 "+
- "WHERE id=$1",
- track.ID,
- track.Title,
- track.Description,
- track.Lyrics,
- track.PreviewURL,
- )
- if err != nil {
- return err
- }
-
- return nil
-}
-func (repo *MusicRepositoryPostgres) UpdateTrackTitle(id string, title string) error {
- _, err := repo.db.Exec("UPDATE musictrack SET title=$2 WHERE id=$1", id, title)
- return err
-}
-func (repo *MusicRepositoryPostgres) UpdateTrackDescription(id string, description string) error {
- _, err := repo.db.Exec("UPDATE musictrack SET description=$2 WHERE id=$1", id, description)
- return err
-}
-func (repo *MusicRepositoryPostgres) UpdateTrackLyrics(id string, lyrics string) error {
- _, err := repo.db.Exec("UPDATE musictrack SET lyrics=$2 WHERE id=$1", id, lyrics)
- return err
-}
-func (repo *MusicRepositoryPostgres) UpdateTrackPreviewURL(id string, previewURL string) error {
- _, err := repo.db.Exec("UPDATE musictrack SET preview_url=$2 WHERE id=$1", id, previewURL)
- return err
-}
-
-func (repo *MusicRepositoryPostgres) DeleteTrack(id string) (string, error) {
- var deletedID string
- err := repo.db.Get(&deletedID, "DELETE FROM musictrack WHERE id=$1 RETURNING id", id)
- return deletedID, err
-}
diff --git a/service/account/account.go b/service/account/account.go
index bf7afd4..730fc8b 100644
--- a/service/account/account.go
+++ b/service/account/account.go
@@ -1,10 +1,9 @@
package account
import (
- "arimelody-web/errors"
"arimelody-web/model"
repository "arimelody-web/repository/account"
- "arimelody-web/service/validator"
+ "errors"
"log"
)
@@ -29,24 +28,15 @@ func (s *AccountService) GetCount() (int, error) {
}
func (s *AccountService) GetByID(id string) (*model.Account, error) {
- account, err := s.repo.GetByID(id)
- if err != nil { return nil, err }
- if account == nil { return nil, errors.NewNotExistError("Account does not exist") }
- return account, nil
+ return s.repo.GetByID(id)
}
func (s *AccountService) GetByUsername(username string) (*model.Account, error) {
- account, err := s.repo.GetByUsername(username)
- if err != nil { return nil, err }
- if account == nil { return nil, errors.NewNotExistError("Account does not exist") }
- return account, nil
+ return s.repo.GetByUsername(username)
}
func (s *AccountService) GetByEmail(email string) (*model.Account, error) {
- account, err := s.repo.GetByEmail(email)
- if err != nil { return nil, err }
- if account == nil { return nil, errors.NewNotExistError("Account does not exist") }
- return account, nil
+ return s.repo.GetByEmail(email)
}
func (s *AccountService) Create(
@@ -55,13 +45,13 @@ func (s *AccountService) Create(
email *string,
avatarURL *string,
) (string, error) {
- if len(username) == 0 { return "", errors.NewValidationError("Username cannot be empty") }
- if !validator.ValidateID(username) { return "", errors.NewValidationError("Username contains invalid characters") }
- if len(password) == 0 { return "", errors.NewValidationError("Password cannot be empty") }
- if email != nil && len(*email) == 0 { return "", errors.NewValidationError("Email cannot be empty") }
-
var id string
var err error
+
+ if len(username) == 0 { return id, errors.New("Username cannot be empty") }
+ if len(password) == 0 { return id, errors.New("Password cannot be empty") }
+ if email != nil && len(*email) == 0 { return id, errors.New("Email cannot be empty") }
+
if id, err = s.repo.Create(username, password, email, avatarURL); err != nil {
return id, err
}
@@ -71,60 +61,58 @@ func (s *AccountService) Create(
}
func (s *AccountService) ChangeUsername(id string, username string) error {
- if len(username) == 0 { return errors.NewValidationError("Username cannot be empty") }
- if !validator.ValidateID(username) { return errors.NewValidationError("Username contains invalid characters") }
- if err := s.repo.UpdateUsername(id, username); err != nil { return err }
+ if len(username) == 0 { return errors.New("Username cannot be empty") }
+ if err := s.repo.ChangeUsername(id, username); err != nil {
+ return err
+ }
+
s.log.Printf("Changed username for %s to '%s'", id, username)
return nil
}
func (s *AccountService) ChangePassword(id string, password string) error {
- if len(password) == 0 { return errors.NewValidationError("Password cannot be empty") }
- if err := s.repo.UpdatePassword(id, password); err != nil { return err }
+ if len(password) == 0 { return errors.New("Password cannot be empty") }
+ if err := s.repo.ChangePassword(id, password); err != nil {
+ return err
+ }
+
s.log.Printf("Changed password for %s", id)
return nil
}
func (s *AccountService) ChangeEmail(id string, email string) error {
if len(email) == 0 { return s.repo.RemoveEmail(id) }
- if err := s.repo.UpdateEmail(id, email); err != nil { return err }
+ if err := s.repo.ChangeEmail(id, email); err != nil {
+ return err
+ }
+
s.log.Printf("Changed email for %s to '%s'", id, email)
return nil
}
func (s *AccountService) ChangeAvatarURL(id string, avatarURL string) error {
if len(avatarURL) == 0 { return s.repo.RemoveAvatar(id) }
- if err := s.repo.UpdateAvatarURL(id, avatarURL); err != nil { return err }
+ if err := s.repo.ChangeAvatarURL(id, avatarURL); err != nil {
+ return err
+ }
+
s.log.Printf("Changed avatar URL for %s to '%s'", id, avatarURL)
return nil
}
-func (s *AccountService) IncrementFails(id string) (int, error) {
- num, err := s.repo.IncrementFails(id)
- if err != nil { return 0, err }
- s.log.Printf("Incremented auth failures for account %s (now %d)", id, num)
- return num, nil
+func (s *AccountService) Delete(accountID string) error {
+ return s.repo.Delete(accountID)
}
-func (s *AccountService) ResetFails(id string) (error) {
- if err := s.repo.ResetFails(id); err != nil { return err }
- s.log.Printf("Reset auth failures for account %s", id)
- return nil
+func (s *AccountService) IncrementFails(accountID string) (int, error) {
+ return s.repo.IncrementFails(accountID)
}
-func (s *AccountService) Lock(id string) error {
- if err := s.repo.SetLocked(id, true); err != nil { return err }
- s.log.Printf("Locked account %s", id)
- return nil
+func (s *AccountService) ResetFails(accountID string) (error) {
+ return s.repo.ResetFails(accountID)
}
-func (s *AccountService) Unlock(id string) error {
- if err := s.repo.SetLocked(id, false); err != nil { return err }
- s.log.Printf("Unlocked account %s", id)
- return nil
+func (s *AccountService) Lock(accountID string) error {
+ return s.repo.Lock(accountID)
}
-func (s *AccountService) Delete(id string) error {
- deletedID, err := s.repo.Delete(id)
- if err != nil { return err }
- if deletedID == "" { return errors.NewNotExistError("Account does not exist") }
- s.log.Printf("Deleted account %s", id)
- return nil
+func (s *AccountService) Unlock(accountID string) error {
+ return s.repo.Unlock(accountID)
}
diff --git a/service/account/account_test.go b/service/account/account_test.go
index 5eec680..6487d64 100644
--- a/service/account/account_test.go
+++ b/service/account/account_test.go
@@ -1,19 +1,16 @@
-package account_test
+package account
import (
"arimelody-web/model"
- repository "arimelody-web/repository/account"
- service "arimelody-web/service/account"
- "arimelody-web/errors"
+ accountRepo "arimelody-web/repository/account"
"log"
"os"
"testing"
-
- "gotest.tools/v3/assert"
+ "gotest.tools/v3/assert"
)
var (
- s *service.AccountService
+ service *AccountService
)
func init() {
@@ -21,8 +18,8 @@ func init() {
if err != nil { panic(err) }
defer devNullFile.Close()
- repo := repository.NewAccountRepositoryMemory(make([]*model.Account, 0))
- s = service.NewAccountService(
+ repo := accountRepo.NewAccountRepositoryMemory()
+ service = NewAccountService(
repo,
log.New(devNullFile, "", model.DEFAULT_LOG_FLAGS),
)
@@ -44,14 +41,14 @@ func Test_Account(t *testing.T) {
username := "testificate"
password := "the amazing digital data breach"
email := "goober@arimelody.space"
- avatarURL := "/img/account-avatar.webp"
+ avatarURL := "/img/default-avatar.webp"
var id string
var err error
t.Run("accounts should start empty", func(t *testing.T) {
t.Run("count is zero", func(t *testing.T) {
- if num, err := s.GetCount(); err != nil {
+ if num, err := service.GetCount(); err != nil {
t.Errorf("Failed to get number of accounts: %v", err)
} else {
assert.Equal(t, num, 0)
@@ -59,8 +56,8 @@ func Test_Account(t *testing.T) {
})
t.Run("service returns empty array", func(t *testing.T) {
- if accounts, err := s.GetAll(); err != nil {
- t.Errorf("Failed to get accounts: %v", err)
+ if accounts, err := service.GetAll(); err != nil {
+ t.Errorf("Failed to get number of accounts: %v", err)
} else {
assert.Equal(t, len(accounts), 0)
}
@@ -68,28 +65,13 @@ func Test_Account(t *testing.T) {
})
t.Run("can create account", func(t *testing.T) {
- id, err = s.Create(username, password, &email, &avatarURL)
+ id, err = service.Create(username, password, &email, &avatarURL)
if err != nil {
t.Errorf("Failed to create account: %v", err)
}
- t.Run("but not with invalid username", func(t *testing.T) {
- if _, err := s.Create("", password, &email, &avatarURL); err == nil {
- t.Error("Could create account with invalid username")
- } else if !errors.IsValidationError(err) {
- t.Error("Error is not validation error")
- }
- })
- t.Run("but not with invalid password", func(t *testing.T) {
- if _, err := s.Create("test-username", "", &email, &avatarURL); err == nil {
- t.Error("Could create account with invalid password")
- } else if !errors.IsValidationError(err) {
- t.Error("Error is not validation error")
- }
- })
-
t.Run("and fetch by ID", func(t *testing.T) {
- account, err := s.GetByID(id)
+ account, err := service.GetByID(id)
if err != nil {
t.Errorf("Failed to get account after creation: %v", err)
}
@@ -102,7 +84,7 @@ func Test_Account(t *testing.T) {
assert.Equal(t, account.Locked, false)
})
t.Run("and fetch by username", func(t *testing.T) {
- account, err := s.GetByUsername(username)
+ account, err := service.GetByUsername(username)
if err != nil {
t.Errorf("Failed to get account after creation: %v", err)
}
@@ -115,7 +97,7 @@ func Test_Account(t *testing.T) {
assert.Equal(t, account.Locked, false)
})
t.Run("and fetch by email", func(t *testing.T) {
- account, err := s.GetByEmail(email)
+ account, err := service.GetByEmail(email)
if err != nil {
t.Errorf("Failed to get account after creation: %v", err)
}
@@ -131,7 +113,7 @@ func Test_Account(t *testing.T) {
t.Run("number of accounts should increment", func(t *testing.T) {
t.Run("count is one", func(t *testing.T) {
- if num, err := s.GetCount(); err != nil {
+ if num, err := service.GetCount(); err != nil {
t.Errorf("Failed to get number of accounts: %v", err)
} else {
assert.Equal(t, num, 1)
@@ -139,8 +121,8 @@ func Test_Account(t *testing.T) {
})
t.Run("service returns array with one account", func(t *testing.T) {
- if accounts, err := s.GetAll(); err != nil {
- t.Errorf("Failed to get accounts: %v", err)
+ if accounts, err := service.GetAll(); err != nil {
+ t.Errorf("Failed to get number of accounts: %v", err)
} else {
assert.Equal(t, len(accounts), 1)
}
@@ -148,7 +130,7 @@ func Test_Account(t *testing.T) {
})
t.Run("can't create duplicate account", func(t *testing.T) {
- _, err := s.Create(username, password, &email, &avatarURL)
+ _, err := service.Create(username, password, &email, &avatarURL)
if err == nil {
t.Error("Duplicate account was created")
}
@@ -156,11 +138,11 @@ func Test_Account(t *testing.T) {
t.Run("can change username", func(t *testing.T) {
testUsername := "some_other_name"
- if err := s.ChangeUsername(id, testUsername); err != nil {
+ if err := service.ChangeUsername(id, testUsername); err != nil {
t.Errorf("Failed to change username: %v", err)
}
- if account, err := s.GetByID(id); err != nil {
+ if account, err := service.GetByID(id); err != nil {
t.Errorf("Failed to get account: %v", err)
} else if account == nil {
t.Error("Account is nil after update")
@@ -169,21 +151,19 @@ func Test_Account(t *testing.T) {
}
t.Run("but not to an invalid value", func(t *testing.T) {
- if err := s.ChangeUsername(id, ""); err == nil {
+ if err := service.ChangeUsername(id, ""); err == nil {
t.Error("Could change username to invalid value")
- } else if !errors.IsValidationError(err) {
- t.Error("Error is not validation error")
}
})
})
t.Run("can change password", func(t *testing.T) {
testPassword := "other more different password"
- if err := s.ChangePassword(id, testPassword); err != nil {
+ if err := service.ChangePassword(id, testPassword); err != nil {
t.Errorf("Failed to change password: %v", err)
}
- if account, err := s.GetByID(id); err != nil {
+ if account, err := service.GetByID(id); err != nil {
t.Errorf("Failed to get account: %v", err)
} else if account == nil {
t.Error("Account is nil after update")
@@ -192,21 +172,19 @@ func Test_Account(t *testing.T) {
}
t.Run("but not to an invalid value", func(t *testing.T) {
- if err := s.ChangePassword(id, ""); err == nil {
+ if err := service.ChangePassword(id, ""); err == nil {
t.Error("Could change password to invalid value")
- } else if !errors.IsValidationError(err) {
- t.Error("Error is not validation error")
}
})
})
t.Run("can change email", func(t *testing.T) {
testEmail := "brandnewemail@for.me"
- if err := s.ChangeEmail(id, testEmail); err != nil {
+ if err := service.ChangeEmail(id, testEmail); err != nil {
t.Errorf("Failed to change email: %v", err)
}
- if account, err := s.GetByID(id); err != nil {
+ if account, err := service.GetByID(id); err != nil {
t.Errorf("Failed to get account: %v", err)
} else if account == nil {
t.Error("Account is nil after update")
@@ -216,11 +194,11 @@ func Test_Account(t *testing.T) {
})
t.Run("can remove email", func(t *testing.T) {
- if err := s.ChangeEmail(id, ""); err != nil {
+ if err := service.ChangeEmail(id, ""); err != nil {
t.Errorf("Failed to change email: %v", err)
}
- if account, err := s.GetByID(id); err != nil {
+ if account, err := service.GetByID(id); err != nil {
t.Errorf("Failed to get account: %v", err)
} else if account == nil {
t.Error("Account is nil after update")
@@ -231,11 +209,11 @@ func Test_Account(t *testing.T) {
t.Run("can change avatar URL", func(t *testing.T) {
testAvatarURL := "/img/some-other-avatar.webp"
- if err := s.ChangeAvatarURL(id, testAvatarURL); err != nil {
+ if err := service.ChangeAvatarURL(id, testAvatarURL); err != nil {
t.Errorf("Failed to change avatar URL: %v", err)
}
- if account, err := s.GetByID(id); err != nil {
+ if account, err := service.GetByID(id); err != nil {
t.Errorf("Failed to get account: %v", err)
} else if account == nil {
t.Error("Account is nil after update")
@@ -245,11 +223,11 @@ func Test_Account(t *testing.T) {
})
t.Run("can remove avatar URL", func(t *testing.T) {
- if err := s.ChangeAvatarURL(id, ""); err != nil {
+ if err := service.ChangeAvatarURL(id, ""); err != nil {
t.Errorf("Failed to change avatar URL: %v", err)
}
- if account, err := s.GetByID(id); err != nil {
+ if account, err := service.GetByID(id); err != nil {
t.Errorf("Failed to get account: %v", err)
} else if account == nil {
t.Error("Account is nil after update")
@@ -259,13 +237,13 @@ func Test_Account(t *testing.T) {
})
t.Run("can increment auth failures", func(t *testing.T) {
- if num, err := s.IncrementFails(id); err != nil {
+ if num, err := service.IncrementFails(id); err != nil {
t.Errorf("Failed to increment account auth failures: %v", err)
} else {
assert.Equal(t, num, 1)
}
- if account, err := s.GetByID(id); err != nil {
+ if account, err := service.GetByID(id); err != nil {
t.Errorf("Failed to get account: %v", err)
} else if account == nil {
t.Error("Account is nil after update")
@@ -275,11 +253,11 @@ func Test_Account(t *testing.T) {
})
t.Run("can reset auth failures", func(t *testing.T) {
- if err := s.ResetFails(id); err != nil {
+ if err := service.ResetFails(id); err != nil {
t.Errorf("Failed to reset account auth failures: %v", err)
}
- if account, err := s.GetByID(id); err != nil {
+ if account, err := service.GetByID(id); err != nil {
t.Errorf("Failed to get account: %v", err)
} else if account == nil {
t.Error("Account is nil after update")
@@ -289,11 +267,11 @@ func Test_Account(t *testing.T) {
})
t.Run("can lock account", func(t *testing.T) {
- if err := s.Lock(id); err != nil {
+ if err := service.Lock(id); err != nil {
t.Errorf("Failed to lock account: %v", err)
}
- if account, err := s.GetByID(id); err != nil {
+ if account, err := service.GetByID(id); err != nil {
t.Errorf("Failed to get account: %v", err)
} else if account == nil {
t.Error("Account is nil after update")
@@ -303,11 +281,11 @@ func Test_Account(t *testing.T) {
})
t.Run("can unlock account", func(t *testing.T) {
- if err := s.Unlock(id); err != nil {
+ if err := service.Unlock(id); err != nil {
t.Errorf("Failed to unlock account: %v", err)
}
- if account, err := s.GetByID(id); err != nil {
+ if account, err := service.GetByID(id); err != nil {
t.Errorf("Failed to get account: %v", err)
} else if account == nil {
t.Error("Account is nil after update")
@@ -317,14 +295,12 @@ func Test_Account(t *testing.T) {
})
t.Run("can delete account", func(t *testing.T) {
- if err = s.Delete(id); err != nil {
+ if err = service.Delete(id); err != nil {
t.Errorf("Failed to delete account: %v", err)
}
- if account, err := s.GetByID(id); err != nil {
- if !errors.IsNotExistError(err) {
- t.Errorf("Failed to get account after deletion: %v", err)
- }
+ if account, err := service.GetByID(id); err != nil {
+ t.Errorf("Failed to get account after deletion: %v", err)
} else if account != nil {
t.Error("Account still exists after deletion")
}
@@ -332,76 +308,56 @@ func Test_Account(t *testing.T) {
t.Run("can't create an account with invalid", func(t *testing.T) {
t.Run("username", func(t *testing.T) {
- if _, err := s.Create("", password, &email, &avatarURL); err == nil {
+ if _, err := service.Create("", password, &email, &avatarURL); err == nil {
t.Error("Could create account with empty username")
}
})
t.Run("password", func(t *testing.T) {
- if _, err := s.Create(username, "", &email, &avatarURL); err == nil {
+ if _, err := service.Create(username, "", &email, &avatarURL); err == nil {
t.Error("Could create account with empty password")
}
})
t.Run("email", func(t *testing.T) {
testEmail := ""
- if _, err := s.Create(username, password, &testEmail, &avatarURL); err == nil {
+ if _, err := service.Create(username, password, &testEmail, &avatarURL); err == nil {
t.Error("Could create account with empty (non-nil) email")
}
})
})
- t.Run("can't fetch account that doesn't exist", func(t *testing.T) {
- t.Run("by ID", func(t *testing.T) {
- if account, err := s.GetByID("adsginh534g9405gmb40i9bm"); err != nil {
- if !errors.IsNotExistError(err) { t.Errorf("Failed to get account: %v", err) }
- } else if account != nil {
- t.Error("Could fetch non-existent account")
- }
- })
-
- t.Run("by username", func(t *testing.T) {
- if account, err := s.GetByUsername("adsginh534g9405gmb40i9bm"); err != nil {
- if !errors.IsNotExistError(err) { t.Errorf("Failed to get account: %v", err) }
- } else if account != nil {
- t.Error("Could fetch non-existent account")
- }
- })
-
- t.Run("email", func(t *testing.T) {
- if account, err := s.GetByEmail("adsginh534g9405gmb40i9bm"); err != nil {
- if !errors.IsNotExistError(err) { t.Errorf("Failed to get account: %v", err) }
- } else if account != nil {
- t.Error("Could fetch non-existent account")
- }
- })
- })
-
t.Run("can't update account that doesn't exist", func(t *testing.T) {
garbageAccountID := "adsginh534g9405gmb40i9bm"
t.Run("username", func(t *testing.T) {
- if err := s.ChangeUsername(garbageAccountID, "some-username"); err == nil {
+ if err := service.ChangeUsername(garbageAccountID, "some-username"); err == nil {
t.Error("Could update non-existent account's username")
}
})
t.Run("password", func(t *testing.T) {
- if err := s.ChangePassword(garbageAccountID, "some-password"); err == nil {
+ if err := service.ChangePassword(garbageAccountID, "some-password"); err == nil {
t.Error("Could update non-existent account's password")
}
})
t.Run("email", func(t *testing.T) {
- if err := s.ChangeEmail(garbageAccountID, "some-email@real.gov"); err == nil {
+ if err := service.ChangeEmail(garbageAccountID, "some-email@real.gov"); err == nil {
t.Error("Could update non-existent account's email")
}
})
t.Run("avatar URL", func(t *testing.T) {
- if err := s.ChangeAvatarURL(garbageAccountID, "/img/null.webp"); err == nil {
+ if err := service.ChangeAvatarURL(garbageAccountID, "/img/null.webp"); err == nil {
t.Error("Could update non-existent account's avatar URL")
}
})
})
+
+ t.Cleanup(func() {
+ if err := service.Delete(id); err != nil {
+ t.Errorf("Failed to clean up test case: %v", err)
+ }
+ })
}
diff --git a/service/log/log.go b/service/log/log.go
index bf06b7b..21f1650 100644
--- a/service/log/log.go
+++ b/service/log/log.go
@@ -3,7 +3,6 @@ package log
import (
"arimelody-web/model"
repository "arimelody-web/repository/log"
- "arimelody-web/errors"
"fmt"
"log"
"time"
@@ -44,10 +43,7 @@ func (s *LogService) Warn(logType string, format string, args ...any) {
}
func (s *LogService) Fetch(id string) (*model.Log, error) {
- log, err := s.repo.Get(id)
- if err != nil { return nil, err }
- if log == nil { return nil, errors.NewNotExistError("Log does not exist") }
- return log, nil
+ return s.repo.Get(id)
}
func (s *LogService) Search(
diff --git a/service/music/artist.go b/service/music/artist.go
deleted file mode 100644
index aa643af..0000000
--- a/service/music/artist.go
+++ /dev/null
@@ -1,82 +0,0 @@
-package music
-
-import (
- "arimelody-web/errors"
- "arimelody-web/model"
- "arimelody-web/service/validator"
-)
-
-func (s *MusicService) GetAllArtists() ([]*model.Artist, error) {
- return s.repo.GetAllArtists()
-}
-func (s *MusicService) GetArtistCount() (int, error) {
- return s.repo.GetArtistCount()
-}
-func (s *MusicService) GetArtistByID(id string) (*model.Artist, error) {
- artist, err := s.repo.GetArtistByID(id)
- if err != nil { return nil, err }
- if artist == nil { return nil, errors.NewNotExistError("Artist does not exist") }
- return artist, nil
-}
-func (s *MusicService) GetArtistsNotOnRelease(releaseID string) ([]*model.Artist, error) {
- return s.repo.GetArtistsNotOnRelease(releaseID)
-}
-func (s *MusicService) GetArtistCredits(artistID string, showHidden bool) ([]*model.Credit, error) {
- return s.repo.GetArtistCredits(artistID, showHidden)
-}
-
-func (s *MusicService) CreateArtist(
- id string,
- name string,
- website string,
- avatar string,
-) error {
- if len(id) == 0 { return errors.NewValidationError("Artist ID cannot be empty") }
- if !validator.ValidateID(id) { return errors.NewValidationError("Artist ID contains invalid characters") }
- if len(name) == 0 { return errors.NewValidationError("Artist name cannot be empty") }
-
- if err := s.repo.CreateArtist(id, name, website, avatar); err != nil { return err }
- s.log.Printf("Created new artist '%s' (%s)", name, id)
- return nil
-}
-
-func (s *MusicService) UpdateArtist(artist *model.Artist) error {
- if len(artist.ID) == 0 { return errors.NewValidationError("Artist ID cannot be empty") }
- if !validator.ValidateID(artist.ID) { return errors.NewValidationError("Artist ID contains invalid characters") }
- if len(artist.Name) == 0 { return errors.NewValidationError("Artist name cannot be empty") }
- if err := s.repo.UpdateArtist(artist); err != nil { return err }
- s.log.Printf("Updated artist %s", artist.ID)
- return nil
-}
-
-func (s *MusicService) UpdateArtistID(oldID string, newID string) error {
- if len(newID) == 0 { return errors.NewValidationError("Artist ID cannot be empty") }
- if !validator.ValidateID(newID) { return errors.NewValidationError("Artist ID contains invalid characters") }
- if err := s.repo.UpdateArtistID(oldID, newID); err != nil { return err }
- s.log.Printf("Updated artist ID %s to %s", oldID, newID)
- return nil
-}
-func (s *MusicService) UpdateArtistName(id string, name string) error {
- if len(name) == 0 { return errors.NewValidationError("Artist name cannot be empty") }
- if err := s.repo.UpdateArtistName(id, name); err != nil { return err }
- s.log.Printf("Updated artist %s name to %s", id, name)
- return nil
-}
-func (s *MusicService) UpdateArtistWebsite(id string, website string) error {
- if err := s.repo.UpdateArtistWebsite(id, website); err != nil { return err }
- s.log.Printf("Updated artist %s website to %s", id, website)
- return nil
-}
-func (s *MusicService) UpdateArtistAvatar(id string, avatar string) error {
- if err := s.repo.UpdateArtistAvatar(id, avatar); err != nil { return err }
- s.log.Printf("Updated artist %s avatar to %s", id, avatar)
- return nil
-}
-
-func (s *MusicService) DeleteArtist(id string) error {
- deletedID, err := s.repo.DeleteArtist(id)
- if err != nil { return err }
- if deletedID == "" { return errors.NewNotExistError("Artist does not exist") }
- s.log.Printf("Deleted artist %s", id)
- return nil
-}
diff --git a/service/music/artist_test.go b/service/music/artist_test.go
deleted file mode 100644
index 1a59645..0000000
--- a/service/music/artist_test.go
+++ /dev/null
@@ -1,225 +0,0 @@
-package music_test
-
-import (
- "arimelody-web/errors"
- "arimelody-web/model"
- repository "arimelody-web/repository/music"
- service "arimelody-web/service/music"
- "log"
- "os"
- "slices"
- "testing"
- "time"
-
- "gotest.tools/v3/assert"
-)
-
-func Test_Artist(t *testing.T) {
- devNullFile, err := os.OpenFile(os.DevNull, os.O_RDWR, 0666)
- if err != nil { panic(err) }
- defer devNullFile.Close()
-
- repo := repository.NewMusicRepositoryMemory(
- make([]*model.Artist, 0),
- make([]*model.Release, 0),
- make([]*model.Track, 0),
- )
- s = service.NewMusicService(
- repo,
- log.New(devNullFile, "", model.DEFAULT_LOG_FLAGS),
- )
-
- id := "cool-artist"
- name := "Cool Artist"
- website := "artist.arimelody.space"
- avatarURL := "/img/cool-artist.webp"
-
- t.Run("artists should start empty", func(t *testing.T) {
- t.Run("count is zero", func(t *testing.T) {
- if num, err := s.GetArtistCount(); err != nil {
- t.Errorf("Failed to get number of artists: %v", err)
- } else {
- assert.Equal(t, num, 0)
- }
- })
-
- t.Run("service returns empty array", func(t *testing.T) {
- if artists, err := s.GetAllArtists(); err != nil {
- t.Errorf("Failed to get artists: %v", err)
- } else {
- assert.Equal(t, len(artists), 0)
- }
- })
- })
-
- t.Run("can create artist", func(t *testing.T) {
- if err := s.CreateArtist(id, name, website, avatarURL); err != nil {
- t.Errorf("Failed to create artist: %v", err)
- }
-
- t.Run("but not with an invalid ID", func(t *testing.T) {
- if err := s.CreateArtist("", name, website, avatarURL); err == nil {
- t.Error("Created artist with invalid ID")
- }
- })
- t.Run("but not with an invalid name", func(t *testing.T) {
- if err := s.CreateArtist("test-artist", "", website, avatarURL); err == nil {
- t.Error("Created artist with invalid name")
- }
- })
-
- t.Run("and retrieve it", func(t *testing.T) {
- repoArtist, err := s.GetArtistByID(id)
- if err != nil { t.Errorf("Failed to get artist: %v", err) }
-
- assert.Equal(t, repoArtist.ID, id)
- assert.Equal(t, repoArtist.Name, name)
- assert.Equal(t, repoArtist.Website, website)
- assert.Equal(t, repoArtist.Avatar, avatarURL)
- })
- })
-
- t.Run("number of artists should increment", func(t *testing.T) {
- t.Run("count is one", func(t *testing.T) {
- if num, err := s.GetArtistCount(); err != nil {
- t.Errorf("Failed to get number of artists: %v", err)
- } else {
- assert.Equal(t, num, 1)
- }
- })
-
- t.Run("service returns array with one artist", func(t *testing.T) {
- if artists, err := s.GetAllArtists(); err != nil {
- t.Errorf("Failed to get artists: %v", err)
- } else {
- assert.Equal(t, len(artists), 1)
- }
- })
- })
-
- releaseID := "test-release"
- t.Run("can get artists not on a release", func(t *testing.T) {
- if err := s.CreateRelease(releaseID, "test", string(model.Single), time.Now(), ""); err != nil {
- t.Errorf("Failed to create release: %v", err)
- }
-
- artists, err := s.GetArtistsNotOnRelease(releaseID)
- if err != nil { t.Errorf("Failed to get artists: %v", err) }
-
- assert.Assert(t, slices.ContainsFunc(artists, func(artist *model.Artist) bool {
- return artist.ID == id
- }))
- })
-
- credit := &model.Credit{
- Artist: &model.Artist{ ID: id, Name: name, Website: website, Avatar: avatarURL },
- Role: "did a lot of stuff",
- Primary: true,
- }
- t.Run("can get artist credits", func(t *testing.T) {
- if err := s.UpdateReleaseCredits(releaseID, []*model.Credit{ credit }); err != nil {
- t.Errorf("Failed to update release credits: %v", err)
- }
-
- credits, err := s.GetArtistCredits(id, true)
- if err != nil { t.Errorf("Failed to get credits: %v", err) }
-
- index := slices.IndexFunc(credits, func(credit *model.Credit) bool {
- return credit.Artist.ID == id
- })
- assert.Assert(t, index != -1)
- assert.Equal(t, credits[index].Artist.ID, id)
- assert.Equal(t, credits[index].Artist.Name, name)
- assert.Equal(t, credits[index].Artist.Website, website)
- assert.Equal(t, credits[index].Artist.Avatar, avatarURL)
- assert.Equal(t, credits[index].Role, credit.Role)
- assert.Equal(t, credits[index].Primary, credit.Primary)
- })
-
- t.Run("can update artist", func(t *testing.T) {
- testName := "this name is only temporary"
- testWebsite := "https://test.example.org"
- testAvatar := "/img/test-avatar.webp"
- if err := s.UpdateArtist(&model.Artist{
- ID: id,
- Name: testName,
- Website: testWebsite,
- Avatar: testAvatar,
- }); err != nil {
- t.Errorf("Failed to update artist: %v", err)
- }
-
- artist, err := s.GetArtistByID(id)
- if err != nil { t.Errorf("Failed to get artist: %v", err) }
- assert.Equal(t, artist.Name, testName)
- assert.Equal(t, artist.Website, testWebsite)
- assert.Equal(t, artist.GetAvatar(), testAvatar)
-
- t.Run("but not with invalid name", func(t *testing.T) {
- if err := s.UpdateArtist(&model.Artist{
- ID: id,
- Name: "",
- }); err == nil {
- t.Errorf("Updated artist name to invalid value")
- }
- })
- })
-
- t.Run("can update artist name", func(t *testing.T) {
- if err := s.UpdateArtistName(id, name); err != nil {
- t.Errorf("Failed to update artist: %v", err)
- }
-
- artist, err := s.GetArtistByID(id)
- if err != nil { t.Errorf("Failed to get artist: %v", err) }
- assert.Equal(t, artist.Name, name)
-
- t.Run("but not with invalid value", func(t *testing.T) {
- if err := s.UpdateArtistName(id, ""); err == nil {
- t.Errorf("Updated artist name to invalid value")
- }
- })
- })
-
- t.Run("can update artist website", func(t *testing.T) {
- if err := s.UpdateArtistWebsite(id, website); err != nil {
- t.Errorf("Failed to update artist: %v", err)
- }
-
- artist, err := s.GetArtistByID(id)
- if err != nil { t.Errorf("Failed to get artist: %v", err) }
- assert.Equal(t, artist.Website, website)
- })
-
- t.Run("can update artist avatar", func(t *testing.T) {
- if err := s.UpdateArtistAvatar(id, ""); err != nil {
- t.Errorf("Failed to update artist: %v", err)
- }
-
- artist, err := s.GetArtistByID(id)
- if err != nil { t.Errorf("Failed to get artist: %v", err) }
- assert.Equal(t, artist.GetAvatar(), model.DEFAULT_AVATAR_URL)
- })
-
- t.Run("can delete artist", func(t *testing.T) {
- if err := s.DeleteArtist(id); err != nil {
- t.Errorf("Failed to delete artist: %v", err)
- }
-
- t.Run("no longer exists", func(t *testing.T) {
- if artist, err := s.GetArtistByID(id); err == nil {
- t.Error("No error getting artist")
- } else if artist != nil {
- t.Error("Artist with this ID still exists")
- }
- })
-
- t.Run("but not one that doesn't exist", func(t *testing.T) {
- if err := s.DeleteArtist("some-garbage-id"); err == nil {
- if !errors.IsNotExistError(err) {
- t.Errorf("Failed to delete artist: %v", err)
- }
- }
- })
- })
-}
diff --git a/service/music/music.go b/service/music/music.go
deleted file mode 100644
index f3037cc..0000000
--- a/service/music/music.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package music
-
-import (
- repository "arimelody-web/repository/music"
- "log"
-)
-
-type MusicService struct {
- repo repository.MusicRepository
- log *log.Logger
-}
-
-func NewMusicService(repo repository.MusicRepository, logger *log.Logger) *MusicService {
- return &MusicService{
- repo: repo,
- log: logger,
- }
-}
diff --git a/service/music/music_test.go b/service/music/music_test.go
deleted file mode 100644
index 72447a4..0000000
--- a/service/music/music_test.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package music_test
-
-import (
- service "arimelody-web/service/music"
-)
-
-var (
- s *service.MusicService
-)
diff --git a/service/music/release.go b/service/music/release.go
deleted file mode 100644
index 494477a..0000000
--- a/service/music/release.go
+++ /dev/null
@@ -1,180 +0,0 @@
-package music
-
-import (
- "arimelody-web/errors"
- "arimelody-web/model"
- "arimelody-web/service/validator"
- "fmt"
- "time"
-)
-
-// Hydrates a release with its additional data (credits, tracks, links)
-func (s *MusicService) fillRelease(release *model.Release) error {
- credits, err := s.GetReleaseCredits(release.ID)
- if err != nil { return fmt.Errorf("credits: %s", err) }
- release.Credits = append(release.Credits, credits...)
-
- tracks, err := s.GetReleaseTracks(release.ID)
- if err != nil { return fmt.Errorf("tracks: %s", err) }
- release.Tracks = append(release.Tracks, tracks...)
-
- links, err := s.GetReleaseLinks(release.ID)
- if err != nil { return fmt.Errorf("links: %s", err) }
- release.Links = append(release.Links, links...)
-
- return nil
-}
-
-func (s *MusicService) GetAllReleases(onlyVisible bool, limit int) ([]*model.Release, error) {
- return s.repo.GetAllReleases(onlyVisible, limit)
-}
-
-func (s *MusicService) GetAllFullReleases(onlyVisible bool, limit int) ([]*model.Release, error) {
- releases, err := s.repo.GetAllReleases(onlyVisible, limit)
- if err != nil { return nil, err }
- for _, release := range releases {
- if err := s.fillRelease(release); err != nil { return nil, err }
- }
- return releases, nil
-}
-
-func (s *MusicService) GetReleaseCount(onlyVisible bool) (int, error) {
- return s.repo.GetReleaseCount(onlyVisible)
-}
-
-func (s *MusicService) GetReleaseByID(id string) (*model.Release, error) {
- release, err := s.repo.GetReleaseByID(id)
- if err != nil { return nil, err }
- if release == nil { return nil, errors.NewNotExistError("Release does not exist") }
- return release, nil
-}
-
-func (s *MusicService) GetFullReleaseByID(id string) (*model.Release, error) {
- release, err := s.GetReleaseByID(id)
- if err != nil { return nil, err }
- if err := s.fillRelease(release); err != nil { return nil, err }
- return release, nil
-}
-
-func (s *MusicService) GetReleaseTracks(id string) ([]*model.Track, error) {
- return s.repo.GetReleaseTracks(id)
-}
-func (s *MusicService) GetReleaseCredits(id string) ([]*model.Credit, error) {
- return s.repo.GetReleaseCredits(id)
-}
-func (s *MusicService) GetReleaseLinks(id string) ([]*model.Link, error) {
- return s.repo.GetReleaseLinks(id)
-}
-
-func (s *MusicService) CreateRelease(
- id string,
- title string,
- releaseType string,
- releaseDate time.Time,
- artworkURL string,
-) error {
- if len(id) == 0 { return errors.NewValidationError("Release ID cannot be empty") }
- if !validator.ValidateID(id) { return errors.NewValidationError("Release ID contains invalid characters") }
- if len(title) == 0 { return errors.NewValidationError("Release title cannot be empty") }
- validReleaseType, ok := model.ValidReleaseType(releaseType)
- if !ok { return errors.NewValidationError("Invalid release type") }
-
- if err := s.repo.CreateRelease(id, title, validReleaseType, releaseDate, artworkURL); err != nil { return err }
- s.log.Printf("Created new release '%s' (%s)", title, id)
- return nil
-}
-
-func (s *MusicService) UpdateRelease(release *model.Release) error {
- if len(release.ID) == 0 { return errors.NewValidationError("Release ID cannot be empty") }
- if !validator.ValidateID(release.ID) { return errors.NewValidationError("Release ID contains invalid characters") }
- if len(release.Title) == 0 { return errors.NewValidationError("Release title cannot be empty") }
-
- if err := s.repo.UpdateRelease(release); err != nil { return err }
- s.log.Printf("Updated release '%s' (%s)", release.Title, release.ID)
- return nil
-}
-func (s *MusicService) UpdateReleaseID(oldID string, newID string) error {
- if len(newID) == 0 { return errors.NewValidationError("Release ID cannot be empty") }
- if !validator.ValidateID(newID) { return errors.NewValidationError("Release ID contains invalid characters") }
- if err := s.repo.UpdateReleaseID(oldID, newID); err != nil { return err }
- s.log.Printf("Updated release ID %s to %s", oldID, newID)
- return nil
-}
-func (s *MusicService) UpdateReleaseVisibility(id string, visible bool) error {
- if err := s.repo.UpdateReleaseVisibility(id, visible); err != nil { return err }
- s.log.Printf("Updated release '%s' visibility to %t", id, visible)
- return nil
-}
-func (s *MusicService) UpdateReleaseTitle(id string, title string) error {
- if len(title) == 0 { return errors.NewValidationError("Release title cannot be empty") }
- if err := s.repo.UpdateReleaseTitle(id, title); err != nil { return err }
- s.log.Printf("Updated release '%s' title to %s", id, title)
- return nil
-}
-func (s *MusicService) UpdateReleaseDescription(id string, description string) error {
- if err := s.repo.UpdateReleaseDescription(id, description); err != nil { return err }
- s.log.Printf("Updated release '%s' description to %s", id, description)
- return nil
-}
-func (s *MusicService) UpdateReleaseType(id string, releaseType string) error {
- validReleaseType, ok := model.ValidReleaseType(releaseType)
- if !ok { return errors.NewValidationError("Invalid release type") }
- if err := s.repo.UpdateReleaseType(id, validReleaseType); err != nil { return err }
- s.log.Printf("Updated release '%s' type to %s", id, releaseType)
- return nil
-}
-func (s *MusicService) UpdateReleaseDate(id string, releaseDate time.Time) error {
- if err := s.repo.UpdateReleaseDate(id, releaseDate); err != nil { return err }
- s.log.Printf("Updated release '%s' date to %s", id, releaseDate.Format(time.RFC3339))
- return nil
-}
-func (s *MusicService) UpdateReleaseArtwork(id string, artwork string) error {
- if err := s.repo.UpdateReleaseArtwork(id, artwork); err != nil { return err }
- s.log.Printf("Updated release '%s' artwork to %s", id, artwork)
- return nil
-}
-func (s *MusicService) UpdateReleaseBuyInfo(id string, buyName string, buyLink string) error {
- if err := s.repo.UpdateReleaseBuyInfo(id, buyName, buyLink); err != nil { return err }
- s.log.Printf("Updated release '%s' buy info (name='%s', link='%s')", id, buyName, buyLink)
- return nil
-}
-func (s *MusicService) UpdateReleaseCopyright(id string, copyright string, url string) error {
- if err := s.repo.UpdateReleaseCopyright(id, copyright, url); err != nil { return err }
- s.log.Printf("Updated release '%s' copyright (copyright='%s', url='%s')", id, copyright, url)
- return nil
-}
-
-func (s *MusicService) UpdateReleaseTracks(id string, newTrackIDs []string) error {
- for _, id := range newTrackIDs {
- if len(id) == 0 { return errors.NewValidationError("Track IDs cannot be empty") }
- }
-
- if err := s.repo.UpdateReleaseTracks(id, newTrackIDs); err != nil { return err }
- s.log.Printf("Updated release '%s' tracks (%d tracks)", id, len(newTrackIDs))
- return nil
-}
-
-func (s *MusicService) UpdateReleaseCredits(id string, newCredits []*model.Credit) error {
- if err := s.repo.UpdateReleaseCredits(id, newCredits); err != nil { return err }
- s.log.Printf("Updated release '%s' credits (%d credits)", id, len(newCredits))
- return nil
-}
-
-func (s *MusicService) UpdateReleaseLinks(id string, newLinks []*model.Link) error {
- for _, link := range newLinks {
- if len(link.Name) == 0 { return errors.NewValidationError("Link names cannot be empty") }
- if len(link.URL) == 0 { return errors.NewValidationError("Link URLs cannot be empty") }
- }
-
- if err := s.repo.UpdateReleaseLinks(id, newLinks); err != nil { return err }
- s.log.Printf("Updated release '%s' links (%d links)", id, len(newLinks))
- return nil
-}
-
-func (s *MusicService) DeleteRelease(id string) error {
- deletedID, err := s.repo.DeleteRelease(id)
- if err != nil { return err }
- if deletedID == "" { return errors.NewNotExistError("Release does not exist") }
- s.log.Printf("Deleted release '%s'", id)
- return nil
-}
diff --git a/service/music/release_test.go b/service/music/release_test.go
deleted file mode 100644
index d14a697..0000000
--- a/service/music/release_test.go
+++ /dev/null
@@ -1,374 +0,0 @@
-package music_test
-
-import (
- "arimelody-web/errors"
- "arimelody-web/model"
- repository "arimelody-web/repository/music"
- service "arimelody-web/service/music"
- "log"
- "os"
- "slices"
- "testing"
- "time"
-
- "gotest.tools/v3/assert"
-)
-
-func Test_Release(t *testing.T) {
- devNullFile, err := os.OpenFile(os.DevNull, os.O_RDWR, 0666)
- if err != nil { panic(err) }
- defer devNullFile.Close()
-
- repo := repository.NewMusicRepositoryMemory(
- make([]*model.Artist, 0),
- make([]*model.Release, 0),
- make([]*model.Track, 0),
- )
- s = service.NewMusicService(
- repo,
- log.New(devNullFile, "", model.DEFAULT_LOG_FLAGS),
- )
-
- id := "cool-release"
- title := "Cool Release"
- releaseType := model.Album
- releaseDate := time.Now()
- artworkURL := "/img/some-cool-artwork.webp"
-
- t.Run("releases should start empty", func(t *testing.T) {
- t.Run("count is zero", func(t *testing.T) {
- if num, err := s.GetReleaseCount(false); err != nil {
- t.Errorf("Failed to get number of releases: %v", err)
- } else {
- assert.Equal(t, num, 0)
- }
- })
-
- t.Run("service returns empty array", func(t *testing.T) {
- if releases, err := s.GetAllReleases(false, 0); err != nil {
- t.Errorf("Failed to get releases: %v", err)
- } else {
- assert.Equal(t, len(releases), 0)
- }
- })
- })
-
- t.Run("can create release", func(t *testing.T) {
- if err := s.CreateRelease(id, title, string(releaseType), releaseDate, artworkURL); err != nil {
- t.Errorf("Failed to create release: %v", err)
- }
-
- t.Run("but not with an invalid ID", func(t *testing.T) {
- if err := s.CreateRelease("", title, string(releaseType), releaseDate, artworkURL); err == nil {
- t.Error("Created release with invalid ID")
- }
- })
- t.Run("but not with an invalid title", func(t *testing.T) {
- if err := s.CreateRelease("test-release", "", string(releaseType), releaseDate, artworkURL); err == nil {
- t.Error("Created release with invalid title")
- }
- })
- t.Run("but not with an invalid type", func(t *testing.T) {
- if err := s.CreateRelease("test-release", "", "garbage-type", releaseDate, artworkURL); err == nil {
- t.Error("Created release with invalid type")
- }
- })
-
- t.Run("and retrieve it", func(t *testing.T) {
- repoRelease, err := s.GetReleaseByID(id)
- if err != nil { t.Errorf("Failed to get release: %v", err) }
- if repoRelease == nil { t.Error("Release does not exist") }
-
- assert.Equal(t, repoRelease.ID, id)
- assert.Equal(t, repoRelease.Title, title)
- assert.Equal(t, repoRelease.ReleaseType, releaseType)
- assert.Equal(t, repoRelease.ReleaseDate, releaseDate)
- assert.Equal(t, repoRelease.GetArtwork(), artworkURL)
- })
-
- t.Run("should not be visible by default", func(t *testing.T) {
- releases, err := s.GetAllReleases(true, 0)
- if err != nil { t.Errorf("Failed to get releases: %v", err) }
-
- assert.Equal(t, slices.ContainsFunc(releases, func(release *model.Release) bool {
- return release.ID == id
- }), false)
- })
- })
-
- t.Run("number of releases should increment", func(t *testing.T) {
- t.Run("count is one", func(t *testing.T) {
- if num, err := s.GetReleaseCount(false); err != nil {
- t.Errorf("Failed to get number of releases: %v", err)
- } else {
- assert.Equal(t, num, 1)
- }
- })
-
- t.Run("service returns array with one release", func(t *testing.T) {
- if releases, err := s.GetAllReleases(false, 0); err != nil {
- t.Errorf("Failed to get releases: %v", err)
- } else {
- assert.Equal(t, len(releases), 1)
- }
- })
- })
-
- t.Run("can update release", func(t *testing.T) {
- testTitle := "this title is only temporary"
- testReleaseType := model.Compilation
- testReleaseDate := time.Now().AddDate(0, 0, 10)
- testArtworkURL := "/img/test-artwork.webp"
- if err := s.UpdateRelease(&model.Release{
- ID: id,
- Title: testTitle,
- ReleaseType: testReleaseType,
- ReleaseDate: testReleaseDate,
- Artwork: testArtworkURL,
- }); err != nil {
- t.Errorf("Failed to update release: %v", err)
- }
-
- release, err := s.GetReleaseByID(id)
- if err != nil { t.Errorf("Failed to get release: %v", err) }
- if release == nil { t.Error("Release does not exist after update") }
- assert.Equal(t, release.Title, testTitle)
- assert.Equal(t, release.ReleaseType, testReleaseType)
- assert.Equal(t, release.ReleaseDate, testReleaseDate)
- assert.Equal(t, release.GetArtwork(), testArtworkURL)
-
- t.Run("but not with invalid title", func(t *testing.T) {
- if err := s.UpdateRelease(&model.Release{
- ID: id,
- Title: "",
- }); err == nil {
- t.Errorf("Updated release title to invalid value")
- }
- })
- })
-
- t.Run("can update visibility", func(t *testing.T) {
- if err := s.UpdateReleaseVisibility(id, true); err != nil {
- t.Errorf("Failed to update release: %v", err)
- }
-
- release, err := s.GetReleaseByID(id)
- if err != nil { t.Errorf("Failed to get release: %v", err) }
- if release == nil { t.Error("Release does not exist after update") }
- assert.Equal(t, release.Visible, true)
-
- t.Run("should be visible after updating", func(t *testing.T) {
- releases, err := s.GetAllReleases(true, 0)
- if err != nil { t.Errorf("Failed to get releases: %v", err) }
-
- assert.Equal(t, slices.ContainsFunc(releases, func(release *model.Release) bool {
- return release.ID == id
- }), true)
- })
- })
-
- t.Run("can update title", func(t *testing.T) {
- if err := s.UpdateReleaseTitle(id, title); err != nil {
- t.Errorf("Failed to update release: %v", err)
- }
-
- release, err := s.GetReleaseByID(id)
- if err != nil { t.Errorf("Failed to get release: %v", err) }
- if release == nil { t.Error("Release does not exist after update") }
- assert.Equal(t, release.Title, title)
-
- t.Run("but not with invalid value", func(t *testing.T) {
- if err := s.UpdateReleaseTitle(id, ""); err == nil {
- t.Errorf("Updated release title to invalid value")
- }
- })
- })
-
- t.Run("can update description", func(t *testing.T) {
- testDescription := "an incredible and thought-provoking description"
- if err := s.UpdateReleaseDescription(id, testDescription); err != nil {
- t.Errorf("Failed to update release: %v", err)
- }
-
- release, err := s.GetReleaseByID(id)
- if err != nil { t.Errorf("Failed to get release: %v", err) }
- if release == nil { t.Error("Release does not exist after update") }
- assert.Equal(t, release.Description, testDescription)
- })
-
- t.Run("can update type", func(t *testing.T) {
- testType := model.EP
- if err := s.UpdateReleaseType(id, string(testType)); err != nil {
- t.Errorf("Failed to update release: %v", err)
- }
-
- release, err := s.GetReleaseByID(id)
- if err != nil { t.Errorf("Failed to get release: %v", err) }
- if release == nil { t.Error("Release does not exist after update") }
- assert.Equal(t, release.ReleaseType, testType)
- releaseType = testType
-
- t.Run("but not with invalid value", func(t *testing.T) {
- if err := s.UpdateReleaseType(id, "garbage-type"); err == nil {
- t.Errorf("Updated release type to invalid value")
- }
- })
- })
-
- t.Run("can update date", func(t *testing.T) {
- testDate := time.Now().Add(time.Hour * 24)
- if err := s.UpdateReleaseDate(id, testDate); err != nil {
- t.Errorf("Failed to update release: %v", err)
- }
-
- release, err := s.GetReleaseByID(id)
- if err != nil { t.Errorf("Failed to get release: %v", err) }
- if release == nil { t.Error("Release does not exist after update") }
- assert.Equal(t, release.ReleaseDate, testDate)
- releaseDate = testDate
- })
-
- t.Run("can update artwork", func(t *testing.T) {
- if err := s.UpdateReleaseArtwork(id, ""); err != nil {
- t.Errorf("Failed to update release: %v", err)
- }
-
- release, err := s.GetReleaseByID(id)
- if err != nil { t.Errorf("Failed to get release: %v", err) }
- if release == nil { t.Error("Release does not exist after update") }
- assert.Equal(t, release.GetArtwork(), model.DEFAULT_RELEASE_ARTWORK_URL)
- })
-
- t.Run("can update buy info", func(t *testing.T) {
- testBuyName := "get it now!!!"
- testBuyLink := "https://arimelody.space/music"
- if err := s.UpdateReleaseBuyInfo(id, testBuyName, testBuyLink); err != nil {
- t.Errorf("Failed to update release: %v", err)
- }
-
- release, err := s.GetReleaseByID(id)
- if err != nil { t.Errorf("Failed to get release: %v", err) }
- if release == nil { t.Error("Release does not exist after update") }
- assert.Equal(t, release.Buyname, testBuyName)
- assert.Equal(t, release.Buylink, testBuyLink)
- })
-
- t.Run("can update copyright info", func(t *testing.T) {
- testCopyright := "CC BY-SA 4.0"
- testCopyrightURL := "https://creativecommons.org/licenses/by-sa/4.0/"
- if err := s.UpdateReleaseCopyright(id, testCopyright, testCopyrightURL); err != nil {
- t.Errorf("Failed to update release: %v", err)
- }
-
- release, err := s.GetReleaseByID(id)
- if err != nil { t.Errorf("Failed to get release: %v", err) }
- if release == nil { t.Error("Release does not exist after update") }
- assert.Equal(t, release.Copyright, testCopyright)
- assert.Equal(t, release.CopyrightURL, testCopyrightURL)
- })
-
- track := &model.Track{
- Title: "test track",
- Description: "average description",
- Lyrics: "some lyrics",
- }
- t.Run("can update tracks", func(t *testing.T) {
- if trackID, err := s.CreateTrack(track.Title, track.Description, track.Lyrics, ""); err != nil {
- t.Errorf("Failed to create track: %v", err)
- } else { track.ID = trackID }
-
- if err := s.UpdateReleaseTracks(id, []string{ track.ID }); err != nil {
- t.Errorf("Failed to update release tracks: %v", err)
- }
-
- tracks, err := s.GetReleaseTracks(id)
- if err != nil { t.Errorf("Failed to get tracks: %v", err) }
-
- index := slices.IndexFunc(tracks, func(repoTrack *model.Track) bool {
- return repoTrack.ID == track.ID
- })
- assert.Assert(t, index != -1)
- assert.Equal(t, tracks[index].Title, track.Title)
- assert.Equal(t, tracks[index].Description, track.Description)
- assert.Equal(t, tracks[index].Lyrics, track.Lyrics)
- })
-
- t.Run("can update credits", func(t *testing.T) {
- artist := &model.Artist{ ID: id, Name: title, Website: "", Avatar: "" }
- if err := s.CreateArtist(artist.ID, artist.Name, artist.Website, artist.Avatar); err != nil {
- t.Errorf("Failed to create artist: %v", err)
- }
-
- credit := &model.Credit{
- Artist: artist,
- Role: "did a lot of stuff",
- Primary: true,
- }
-
- if err := s.UpdateReleaseCredits(id, []*model.Credit{ credit }); err != nil {
- t.Errorf("Failed to update release credits: %v", err)
- }
-
- credits, err := s.GetReleaseCredits(id)
- if err != nil { t.Errorf("Failed to get credits: %v", err) }
- assert.Equal(t, len(credits), 1)
-
- index := slices.IndexFunc(credits, func(repoCredit *model.Credit) bool {
- return repoCredit.Artist.ID == credit.Artist.ID
- })
- assert.Assert(t, index != -1)
- assert.Equal(t, credits[index].Release.ID, id)
- assert.Equal(t, credits[index].Release.Title, title)
- assert.Equal(t, credits[index].Release.ReleaseType, releaseType)
- assert.Equal(t, credits[index].Release.ReleaseDate, releaseDate)
- assert.Equal(t, credits[index].Release.GetArtwork(), model.DEFAULT_RELEASE_ARTWORK_URL)
- assert.Equal(t, credits[index].Artist.ID, credit.Artist.ID)
- assert.Equal(t, credits[index].Artist.Name, credit.Artist.Name)
- assert.Equal(t, credits[index].Artist.Website, credit.Artist.Website)
- assert.Equal(t, credits[index].Artist.GetAvatar(), credit.Artist.GetAvatar())
- assert.Equal(t, credits[index].Role, credit.Role)
- assert.Equal(t, credits[index].Primary, credit.Primary)
- })
-
- t.Run("can update links", func(t *testing.T) {
- link := &model.Link{
- Name: "awesome link you should totally go here",
- URL: "https://arimelody.space",
- }
-
- if err := s.UpdateReleaseLinks(id, []*model.Link{ link }); err != nil {
- t.Errorf("Failed to update release tracks: %v", err)
- }
-
- links, err := s.GetReleaseLinks(id)
- if err != nil { t.Errorf("Failed to get tracks: %v", err) }
-
- index := slices.IndexFunc(links, func(repoLink *model.Link) bool {
- return repoLink.Name == link.Name
- })
- assert.Assert(t, index != -1)
- assert.Equal(t, links[index].URL, link.URL)
- })
-
- t.Run("can delete release", func(t *testing.T) {
- if err := s.DeleteRelease(id); err != nil {
- t.Errorf("Failed to delete release: %v", err)
- }
-
- t.Run("no longer exists", func(t *testing.T) {
- if release, err := s.GetReleaseByID(id); err == nil {
- t.Error("No error getting release")
- } else if release != nil {
- t.Error("Release with this ID still exists")
- }
- })
-
- t.Run("but not one that doesn't exist", func(t *testing.T) {
- if err := s.DeleteRelease("some-garbage-id"); err == nil {
- if !errors.IsNotExistError(err) {
- t.Errorf("Failed to delete release: %v", err)
- }
- }
- })
- })
-}
diff --git a/service/music/track.go b/service/music/track.go
deleted file mode 100644
index 5a9e993..0000000
--- a/service/music/track.go
+++ /dev/null
@@ -1,81 +0,0 @@
-package music
-
-import (
- "arimelody-web/model"
- "arimelody-web/errors"
-)
-
-
-func (s *MusicService) GetAllTracks() ([]*model.Track, error) {
- return s.repo.GetAllTracks()
-}
-func (s *MusicService) GetTrackCount() (int, error) {
- return s.repo.GetTrackCount()
-}
-func (s *MusicService) GetTrackByID(id string) (*model.Track, error) {
- track, err := s.repo.GetTrackByID(id)
- if err != nil { return nil, err }
- if track == nil { return nil, errors.NewNotExistError("Track does not exist") }
- return track, nil
-}
-func (s *MusicService) GetOrphanTracks() ([]*model.Track, error) {
- return s.repo.GetOrphanTracks()
-}
-func (s *MusicService) GetTracksNotOnRelease(releaseID string) ([]*model.Track, error) {
- return s.repo.GetTracksNotOnRelease(releaseID)
-}
-func (s *MusicService) GetTrackReleases(trackID string) ([]*model.Release, error) {
- return s.repo.GetTrackReleases(trackID)
-}
-func (s *MusicService) GetTrackFullReleases(trackID string) ([]*model.Release, error) {
- releases, err := s.GetTrackReleases(trackID)
- if err != nil { return nil, err }
- for _, release := range releases {
- if err := s.fillRelease(release); err != nil { return nil, err }
- }
- return releases, nil
-}
-
-func (s *MusicService) CreateTrack(title string, description string, lyrics string, previewURL string) (string, error) {
- if len(title) == 0 { return "", errors.NewValidationError("Track title cannot be empty") }
- id, err := s.repo.CreateTrack(title, description, lyrics, previewURL)
- if err != nil { return "", err }
- s.log.Printf("Created track '%s' (%s)", title, id)
- return id, nil
-}
-
-func (s *MusicService) UpdateTrack(track *model.Track) error {
- if len(track.Title) == 0 { return errors.NewValidationError("Track title cannot be empty") }
- if err := s.repo.UpdateTrack(track); err != nil { return err }
- s.log.Printf("Updated track '%s' (%s)", track.Title, track.ID)
- return nil
-}
-func (s *MusicService) UpdateTrackTitle(id string, title string) error {
- if len(title) == 0 { return errors.NewValidationError("Track title cannot be empty") }
- if err := s.repo.UpdateTrackTitle(id, title); err != nil { return err }
- s.log.Printf("Updated track %s title to '%s'", id, title)
- return nil
-}
-func (s *MusicService) UpdateTrackDescription(id string, description string) error {
- if err := s.repo.UpdateTrackDescription(id, description); err != nil { return err }
- s.log.Printf("Updated track %s description to '%s'", id, description)
- return nil
-}
-func (s *MusicService) UpdateTrackLyrics(id string, lyrics string) error {
- if err := s.repo.UpdateTrackLyrics(id, lyrics); err != nil { return err }
- s.log.Printf("Updated track %s lyrics to '%s'", id, lyrics)
- return nil
-}
-func (s *MusicService) UpdateTrackPreviewURL(id string, previewURL string) error {
- if err := s.repo.UpdateTrackPreviewURL(id, previewURL); err != nil { return err }
- s.log.Printf("Updated track %s preview URL to '%s'", id, previewURL)
- return nil
-}
-
-func (s *MusicService) DeleteTrack(id string) error {
- deletedID, err := s.repo.DeleteTrack(id)
- if err != nil { return err }
- if deletedID == "" { return errors.NewNotExistError("Track does not exist") }
- s.log.Printf("Deleted track %s", id)
- return nil
-}
diff --git a/service/validator/validator.go b/service/validator/validator.go
deleted file mode 100644
index cb23857..0000000
--- a/service/validator/validator.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package validator
-
-import "regexp"
-
-var idRegexp = regexp.MustCompile(`^[a-zA-Z0-9\-_\.]+$`)
-func ValidateID(id string) bool {
- return idRegexp.MatchString(id)
-}
diff --git a/service/validator/validator_test.go b/service/validator/validator_test.go
deleted file mode 100644
index 0de2353..0000000
--- a/service/validator/validator_test.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package validator_test
-
-import (
- "arimelody-web/service/validator"
- "testing"
-
- "gotest.tools/v3/assert"
-)
-
-func Test_ValidateID(t *testing.T) {
- t.Run("accepts alphanumberic ID", func(t *testing.T) {
- assert.Equal(t, validator.ValidateID("abcDEF123"), true)
- })
-
- t.Run("accepts hyphens", func(t *testing.T) {
- assert.Equal(t, validator.ValidateID("a0-1b"), true)
- })
- t.Run("accepts underscores", func(t *testing.T) {
- assert.Equal(t, validator.ValidateID("a0_1b"), true)
- })
- t.Run("accepts periods", func(t *testing.T) {
- assert.Equal(t, validator.ValidateID("a0.1b"), true)
- })
-
- t.Run("rejects other characters", func(t *testing.T) {
- t.Run("emoji", func(t *testing.T) {
- assert.Equal(t, validator.ValidateID("🗣️🔥‼️"), false)
- })
- t.Run("slashes", func(t *testing.T) {
- assert.Equal(t, validator.ValidateID("this/could/be/really/bad/for/the/router"), false)
- })
- t.Run("question marks", func(t *testing.T) {
- assert.Equal(t, validator.ValidateID("query?these=nuts"), false)
- })
- t.Run("hashes", func(t *testing.T) {
- assert.Equal(t, validator.ValidateID("unnecessary#tagging"), false)
- })
- })
-}
diff --git a/view/music.go b/view/music.go
index ed48c1b..9cb4e59 100644
--- a/view/music.go
+++ b/view/music.go
@@ -22,7 +22,7 @@ func MusicHandler(app *app.AppState) http.Handler {
return
}
- release, err := app.MusicService.GetFullReleaseByID(r.URL.Path[1:])
+ release, err := controller.GetRelease(app.DB, r.URL.Path[1:], true)
if err != nil {
http.NotFound(w, r)
return
@@ -36,7 +36,7 @@ func MusicHandler(app *app.AppState) http.Handler {
func ServeCatalog(app *app.AppState) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- releases, err := app.MusicService.GetAllFullReleases(true, 0)
+ releases, err := controller.GetAllReleases(app.DB, true, 0, true)
if err != nil {
fmt.Printf("WARN: Failed to pull releases for catalog: %s\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)