From 2675e6d82b252ce03b05f8a7093bf9e6f2ebad15 Mon Sep 17 00:00:00 2001 From: ponzischeme89 Date: Mon, 3 Aug 2026 10:16:44 +1200 Subject: [PATCH] Add Memby account management --- app/build.gradle.kts | 2 +- server/internal/api/admin.go | 7 +- server/internal/api/admin.html | 148 ++++++++++++++++++++- server/internal/api/admin_accounts.go | 180 ++++++++++++++++++++++++++ server/internal/api/admin_test.go | 26 +++- server/internal/store/ranking.go | 6 + server/internal/store/store.go | 103 +++++++++++++++ 7 files changed, 467 insertions(+), 5 deletions(-) create mode 100644 server/internal/api/admin_accounts.go diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 3e98b63..e9a1ec7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -39,7 +39,7 @@ val projectNoticeText = // A release workflow can derive the app version from its Git tag without editing the // source tree. Local builds keep using the checked-in default. -val defaultVersionName = "0.2.9" +val defaultVersionName = "0.2.10" val membyVersionName: String = (project.findProperty("memby.versionName") as String?) ?.trim() diff --git a/server/internal/api/admin.go b/server/internal/api/admin.go index a9c4089..63cf468 100644 --- a/server/internal/api/admin.go +++ b/server/internal/api/admin.go @@ -32,6 +32,11 @@ func (s *Server) adminRoutes() http.Handler { mux.HandleFunc("GET /admin/{$}", s.handleAdminRoot) mux.HandleFunc("GET /admin/{page}", s.handleAdminPage) mux.Handle("GET /admin/api/status", s.adminAuth(s.handleAdminStatus)) + mux.Handle("GET /admin/api/accounts", s.adminAuth(s.handleAdminAccounts)) + mux.Handle("PUT /admin/api/accounts/{userID}/devices/{deviceID}", s.adminAuth(s.handleAdminRenameDevice)) + mux.Handle("DELETE /admin/api/accounts/{userID}/devices/{deviceID}", s.adminAuth(s.handleAdminDeleteDevice)) + mux.Handle("DELETE /admin/api/accounts/{userID}/sessions", s.adminAuth(s.handleAdminDeleteAccount)) + mux.Handle("DELETE /admin/api/accounts/{userID}/recommendations", s.adminAuth(s.handleAdminResetRecommendations)) mux.Handle("GET /admin/api/recommendations", s.adminAuth(s.handleAdminRecommendations)) mux.Handle("GET /admin/api/analytics", s.adminAuth(s.handleAdminAnalytics)) mux.Handle("GET /admin/api/events", s.adminAuth(s.handleAdminEvents)) @@ -50,7 +55,7 @@ func (s *Server) adminRoutes() http.Handler { } var adminPages = map[string]bool{ - "library": true, "recommendations": true, "requests": true, + "accounts": true, "library": true, "recommendations": true, "requests": true, "features": true, "playback": true, "maintenance": true, "updates": true, "engagement": true, "ratings": true, "imports": true, "logs": true, } diff --git a/server/internal/api/admin.html b/server/internal/api/admin.html index 0d3ec04..430e388 100644 --- a/server/internal/api/admin.html +++ b/server/internal/api/admin.html @@ -138,6 +138,34 @@ .component.negative { color:#ffaaa3; background:#3a1d1c; } .recommendation-meta { color:var(--muted); font-size:12px; margin-top:4px; } .reason-text { margin-top:9px; color:#dce3e8; } + .account-summary { + display:grid; grid-template-columns:repeat(3,minmax(150px,1fr)); gap:12px; margin-bottom:16px; + } + .account-stat { padding:15px; border:1px solid var(--line); border-radius:10px; background:#10151a; } + .account-stat b { display:block; font-size:24px; } + .account-stat span { color:var(--muted); font-size:11px; text-transform:uppercase; letter-spacing:.06em; } + .account-list { display:grid; gap:12px; } + .account-card { border:1px solid var(--line); border-radius:11px; background:#10151a; overflow:hidden; } + .account-head { display:flex; align-items:flex-start; justify-content:space-between; gap:16px; padding:17px; } + .account-name { display:flex; align-items:center; gap:12px; } + .account-avatar { display:grid; place-items:center; width:42px; height:42px; border-radius:50%; background:var(--accent-soft); color:#91df94; font-size:17px; font-weight:800; } + .account-name h3 { margin:0; font-size:17px; } + .account-name p { margin:2px 0 0; color:var(--muted); font-size:12px; } + .account-actions { display:flex; gap:8px; flex-wrap:wrap; justify-content:flex-end; } + .account-actions button,.device-actions button { padding:7px 10px; font-size:12px; } + .account-body { display:grid; grid-template-columns:minmax(0,1.2fr) minmax(280px,.8fr); border-top:1px solid var(--line); } + .account-pane { padding:16px 17px; min-width:0; } + .account-pane + .account-pane { border-left:1px solid var(--line); } + .account-pane h4 { margin:0 0 10px; color:var(--muted); font-size:11px; text-transform:uppercase; letter-spacing:.07em; } + .device-row { display:grid; grid-template-columns:minmax(150px,1fr) auto; gap:12px; align-items:center; padding:10px 0; border-top:1px solid #202830; } + .device-row:first-of-type { border-top:0; padding-top:0; } + .device-title { font-weight:650; } + .device-meta { color:var(--muted); font-size:12px; margin-top:2px; } + .device-actions { display:flex; gap:6px; } + .choice-list { display:flex; gap:6px; flex-wrap:wrap; } + .choice { padding:4px 8px; border-radius:6px; background:#202830; color:#dce3e8; font-size:12px; } + .choice.rating { background:#302b16; color:#f2d575; } + .account-note { padding:12px 14px; border:1px solid #2b4830; border-radius:8px; background:rgba(82,181,75,.07); color:#b9c6bc; font-size:13px; margin-bottom:16px; } .feature-hero { display:grid; grid-template-columns:minmax(0,1fr) auto; gap:22px; align-items:center; padding:20px; margin-bottom:16px; border:1px solid #2b4830; border-radius:11px; @@ -176,6 +204,8 @@ .event-attrs { grid-column:1 / -1; } .recommendation-controls { grid-template-columns:repeat(2,minmax(150px,1fr)); } .feature-grid { grid-template-columns:1fr; } + .account-body { grid-template-columns:1fr; } + .account-pane + .account-pane { border-left:0; border-top:1px solid var(--line); } } @media (max-width: 560px) { input[type=text], input[type=password], input[type=number], input[type=datetime-local], @@ -184,6 +214,9 @@ .stats { gap: 18px; } .recommendation-controls { grid-template-columns:1fr; } .feature-hero { grid-template-columns:1fr; } + .account-summary { grid-template-columns:1fr; } + .account-head,.device-row { grid-template-columns:1fr; display:grid; } + .account-actions { justify-content:flex-start; } } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } @@ -199,6 +232,9 @@
Manage