home to your local SPACEGIRL 💫 arimelody.space
1
fork

Configure Feed

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

fix indentation (tabs to 4 spaces) (oops)

+447 -447
+9 -9
admin/accounthttp.go
··· 1 1 package admin 2 2 3 3 import ( 4 - "database/sql" 5 - "fmt" 6 - "net/http" 7 - "net/url" 8 - "os" 4 + "database/sql" 5 + "fmt" 6 + "net/http" 7 + "net/url" 8 + "os" 9 9 10 - "arimelody-web/controller" 11 - "arimelody-web/log" 12 - "arimelody-web/model" 10 + "arimelody-web/controller" 11 + "arimelody-web/log" 12 + "arimelody-web/model" 13 13 14 - "golang.org/x/crypto/bcrypt" 14 + "golang.org/x/crypto/bcrypt" 15 15 ) 16 16 17 17 func accountHandler(app *model.AppState) http.Handler {
+3 -3
admin/artisthttp.go
··· 1 1 package admin 2 2 3 3 import ( 4 - "fmt" 5 - "net/http" 6 - "strings" 4 + "fmt" 5 + "net/http" 6 + "strings" 7 7 8 8 "arimelody-web/model" 9 9 "arimelody-web/controller"
+54 -54
admin/http.go
··· 1 1 package admin 2 2 3 3 import ( 4 - "context" 5 - "database/sql" 6 - "fmt" 7 - "net/http" 8 - "os" 9 - "path/filepath" 10 - "strings" 11 - "time" 4 + "context" 5 + "database/sql" 6 + "fmt" 7 + "net/http" 8 + "os" 9 + "path/filepath" 10 + "strings" 11 + "time" 12 12 13 - "arimelody-web/controller" 14 - "arimelody-web/log" 15 - "arimelody-web/model" 13 + "arimelody-web/controller" 14 + "arimelody-web/log" 15 + "arimelody-web/model" 16 16 17 - "golang.org/x/crypto/bcrypt" 17 + "golang.org/x/crypto/bcrypt" 18 18 ) 19 19 20 20 func Handler(app *model.AppState) http.Handler { ··· 274 274 render() 275 275 return 276 276 } 277 - if account.Locked { 278 - controller.SetSessionError(app.DB, session, "This account is locked.") 279 - render() 280 - return 281 - } 277 + if account.Locked { 278 + controller.SetSessionError(app.DB, session, "This account is locked.") 279 + render() 280 + return 281 + } 282 282 283 283 err = bcrypt.CompareHashAndPassword([]byte(account.Password), []byte(password)) 284 284 if err != nil { 285 285 app.Log.Warn(log.TYPE_ACCOUNT, "\"%s\" attempted login with incorrect password. (%s)", account.Username, controller.ResolveIP(app, r)) 286 - if locked := handleFailedLogin(app, account); locked { 287 - controller.SetSessionError(app.DB, session, "Too many failed attempts. This account is now locked.") 288 - } else { 289 - controller.SetSessionError(app.DB, session, "Invalid username or password.") 290 - } 286 + if locked := handleFailedLogin(app, account); locked { 287 + controller.SetSessionError(app.DB, session, "Too many failed attempts. This account is now locked.") 288 + } else { 289 + controller.SetSessionError(app.DB, session, "Invalid username or password.") 290 + } 291 291 render() 292 292 return 293 293 } ··· 308 308 render() 309 309 return 310 310 } 311 - controller.SetSessionMessage(app.DB, session, "") 312 - controller.SetSessionError(app.DB, session, "") 311 + controller.SetSessionMessage(app.DB, session, "") 312 + controller.SetSessionError(app.DB, session, "") 313 313 http.Redirect(w, r, "/admin/totp", http.StatusFound) 314 314 return 315 315 } ··· 389 389 } 390 390 if totpMethod == nil { 391 391 app.Log.Warn(log.TYPE_ACCOUNT, "\"%s\" failed login (Incorrect TOTP). (%s)", session.AttemptAccount.Username, controller.ResolveIP(app, r)) 392 - if locked := handleFailedLogin(app, session.AttemptAccount); locked { 393 - controller.SetSessionError(app.DB, session, "Too many failed attempts. This account is now locked.") 394 - controller.SetSessionAttemptAccount(app.DB, session, nil) 395 - http.Redirect(w, r, "/admin", http.StatusFound) 396 - } else { 397 - controller.SetSessionError(app.DB, session, "Incorrect TOTP.") 398 - } 392 + if locked := handleFailedLogin(app, session.AttemptAccount); locked { 393 + controller.SetSessionError(app.DB, session, "Too many failed attempts. This account is now locked.") 394 + controller.SetSessionAttemptAccount(app.DB, session, nil) 395 + http.Redirect(w, r, "/admin", http.StatusFound) 396 + } else { 397 + controller.SetSessionError(app.DB, session, "Incorrect TOTP.") 398 + } 399 399 render() 400 400 return 401 401 } ··· 515 515 } 516 516 517 517 func handleFailedLogin(app *model.AppState, account *model.Account) bool { 518 - locked, err := controller.IncrementAccountFails(app.DB, account.ID) 519 - if err != nil { 520 - fmt.Fprintf( 521 - os.Stderr, 522 - "WARN: Failed to increment login failures for \"%s\": %v\n", 523 - account.Username, 524 - err, 525 - ) 526 - app.Log.Warn( 527 - log.TYPE_ACCOUNT, 528 - "Failed to increment login failures for \"%s\"", 529 - account.Username, 530 - ) 531 - } 532 - if locked { 533 - app.Log.Warn( 534 - log.TYPE_ACCOUNT, 535 - "Account \"%s\" was locked: %d failed login attempts", 536 - account.Username, 537 - model.MAX_LOGIN_FAIL_ATTEMPTS, 538 - ) 539 - } 540 - return locked 518 + locked, err := controller.IncrementAccountFails(app.DB, account.ID) 519 + if err != nil { 520 + fmt.Fprintf( 521 + os.Stderr, 522 + "WARN: Failed to increment login failures for \"%s\": %v\n", 523 + account.Username, 524 + err, 525 + ) 526 + app.Log.Warn( 527 + log.TYPE_ACCOUNT, 528 + "Failed to increment login failures for \"%s\"", 529 + account.Username, 530 + ) 531 + } 532 + if locked { 533 + app.Log.Warn( 534 + log.TYPE_ACCOUNT, 535 + "Account \"%s\" was locked: %d failed login attempts", 536 + account.Username, 537 + model.MAX_LOGIN_FAIL_ATTEMPTS, 538 + ) 539 + } 540 + return locked 541 541 }
+6 -6
admin/logshttp.go
··· 1 1 package admin 2 2 3 3 import ( 4 - "arimelody-web/log" 5 - "arimelody-web/model" 6 - "fmt" 7 - "net/http" 8 - "os" 9 - "strings" 4 + "arimelody-web/log" 5 + "arimelody-web/model" 6 + "fmt" 7 + "net/http" 8 + "os" 9 + "strings" 10 10 ) 11 11 12 12 func logsHandler(app *model.AppState) http.Handler {
+5 -5
admin/releasehttp.go
··· 1 1 package admin 2 2 3 3 import ( 4 - "fmt" 5 - "net/http" 6 - "strings" 4 + "fmt" 5 + "net/http" 6 + "strings" 7 7 8 - "arimelody-web/controller" 9 - "arimelody-web/model" 8 + "arimelody-web/controller" 9 + "arimelody-web/model" 10 10 ) 11 11 12 12 func serveRelease(app *model.AppState) http.Handler {
+6 -6
admin/templates.go
··· 1 1 package admin 2 2 3 3 import ( 4 - "arimelody-web/log" 5 - "fmt" 6 - "html/template" 7 - "path/filepath" 8 - "strings" 9 - "time" 4 + "arimelody-web/log" 5 + "fmt" 6 + "html/template" 7 + "path/filepath" 8 + "strings" 9 + "time" 10 10 ) 11 11 12 12 var indexTemplate = template.Must(template.ParseFiles(
+3 -3
admin/trackhttp.go
··· 1 1 package admin 2 2 3 3 import ( 4 - "fmt" 5 - "net/http" 6 - "strings" 4 + "fmt" 5 + "net/http" 6 + "strings" 7 7 8 8 "arimelody-web/model" 9 9 "arimelody-web/controller"
+8 -8
api/api.go
··· 1 1 package api 2 2 3 3 import ( 4 - "context" 5 - "errors" 6 - "fmt" 7 - "net/http" 8 - "os" 9 - "strings" 4 + "context" 5 + "errors" 6 + "fmt" 7 + "net/http" 8 + "os" 9 + "strings" 10 10 11 - "arimelody-web/controller" 12 - "arimelody-web/model" 11 + "arimelody-web/controller" 12 + "arimelody-web/model" 13 13 ) 14 14 15 15 func Handler(app *model.AppState) http.Handler {
+39 -39
api/artist.go
··· 1 1 package api 2 2 3 3 import ( 4 - "encoding/json" 5 - "fmt" 6 - "io/fs" 7 - "net/http" 8 - "os" 9 - "path/filepath" 10 - "strings" 11 - "time" 4 + "encoding/json" 5 + "fmt" 6 + "io/fs" 7 + "net/http" 8 + "os" 9 + "path/filepath" 10 + "strings" 11 + "time" 12 12 13 - "arimelody-web/controller" 14 - "arimelody-web/log" 15 - "arimelody-web/model" 13 + "arimelody-web/controller" 14 + "arimelody-web/log" 15 + "arimelody-web/model" 16 16 ) 17 17 18 18 func ServeAllArtists(app *model.AppState) http.Handler { 19 19 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 20 20 var artists = []*model.Artist{} 21 21 artists, err := controller.GetAllArtists(app.DB) 22 - if err != nil { 22 + if err != nil { 23 23 fmt.Printf("WARN: Failed to serve all artists: %s\n", err) 24 - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 25 - return 26 - } 24 + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 25 + return 26 + } 27 27 28 - w.Header().Add("Content-Type", "application/json") 28 + w.Header().Add("Content-Type", "application/json") 29 29 encoder := json.NewEncoder(w) 30 30 encoder.SetIndent("", "\t") 31 31 err = encoder.Encode(artists) 32 - if err != nil { 33 - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 34 - } 32 + if err != nil { 33 + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 34 + } 35 35 }) 36 36 } 37 37 38 38 func ServeArtist(app *model.AppState, artist *model.Artist) http.Handler { 39 - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 40 - type ( 41 - creditJSON struct { 39 + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 40 + type ( 41 + creditJSON struct { 42 42 ID string `json:"id"` 43 43 Title string `json:"title"` 44 44 ReleaseDate time.Time `json:"releaseDate" db:"release_date"` 45 45 Artwork string `json:"artwork"` 46 - Role string `json:"role"` 47 - Primary bool `json:"primary"` 48 - } 49 - artistJSON struct { 50 - *model.Artist 51 - Credits map[string]creditJSON `json:"credits"` 52 - } 53 - ) 46 + Role string `json:"role"` 47 + Primary bool `json:"primary"` 48 + } 49 + artistJSON struct { 50 + *model.Artist 51 + Credits map[string]creditJSON `json:"credits"` 52 + } 53 + ) 54 54 55 55 session := r.Context().Value("session").(*model.Session) 56 56 show_hidden_releases := session != nil && session.Account != nil 57 57 58 58 dbCredits, err := controller.GetArtistCredits(app.DB, artist.ID, show_hidden_releases) 59 - if err != nil { 59 + if err != nil { 60 60 fmt.Printf("WARN: Failed to retrieve artist credits for %s: %v\n", artist.ID, err) 61 - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 62 - return 63 - } 61 + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 62 + return 63 + } 64 64 65 65 var credits = map[string]creditJSON{} 66 66 for _, credit := range dbCredits { ··· 74 74 } 75 75 } 76 76 77 - w.Header().Add("Content-Type", "application/json") 77 + w.Header().Add("Content-Type", "application/json") 78 78 encoder := json.NewEncoder(w) 79 79 encoder.SetIndent("", "\t") 80 80 err = encoder.Encode(artistJSON{ 81 81 Artist: artist, 82 82 Credits: credits, 83 83 }) 84 - if err != nil { 85 - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 86 - } 87 - }) 84 + if err != nil { 85 + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 86 + } 87 + }) 88 88 } 89 89 90 90 func CreateArtist(app *model.AppState) http.Handler {
+18 -18
api/release.go
··· 1 1 package api 2 2 3 3 import ( 4 - "encoding/json" 5 - "fmt" 6 - "io/fs" 7 - "net/http" 8 - "os" 9 - "path/filepath" 10 - "strings" 11 - "time" 4 + "encoding/json" 5 + "fmt" 6 + "io/fs" 7 + "net/http" 8 + "os" 9 + "path/filepath" 10 + "strings" 11 + "time" 12 12 13 - "arimelody-web/controller" 14 - "arimelody-web/log" 15 - "arimelody-web/model" 13 + "arimelody-web/controller" 14 + "arimelody-web/log" 15 + "arimelody-web/model" 16 16 ) 17 17 18 18 func ServeRelease(app *model.AppState, release *model.Release) http.Handler { 19 - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 19 + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 20 20 // only allow authorised users to view hidden releases 21 21 privileged := false 22 22 if !release.Visible { ··· 116 116 } 117 117 } 118 118 119 - w.Header().Add("Content-Type", "application/json") 119 + w.Header().Add("Content-Type", "application/json") 120 120 encoder := json.NewEncoder(w) 121 121 encoder.SetIndent("", "\t") 122 122 err := encoder.Encode(response) 123 - if err != nil { 124 - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 125 - return 126 - } 127 - }) 123 + if err != nil { 124 + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 125 + return 126 + } 127 + }) 128 128 } 129 129 130 130 func ServeCatalog(app *model.AppState) http.Handler {
+18 -18
api/track.go
··· 1 1 package api 2 2 3 3 import ( 4 - "encoding/json" 5 - "fmt" 6 - "net/http" 4 + "encoding/json" 5 + "fmt" 6 + "net/http" 7 7 8 - "arimelody-web/controller" 9 - "arimelody-web/log" 10 - "arimelody-web/model" 8 + "arimelody-web/controller" 9 + "arimelody-web/log" 10 + "arimelody-web/model" 11 11 ) 12 12 13 13 type ( ··· 29 29 dbTracks, err := controller.GetAllTracks(app.DB) 30 30 if err != nil { 31 31 fmt.Printf("WARN: Failed to pull tracks from DB: %s\n", err) 32 - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 32 + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 33 33 } 34 34 35 35 for _, track := range dbTracks { ··· 39 39 }) 40 40 } 41 41 42 - w.Header().Add("Content-Type", "application/json") 42 + w.Header().Add("Content-Type", "application/json") 43 43 encoder := json.NewEncoder(w) 44 44 encoder.SetIndent("", "\t") 45 45 err = encoder.Encode(tracks) 46 - if err != nil { 46 + if err != nil { 47 47 fmt.Printf("WARN: Failed to serve all tracks: %s\n", err) 48 - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 49 - } 48 + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 49 + } 50 50 }) 51 51 } 52 52 53 53 func ServeTrack(app *model.AppState, track *model.Track) http.Handler { 54 - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 54 + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 55 55 dbReleases, err := controller.GetTrackReleases(app.DB, track.ID, false) 56 56 if err != nil { 57 57 fmt.Printf("WARN: Failed to pull track releases for %s from DB: %s\n", track.ID, err) 58 - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 58 + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 59 59 } 60 60 61 61 releases := []string{} ··· 63 63 releases = append(releases, release.ID) 64 64 } 65 65 66 - w.Header().Add("Content-Type", "application/json") 66 + w.Header().Add("Content-Type", "application/json") 67 67 encoder := json.NewEncoder(w) 68 68 encoder.SetIndent("", "\t") 69 69 err = encoder.Encode(Track{ track, releases }) 70 - if err != nil { 70 + if err != nil { 71 71 fmt.Printf("WARN: Failed to serve track %s: %s\n", track.ID, err) 72 - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 73 - } 74 - }) 72 + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 73 + } 74 + }) 75 75 } 76 76 77 77 func CreateTrack(app *model.AppState) http.Handler {
+35 -35
api/uploads.go
··· 1 1 package api 2 2 3 3 import ( 4 - "arimelody-web/log" 5 - "arimelody-web/model" 6 - "bufio" 7 - "encoding/base64" 8 - "errors" 9 - "fmt" 10 - "os" 11 - "path/filepath" 12 - "strings" 4 + "arimelody-web/log" 5 + "arimelody-web/model" 6 + "bufio" 7 + "encoding/base64" 8 + "errors" 9 + "fmt" 10 + "os" 11 + "path/filepath" 12 + "strings" 13 13 ) 14 14 15 15 func HandleImageUpload(app *model.AppState, data *string, directory string, filename string) (string, error) { 16 - split := strings.Split(*data, ";base64,") 17 - header := split[0] 18 - imageData, err := base64.StdEncoding.DecodeString(split[1]) 19 - ext, _ := strings.CutPrefix(header, "data:image/") 16 + split := strings.Split(*data, ";base64,") 17 + header := split[0] 18 + imageData, err := base64.StdEncoding.DecodeString(split[1]) 19 + ext, _ := strings.CutPrefix(header, "data:image/") 20 20 directory = filepath.Join(app.Config.DataDirectory, directory) 21 21 22 - switch ext { 23 - case "png": 24 - case "jpg": 25 - case "jpeg": 26 - default: 27 - return "", errors.New("Invalid image type. Allowed: .png, .jpg, .jpeg") 28 - } 22 + switch ext { 23 + case "png": 24 + case "jpg": 25 + case "jpeg": 26 + default: 27 + return "", errors.New("Invalid image type. Allowed: .png, .jpg, .jpeg") 28 + } 29 29 filename = fmt.Sprintf("%s.%s", filename, ext) 30 30 31 - // ensure directory exists 32 - os.MkdirAll(directory, os.ModePerm) 31 + // ensure directory exists 32 + os.MkdirAll(directory, os.ModePerm) 33 33 34 - imagePath := filepath.Join(directory, filename) 35 - file, err := os.Create(imagePath) 36 - if err != nil { 37 - return "", err 38 - } 39 - defer file.Close() 34 + imagePath := filepath.Join(directory, filename) 35 + file, err := os.Create(imagePath) 36 + if err != nil { 37 + return "", err 38 + } 39 + defer file.Close() 40 40 41 41 // TODO: generate compressed versions of image (512x512?) 42 42 43 - buffer := bufio.NewWriter(file) 44 - _, err = buffer.Write(imageData) 45 - if err != nil { 43 + buffer := bufio.NewWriter(file) 44 + _, err = buffer.Write(imageData) 45 + if err != nil { 46 46 return "", nil 47 - } 47 + } 48 48 49 - if err := buffer.Flush(); err != nil { 49 + if err := buffer.Flush(); err != nil { 50 50 return "", nil 51 - } 51 + } 52 52 53 53 app.Log.Info(log.TYPE_FILES, "\"%s/%s.%s\" created.", directory, filename, ext) 54 54 55 - return filename, nil 55 + return filename, nil 56 56 }
+17 -17
controller/account.go
··· 1 1 package controller 2 2 3 3 import ( 4 - "arimelody-web/model" 5 - "strings" 4 + "arimelody-web/model" 5 + "strings" 6 6 7 - "github.com/jmoiron/sqlx" 7 + "github.com/jmoiron/sqlx" 8 8 ) 9 9 10 10 func GetAllAccounts(db *sqlx.DB) ([]model.Account, error) { ··· 112 112 } 113 113 114 114 func IncrementAccountFails(db *sqlx.DB, accountID string) (bool, error) { 115 - failAttempts := 0 116 - err := db.Get(&failAttempts, "UPDATE account SET fail_attempts = fail_attempts + 1 WHERE id=$1 RETURNING fail_attempts", accountID) 117 - if err != nil { return false, err } 118 - locked := false 119 - if failAttempts >= model.MAX_LOGIN_FAIL_ATTEMPTS { 120 - err = LockAccount(db, accountID) 121 - if err != nil { return false, err } 122 - locked = true 123 - } 124 - return locked, err 115 + failAttempts := 0 116 + err := db.Get(&failAttempts, "UPDATE account SET fail_attempts = fail_attempts + 1 WHERE id=$1 RETURNING fail_attempts", accountID) 117 + if err != nil { return false, err } 118 + locked := false 119 + if failAttempts >= model.MAX_LOGIN_FAIL_ATTEMPTS { 120 + err = LockAccount(db, accountID) 121 + if err != nil { return false, err } 122 + locked = true 123 + } 124 + return locked, err 125 125 } 126 126 127 127 func LockAccount(db *sqlx.DB, accountID string) error { 128 - _, err := db.Exec("UPDATE account SET locked = true WHERE id=$1", accountID) 129 - return err 128 + _, err := db.Exec("UPDATE account SET locked = true WHERE id=$1", accountID) 129 + return err 130 130 } 131 131 132 132 func UnlockAccount(db *sqlx.DB, accountID string) error { 133 - _, err := db.Exec("UPDATE account SET locked = false, fail_attempts = 0 WHERE id=$1", accountID) 134 - return err 133 + _, err := db.Exec("UPDATE account SET locked = false, fail_attempts = 0 WHERE id=$1", accountID) 134 + return err 135 135 }
+28 -28
controller/artist.go
··· 1 1 package controller 2 2 3 3 import ( 4 - "arimelody-web/model" 4 + "arimelody-web/model" 5 5 6 - "github.com/jmoiron/sqlx" 6 + "github.com/jmoiron/sqlx" 7 7 ) 8 8 9 9 // DATABASE 10 10 11 11 func GetArtist(db *sqlx.DB, id string) (*model.Artist, error) { 12 - var artist = model.Artist{} 12 + var artist = model.Artist{} 13 13 14 - err := db.Get(&artist, "SELECT * FROM artist WHERE id=$1", id) 15 - if err != nil { 16 - return nil, err 17 - } 14 + err := db.Get(&artist, "SELECT * FROM artist WHERE id=$1", id) 15 + if err != nil { 16 + return nil, err 17 + } 18 18 19 - return &artist, nil 19 + return &artist, nil 20 20 } 21 21 22 22 func GetAllArtists(db *sqlx.DB) ([]*model.Artist, error) { 23 - var artists = []*model.Artist{} 23 + var artists = []*model.Artist{} 24 24 25 - err := db.Select(&artists, "SELECT * FROM artist") 26 - if err != nil { 27 - return nil, err 28 - } 25 + err := db.Select(&artists, "SELECT * FROM artist") 26 + if err != nil { 27 + return nil, err 28 + } 29 29 30 - return artists, nil 30 + return artists, nil 31 31 } 32 32 33 33 func GetArtistsNotOnRelease(db *sqlx.DB, releaseID string) ([]*model.Artist, error) { 34 - var artists = []*model.Artist{} 34 + var artists = []*model.Artist{} 35 35 36 - err := db.Select(&artists, 36 + err := db.Select(&artists, 37 37 "SELECT * FROM artist "+ 38 38 "WHERE id NOT IN "+ 39 39 "(SELECT artist FROM musiccredit WHERE release=$1)", 40 40 releaseID) 41 - if err != nil { 42 - return nil, err 43 - } 41 + if err != nil { 42 + return nil, err 43 + } 44 44 45 - return artists, nil 45 + return artists, nil 46 46 } 47 47 48 48 func GetArtistCredits(db *sqlx.DB, artistID string, show_hidden bool) ([]*model.Credit, error) { ··· 54 54 if !show_hidden { query += "AND visible=true " } 55 55 query += "ORDER BY release_date DESC" 56 56 rows, err := db.Query(query, artistID) 57 - if err != nil { 58 - return nil, err 59 - } 57 + if err != nil { 58 + return nil, err 59 + } 60 60 defer rows.Close() 61 61 62 62 type NamePrimary struct { ··· 102 102 103 103 func CreateArtist(db *sqlx.DB, artist *model.Artist) error { 104 104 _, err := db.Exec( 105 - "INSERT INTO artist (id, name, website, avatar) "+ 105 + "INSERT INTO artist (id, name, website, avatar) "+ 106 106 "VALUES ($1, $2, $3, $4)", 107 - artist.ID, 108 - artist.Name, 109 - artist.Website, 107 + artist.ID, 108 + artist.Name, 109 + artist.Website, 110 110 artist.Avatar, 111 - ) 111 + ) 112 112 if err != nil { 113 113 return err 114 114 }
+6 -6
controller/config.go
··· 1 1 package controller 2 2 3 3 import ( 4 - "errors" 5 - "fmt" 6 - "os" 7 - "strconv" 4 + "errors" 5 + "fmt" 6 + "os" 7 + "strconv" 8 8 9 - "arimelody-web/model" 9 + "arimelody-web/model" 10 10 11 - "github.com/pelletier/go-toml/v2" 11 + "github.com/pelletier/go-toml/v2" 12 12 ) 13 13 14 14 func GetConfig() model.Config {
+5 -5
controller/invite.go
··· 1 1 package controller 2 2 3 3 import ( 4 - "arimelody-web/model" 5 - "math/rand" 6 - "strings" 7 - "time" 4 + "arimelody-web/model" 5 + "math/rand" 6 + "strings" 7 + "time" 8 8 9 - "github.com/jmoiron/sqlx" 9 + "github.com/jmoiron/sqlx" 10 10 ) 11 11 12 12 var inviteChars = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
+4 -4
controller/ip.go
··· 1 1 package controller 2 2 3 3 import ( 4 - "arimelody-web/model" 5 - "net/http" 6 - "slices" 7 - "strings" 4 + "arimelody-web/model" 5 + "net/http" 6 + "slices" 7 + "strings" 8 8 ) 9 9 10 10 // Returns the request's original IP address, resolving the `x-forwarded-for`
+4 -4
controller/migrator.go
··· 1 1 package controller 2 2 3 3 import ( 4 - "fmt" 5 - "os" 6 - "time" 4 + "fmt" 5 + "os" 6 + "time" 7 7 8 - "github.com/jmoiron/sqlx" 8 + "github.com/jmoiron/sqlx" 9 9 ) 10 10 11 11 const DB_VERSION int = 4
+7 -7
controller/qr.go
··· 1 1 package controller 2 2 3 3 import ( 4 - "bytes" 5 - "encoding/base64" 6 - "errors" 7 - "fmt" 8 - "image" 9 - "image/color" 10 - "image/png" 4 + "bytes" 5 + "encoding/base64" 6 + "errors" 7 + "fmt" 8 + "image" 9 + "image/color" 10 + "image/png" 11 11 12 12 "github.com/skip2/go-qrcode" 13 13 )
+4 -4
controller/release.go
··· 1 1 package controller 2 2 3 3 import ( 4 - "errors" 5 - "fmt" 4 + "errors" 5 + "fmt" 6 6 7 - "arimelody-web/model" 7 + "arimelody-web/model" 8 8 9 - "github.com/jmoiron/sqlx" 9 + "github.com/jmoiron/sqlx" 10 10 ) 11 11 12 12 func GetRelease(db *sqlx.DB, id string, full bool) (*model.Release, error) {
+22 -22
controller/session.go
··· 1 1 package controller 2 2 3 3 import ( 4 - "database/sql" 5 - "errors" 6 - "fmt" 7 - "net/http" 8 - "strings" 9 - "time" 4 + "database/sql" 5 + "errors" 6 + "fmt" 7 + "net/http" 8 + "strings" 9 + "time" 10 10 11 - "arimelody-web/log" 12 - "arimelody-web/model" 11 + "arimelody-web/log" 12 + "arimelody-web/model" 13 13 14 - "github.com/jmoiron/sqlx" 14 + "github.com/jmoiron/sqlx" 15 15 ) 16 16 17 17 const TOKEN_LEN = 64 ··· 33 33 } 34 34 35 35 if session != nil { 36 - if session.UserAgent != r.UserAgent() { 37 - msg := "Session user agent mismatch. A cookie may have been hijacked!" 38 - if session.Account != nil { 39 - account, _ := GetAccountByID(app.DB, session.Account.ID) 40 - msg += " (Account \"" + account.Username + "\")" 41 - } 42 - app.Log.Warn(log.TYPE_ACCOUNT, msg) 43 - err = DeleteSession(app.DB, session.Token) 44 - if err != nil { 45 - app.Log.Warn(log.TYPE_ACCOUNT, "Failed to delete affected session") 46 - } 47 - return nil, nil 48 - } 36 + if session.UserAgent != r.UserAgent() { 37 + msg := "Session user agent mismatch. A cookie may have been hijacked!" 38 + if session.Account != nil { 39 + account, _ := GetAccountByID(app.DB, session.Account.ID) 40 + msg += " (Account \"" + account.Username + "\")" 41 + } 42 + app.Log.Warn(log.TYPE_ACCOUNT, msg) 43 + err = DeleteSession(app.DB, session.Token) 44 + if err != nil { 45 + app.Log.Warn(log.TYPE_ACCOUNT, "Failed to delete affected session") 46 + } 47 + return nil, nil 48 + } 49 49 } 50 50 } 51 51
+13 -13
controller/totp.go
··· 1 1 package controller 2 2 3 3 import ( 4 - "arimelody-web/model" 5 - "crypto/hmac" 6 - "crypto/rand" 7 - "crypto/sha1" 8 - "encoding/base32" 9 - "encoding/binary" 10 - "fmt" 11 - "math" 12 - "net/url" 13 - "os" 14 - "strings" 15 - "time" 4 + "arimelody-web/model" 5 + "crypto/hmac" 6 + "crypto/rand" 7 + "crypto/sha1" 8 + "encoding/base32" 9 + "encoding/binary" 10 + "fmt" 11 + "math" 12 + "net/url" 13 + "os" 14 + "strings" 15 + "time" 16 16 17 - "github.com/jmoiron/sqlx" 17 + "github.com/jmoiron/sqlx" 18 18 ) 19 19 20 20 const TOTP_SECRET_LENGTH = 32
+22 -22
controller/track.go
··· 1 1 package controller 2 2 3 3 import ( 4 - "arimelody-web/model" 4 + "arimelody-web/model" 5 5 6 - "github.com/jmoiron/sqlx" 6 + "github.com/jmoiron/sqlx" 7 7 ) 8 8 9 9 // DATABASE ··· 13 13 14 14 stmt, _ := db.Preparex("SELECT * FROM musictrack WHERE id=$1") 15 15 err := stmt.Get(&track, id) 16 - if err != nil { 16 + if err != nil { 17 17 return nil, err 18 - } 18 + } 19 19 return &track, nil 20 20 } 21 21 22 22 func GetAllTracks(db *sqlx.DB) ([]*model.Track, error) { 23 23 var tracks = []*model.Track{} 24 24 25 - err := db.Select(&tracks, "SELECT * FROM musictrack") 26 - if err != nil { 25 + err := db.Select(&tracks, "SELECT * FROM musictrack") 26 + if err != nil { 27 27 return nil, err 28 - } 28 + } 29 29 30 30 return tracks, nil 31 31 } ··· 33 33 func GetOrphanTracks(db *sqlx.DB) ([]*model.Track, error) { 34 34 var tracks = []*model.Track{} 35 35 36 - err := db.Select(&tracks, "SELECT * FROM musictrack WHERE id NOT IN (SELECT track FROM musicreleasetrack)") 37 - if err != nil { 36 + err := db.Select(&tracks, "SELECT * FROM musictrack WHERE id NOT IN (SELECT track FROM musicreleasetrack)") 37 + if err != nil { 38 38 return nil, err 39 - } 39 + } 40 40 41 41 return tracks, nil 42 42 } 43 43 44 44 func GetTracksNotOnRelease(db *sqlx.DB, releaseID string) ([]*model.Track, error) { 45 - var tracks = []*model.Track{} 45 + var tracks = []*model.Track{} 46 46 47 - err := db.Select(&tracks, 47 + err := db.Select(&tracks, 48 48 "SELECT * FROM musictrack "+ 49 49 "WHERE id NOT IN "+ 50 50 "(SELECT track FROM musicreleasetrack WHERE release=$1)", 51 51 releaseID) 52 - if err != nil { 53 - return nil, err 54 - } 52 + if err != nil { 53 + return nil, err 54 + } 55 55 56 - return tracks, nil 56 + return tracks, nil 57 57 } 58 58 59 59 func GetTrackReleases(db *sqlx.DB, trackID string, full bool) ([]*model.Release, error) { 60 60 var releases = []*model.Release{} 61 61 62 - err := db.Select(&releases, 62 + err := db.Select(&releases, 63 63 "SELECT id,title,type,release_date,artwork,buylink "+ 64 64 "FROM musicrelease "+ 65 65 "JOIN musicreleasetrack ON release=id "+ ··· 67 67 "ORDER BY release_date", 68 68 trackID, 69 69 ) 70 - if err != nil { 70 + if err != nil { 71 71 return nil, err 72 - } 72 + } 73 73 74 74 type NamePrimary struct { 75 75 Name string `json:"name"` ··· 114 114 func PullOrphanTracks(db *sqlx.DB) ([]*model.Track, error) { 115 115 var tracks = []*model.Track{} 116 116 117 - err := db.Select(&tracks, 117 + err := db.Select(&tracks, 118 118 "SELECT id, title, description, lyrics, preview_url FROM musictrack "+ 119 119 "WHERE id NOT IN "+ 120 120 "(SELECT track FROM musicreleasetrack)", 121 121 ) 122 - if err != nil { 122 + if err != nil { 123 123 return nil, err 124 - } 124 + } 125 125 126 126 return tracks, nil 127 127 }
+9 -9
cursor/cursor.go
··· 1 1 package cursor 2 2 3 3 import ( 4 - "arimelody-web/model" 5 - "fmt" 6 - "math/rand" 7 - "net/http" 8 - "strconv" 9 - "strings" 10 - "sync" 11 - "time" 4 + "arimelody-web/model" 5 + "fmt" 6 + "math/rand" 7 + "net/http" 8 + "strconv" 9 + "strings" 10 + "sync" 11 + "time" 12 12 13 - "github.com/gorilla/websocket" 13 + "github.com/gorilla/websocket" 14 14 ) 15 15 16 16 type CursorClient struct {
+7 -7
discord/discord.go
··· 1 1 package discord 2 2 3 3 import ( 4 - "arimelody-web/model" 5 - "encoding/json" 6 - "errors" 7 - "fmt" 8 - "net/http" 9 - "net/url" 10 - "strings" 4 + "arimelody-web/model" 5 + "encoding/json" 6 + "errors" 7 + "fmt" 8 + "net/http" 9 + "net/url" 10 + "strings" 11 11 ) 12 12 13 13 const API_ENDPOINT = "https://discord.com/api/v10"
+4 -4
log/log.go
··· 1 1 package log 2 2 3 3 import ( 4 - "fmt" 5 - "os" 6 - "time" 4 + "fmt" 5 + "os" 6 + "time" 7 7 8 - "github.com/jmoiron/sqlx" 8 + "github.com/jmoiron/sqlx" 9 9 ) 10 10 11 11 type (
+26 -26
main.go
··· 1 1 package main 2 2 3 3 import ( 4 - "bufio" 5 - "errors" 6 - "fmt" 7 - stdLog "log" 8 - "math" 9 - "math/rand" 10 - "net" 11 - "net/http" 12 - "os" 13 - "path/filepath" 14 - "strconv" 15 - "strings" 16 - "time" 4 + "bufio" 5 + "errors" 6 + "fmt" 7 + stdLog "log" 8 + "math" 9 + "math/rand" 10 + "net" 11 + "net/http" 12 + "os" 13 + "path/filepath" 14 + "strconv" 15 + "strings" 16 + "time" 17 17 18 - "arimelody-web/admin" 19 - "arimelody-web/api" 20 - "arimelody-web/colour" 21 - "arimelody-web/controller" 22 - "arimelody-web/cursor" 23 - "arimelody-web/log" 24 - "arimelody-web/model" 25 - "arimelody-web/templates" 26 - "arimelody-web/view" 18 + "arimelody-web/admin" 19 + "arimelody-web/api" 20 + "arimelody-web/colour" 21 + "arimelody-web/controller" 22 + "arimelody-web/cursor" 23 + "arimelody-web/log" 24 + "arimelody-web/model" 25 + "arimelody-web/templates" 26 + "arimelody-web/view" 27 27 28 - "github.com/jmoiron/sqlx" 29 - _ "github.com/lib/pq" 30 - "golang.org/x/crypto/bcrypt" 28 + "github.com/jmoiron/sqlx" 29 + _ "github.com/lib/pq" 30 + "golang.org/x/crypto/bcrypt" 31 31 ) 32 32 33 33 // used for database migrations ··· 282 282 account.ID, 283 283 email, 284 284 account.CreatedAt, 285 - account.Locked, 285 + account.Locked, 286 286 ) 287 287 } 288 288 return
+11 -11
model/account.go
··· 1 1 package model 2 2 3 3 import ( 4 - "database/sql" 5 - "time" 4 + "database/sql" 5 + "time" 6 6 ) 7 7 8 8 const COOKIE_TOKEN string = "AM_SESSION" 9 9 const MAX_LOGIN_FAIL_ATTEMPTS int = 3 10 10 11 11 type ( 12 - Account struct { 13 - ID string `json:"id" db:"id"` 14 - Username string `json:"username" db:"username"` 15 - Password string `json:"password" db:"password"` 16 - Email sql.NullString `json:"email" db:"email"` 17 - AvatarURL sql.NullString `json:"avatar_url" db:"avatar_url"` 18 - CreatedAt time.Time `json:"created_at" db:"created_at"` 19 - FailAttempts int `json:"fail_attempts" db:"fail_attempts"` 20 - Locked bool `json:"locked" db:"locked"` 12 + Account struct { 13 + ID string `json:"id" db:"id"` 14 + Username string `json:"username" db:"username"` 15 + Password string `json:"password" db:"password"` 16 + Email sql.NullString `json:"email" db:"email"` 17 + AvatarURL sql.NullString `json:"avatar_url" db:"avatar_url"` 18 + CreatedAt time.Time `json:"created_at" db:"created_at"` 19 + FailAttempts int `json:"fail_attempts" db:"fail_attempts"` 20 + Locked bool `json:"locked" db:"locked"` 21 21 22 22 Privileges []AccountPrivilege `json:"privileges"` 23 23 }
+1 -1
model/appstate.go
··· 1 1 package model 2 2 3 3 import ( 4 - "github.com/jmoiron/sqlx" 4 + "github.com/jmoiron/sqlx" 5 5 6 6 "arimelody-web/log" 7 7 )
+10 -10
model/artist.go
··· 1 1 package model 2 2 3 3 type ( 4 - Artist struct { 5 - ID string `json:"id"` 6 - Name string `json:"name"` 7 - Website string `json:"website"` 8 - Avatar string `json:"avatar"` 9 - } 4 + Artist struct { 5 + ID string `json:"id"` 6 + Name string `json:"name"` 7 + Website string `json:"website"` 8 + Avatar string `json:"avatar"` 9 + } 10 10 ) 11 11 12 12 func (artist Artist) GetAvatar() string { 13 - if artist.Avatar == "" { 14 - return "/img/default-avatar.png" 15 - } 16 - return artist.Avatar 13 + if artist.Avatar == "" { 14 + return "/img/default-avatar.png" 15 + } 16 + return artist.Avatar 17 17 }
+6 -6
model/link.go
··· 1 1 package model 2 2 3 3 import ( 4 - "regexp" 5 - "strings" 4 + "regexp" 5 + "strings" 6 6 ) 7 7 8 8 type Link struct { 9 - Name string `json:"name"` 10 - URL string `json:"url"` 9 + Name string `json:"name"` 10 + URL string `json:"url"` 11 11 } 12 12 13 13 func (link Link) NormaliseName() string { 14 - rgx := regexp.MustCompile(`[^a-z0-9\-]`) 15 - return rgx.ReplaceAllString(strings.ToLower(link.Name), "") 14 + rgx := regexp.MustCompile(`[^a-z0-9\-]`) 15 + return rgx.ReplaceAllString(strings.ToLower(link.Name), "") 16 16 }
+16 -16
model/release.go
··· 1 1 package model 2 2 3 3 import ( 4 - "html/template" 5 - "strings" 6 - "time" 4 + "html/template" 5 + "strings" 6 + "time" 7 7 ) 8 8 9 9 type ( ··· 73 73 names = append(names, credit.Artist.Name) 74 74 } 75 75 76 - return names 76 + return names 77 77 } 78 78 79 79 func (release Release) PrintArtists(only_primary bool, ampersand bool) string { 80 80 names := release.GetUniqueArtistNames(only_primary) 81 81 82 - if len(names) == 0 { 83 - return "Unknown Artist" 84 - } else if len(names) == 1 { 85 - return names[0] 86 - } 82 + if len(names) == 0 { 83 + return "Unknown Artist" 84 + } else if len(names) == 1 { 85 + return names[0] 86 + } 87 87 88 - if ampersand { 89 - res := strings.Join(names[:len(names)-1], ", ") 90 - res += " & " + names[len(names)-1] 91 - return res 92 - } else { 93 - return strings.Join(names[:], ", ") 94 - } 88 + if ampersand { 89 + res := strings.Join(names[:len(names)-1], ", ") 90 + res += " & " + names[len(names)-1] 91 + return res 92 + } else { 93 + return strings.Join(names[:], ", ") 94 + } 95 95 }
+2 -2
model/release_test.go
··· 1 1 package model 2 2 3 3 import ( 4 - "testing" 5 - "time" 4 + "testing" 5 + "time" 6 6 ) 7 7 8 8 func Test_Release_DescriptionHTML(t *testing.T) {
+2 -2
model/session.go
··· 1 1 package model 2 2 3 3 import ( 4 - "database/sql" 5 - "time" 4 + "database/sql" 5 + "time" 6 6 ) 7 7 8 8 type Session struct {
+1 -1
model/totp.go
··· 1 1 package model 2 2 3 3 import ( 4 - "time" 4 + "time" 5 5 ) 6 6 7 7 type TOTP struct {
+8 -8
model/track.go
··· 1 1 package model 2 2 3 3 import ( 4 - "html/template" 5 - "strings" 4 + "html/template" 5 + "strings" 6 6 ) 7 7 8 8 type ( 9 - Track struct { 10 - ID string `json:"id"` 11 - Title string `json:"title"` 12 - Description string `json:"description"` 9 + Track struct { 10 + ID string `json:"id"` 11 + Title string `json:"title"` 12 + Description string `json:"description"` 13 13 Lyrics string `json:"lyrics" db:"lyrics"` 14 - PreviewURL string `json:"previewURL" db:"preview_url"` 14 + PreviewURL string `json:"previewURL" db:"preview_url"` 15 15 16 16 Number int 17 - } 17 + } 18 18 ) 19 19 20 20 func (track Track) GetDescriptionHTML() template.HTML {
+2 -2
templates/templates.go
··· 1 1 package templates 2 2 3 3 import ( 4 - "html/template" 5 - "path/filepath" 4 + "html/template" 5 + "path/filepath" 6 6 ) 7 7 8 8 var IndexTemplate = template.Must(template.ParseFiles(
+6 -6
view/music.go
··· 1 1 package view 2 2 3 3 import ( 4 - "fmt" 5 - "net/http" 6 - "os" 4 + "fmt" 5 + "net/http" 6 + "os" 7 7 8 - "arimelody-web/controller" 9 - "arimelody-web/model" 10 - "arimelody-web/templates" 8 + "arimelody-web/controller" 9 + "arimelody-web/model" 10 + "arimelody-web/templates" 11 11 ) 12 12 13 13 // HTTP HANDLER METHODS