Add stats, profiles, avatars and palaute
Step 6. The surfaces around the review loop. - Nine leaderboards, ordered and limited in SQL, each with a deterministic tie-break so a tied board doesn't reshuffle between reloads. Min 3 reviews to qualify, for reviewer boards too - Profiles show counts and history-wide averages and the member's songs, never a list of their reviews — per-song opinion stays gated - Avatars: 5MB in, 256px JPEG out, ffmpeg's re-encode being the validation. No upload still means initials, and avatars are public - Changing your own password requires the current one and drops your other sessions - Palaute: free text plus the page you were on, carried in a footer link, and the user agent from the header. Reporters see their own; the admin resolves them with a timestamp rather than a status enum - Admin gained the song list with delete, the reports page, and an open-report count on the dashboard Two theme fixes the screenshots caught: leaderboard ranks need a CSS counter because display:grid suppresses list markers, and count-based boards were printing 3.0 where they mean 3.
This commit is contained in:
@@ -96,7 +96,7 @@ func main() {
|
||||
if err := sweep(ctx, pool); err != nil {
|
||||
fatal("startup sweep", "error", err)
|
||||
}
|
||||
for _, dir := range []string{"audio", "tmp"} {
|
||||
for _, dir := range []string{"audio", "tmp", "avatars"} {
|
||||
if err := os.MkdirAll(filepath.Join(cfg.storageDir, dir), 0o755); err != nil {
|
||||
fatal("storage dir", "error", err, "dir", dir)
|
||||
}
|
||||
@@ -141,6 +141,15 @@ func (a *app) memberMux() *http.ServeMux {
|
||||
mux.HandleFunc("POST /songs/{id}", a.requireMember(a.editSong))
|
||||
mux.HandleFunc("POST /songs/{id}/delete", a.requireMember(a.deleteSong))
|
||||
mux.HandleFunc("GET /audio/{id}", a.requireMember(a.audio))
|
||||
mux.HandleFunc("GET /avatars/{id}", a.avatar) // public: avatars are not secret
|
||||
|
||||
mux.HandleFunc("GET /stats", a.requireMember(a.statsPage))
|
||||
mux.HandleFunc("GET /profile", a.requireMember(a.profilePage))
|
||||
mux.HandleFunc("GET /profile/{id}", a.requireMember(a.profilePage))
|
||||
mux.HandleFunc("POST /profile", a.requireMember(a.editProfile))
|
||||
|
||||
mux.HandleFunc("GET /report", a.requireMember(a.reportPage))
|
||||
mux.HandleFunc("POST /report", a.requireMember(a.createReport))
|
||||
|
||||
mux.HandleFunc("POST /songs/{id}/review", a.requireMember(a.createReview))
|
||||
mux.HandleFunc("POST /reviews/{id}", a.requireMember(a.editReview))
|
||||
@@ -164,6 +173,10 @@ func (a *app) adminMux() *http.ServeMux {
|
||||
mux.HandleFunc("POST /admin/invites", a.createInvite)
|
||||
mux.HandleFunc("POST /admin/users/{id}/ban", a.toggleBan)
|
||||
mux.HandleFunc("POST /admin/users/{id}/password", a.resetPassword)
|
||||
mux.HandleFunc("POST /admin/songs/{id}/delete", a.adminDeleteSong)
|
||||
mux.HandleFunc("GET /admin/reports", a.adminReports)
|
||||
mux.HandleFunc("POST /admin/reports/{id}/resolve", a.resolveReport)
|
||||
mux.HandleFunc("GET /admin/audio/{id}", a.adminAudio)
|
||||
mux.HandleFunc("GET /{$}", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, "/admin", http.StatusSeeOther)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user