diff --git a/PLAN.md b/PLAN.md index 549c9aa..1c05946 100644 --- a/PLAN.md +++ b/PLAN.md @@ -1378,6 +1378,15 @@ provided cert) at the contour caddy; prod VPN; rollback. sub-kind, decline) to the **original requester**, whose open game re-derives its friend state. Owner decisions: a declined request stays "request sent" (non-revealing); an accepted opponent reads "✓ in friends"; rack-tile reorder while tiles are placed stays disabled by design. + - **Admin "Messages" moderation section (#18, PR D):** a new `/_gm/messages` console page lists + posted chat messages (**nudges excluded**) newest-first — time · **source** (guest / robot / + oldest identity kind) · sender (→ user card) · IP · body · game (→ game card) — searchable by + sender name / external-id glob masks and pinnable to one game (`?game=`) or sender (`?user=`), + linked from the game and user cards. Server-rendered (`adminconsole` `MessagesView` + + `messages.gohtml`, 50/page via the shared pager); the list query lives in `social` (raw SQL, + `kind='message'`, the source via a SQL `CASE`), reusing the now-exported `account.LikePattern` + glob helper. Owner decisions: messages only (no nudges), separate name/ext masks (matching the + Users section), a top-level nav entry plus the card deep-links. ## Deferred TODOs (cross-stage) diff --git a/backend/internal/account/userlist.go b/backend/internal/account/userlist.go index e2c12fc..b2bd372 100644 --- a/backend/internal/account/userlist.go +++ b/backend/internal/account/userlist.go @@ -51,11 +51,11 @@ func (s *Store) IsRobot(ctx context.Context, accountID uuid.UUID) (bool, error) func userListWhere(f UserFilter) (string, []any) { args := []any{f.Robots} where := robotExists + ` = $1` - if name := likePattern(f.NameMask); name != "" { + if name := LikePattern(f.NameMask); name != "" { args = append(args, name) where += fmt.Sprintf(` AND a.display_name ILIKE $%d ESCAPE '\'`, len(args)) } - if ext := likePattern(f.ExternalIDMask); ext != "" { + if ext := LikePattern(f.ExternalIDMask); ext != "" { args = append(args, ext) where += fmt.Sprintf(` AND EXISTS (SELECT 1 FROM backend.identities i WHERE i.account_id = a.account_id AND i.external_id ILIKE $%d ESCAPE '\')`, len(args)) } @@ -95,9 +95,9 @@ func (s *Store) CountUsers(ctx context.Context, f UserFilter) (int, error) { return n, nil } -// likePattern converts a glob mask ('*' any run, '?' one char) to an ILIKE pattern, +// LikePattern converts a glob mask ('*' any run, '?' one char) to an ILIKE pattern, // escaping the SQL wildcards already in the input first. An empty/blank mask returns "". -func likePattern(mask string) string { +func LikePattern(mask string) string { mask = strings.TrimSpace(mask) if mask == "" { return "" diff --git a/backend/internal/adminconsole/render_test.go b/backend/internal/adminconsole/render_test.go index 39778d2..ab98a2c 100644 --- a/backend/internal/adminconsole/render_test.go +++ b/backend/internal/adminconsole/render_test.go @@ -26,6 +26,7 @@ func TestRendererRendersEveryPage(t *testing.T) { {"games", GamesView{Items: []GameRow{{ID: "g1", Variant: "english", Status: "active"}}, Status: "active", Pager: NewPager(1, 50, 1)}, "g1"}, {"game_detail", GameDetailView{ID: "g1", Variant: "english", Seats: []SeatRow{{Seat: 0, DisplayName: "Kaya"}}}, "Seats"}, {"complaints", ComplaintsView{Items: []ComplaintRow{{ID: "c1", Word: "qi", Status: "open"}}, Status: "open", Pager: NewPager(1, 50, 1)}, "qi"}, + {"messages", MessagesView{Items: []MessageRow{{ID: "m1", SenderID: "a1", SenderName: "Kaya", Source: "telegram", Body: "good luck", GameID: "g1"}}, Pager: NewPager(1, 50, 1)}, "good luck"}, {"complaint_detail", ComplaintDetailView{ID: "c1", Word: "qi", Variant: "english"}, "Resolve"}, {"dictionary", DictionaryView{Variants: []VariantVersions{{Variant: "english", Latest: "v1", Versions: []string{"v1"}}}, Changes: []DictChangeRow{{Variant: "english", Word: "qi", Action: "add"}}}, "Hot-reload"}, {"broadcast", BroadcastView{ConnectorEnabled: true}, "Post to the game channel"}, diff --git a/backend/internal/adminconsole/templates/layout.gohtml b/backend/internal/adminconsole/templates/layout.gohtml index c7e4946..970d876 100644 --- a/backend/internal/adminconsole/templates/layout.gohtml +++ b/backend/internal/adminconsole/templates/layout.gohtml @@ -16,6 +16,7 @@ Users Games Complaints + Messages Dictionary Broadcast Grafana ↗ diff --git a/backend/internal/adminconsole/templates/pages/game_detail.gohtml b/backend/internal/adminconsole/templates/pages/game_detail.gohtml index 976d691..068b1a6 100644 --- a/backend/internal/adminconsole/templates/pages/game_detail.gohtml +++ b/backend/internal/adminconsole/templates/pages/game_detail.gohtml @@ -1,7 +1,7 @@ {{define "content" -}} {{with .Data}}

Game {{.ID}}

- +

Summary