Files
scrabble-game/backend/internal/adminconsole/views.go
T
Ilia Denisov aafdd46a4b
Tests · Go / test (push) Successful in 7s
Tests · Integration / integration (push) Successful in 11s
Tests · Go / test (pull_request) Successful in 6s
Tests · Integration / integration (pull_request) Successful in 13s
Stage 10: admin console & dictionary ops (complaint review, hot-reload, broadcasts)
Server-rendered admin console in the backend at /_gm (internal/adminconsole),
fronted on the gateway's public listener by Basic-Auth + a verbatim reverse proxy
(mounted on the edge mux below the h2c wrap). A same-origin check guards its POSTs;
no operator identity is tracked. This supersedes the Stage 6 gateway-fronts-
/api/v1/admin model: GATEWAY_ADMIN_ADDR and the backend /api/v1/admin ping are
dropped and gateway/internal/admin is repurposed to the verbatim proxy.

- Complaints: migration 00008 (+ jetgen) adds disposition/resolution_note/
  resolved_at/applied_in_version + the deferred status CHECK; resolution feeds a
  query-derived pending dictionary-change pipeline (marked applied after a reload).
- Dictionary hot-reload: per-version subdir BACKEND_DICT_DIR/<version>/ via the new
  Registry.LoadAvailable; engine.OpenWithVersions restores resident versions on
  restart. Partially addresses TODO-2.
- Broadcasts: a backend Telegram-connector client (internal/connector,
  BACKEND_CONNECTOR_ADDR) for SendToUser / SendToGameChannel (discharges the Stage 9
  forward-note).
- Admin reads: account.ListAccounts/CountAccounts/Identities and
  game.ListGames/CountGames/GameByID/ListComplaints/GetComplaint/CountComplaints/
  ResolveComplaint/DictionaryChanges/MarkChangesApplied.
- Tests: adminconsole render, engine reload, same-origin guard, gateway verbatim
  proxy + h2c console mount, inttest complaint pipeline + list/count + /_gm console.
- Docs: PLAN (Stage 10 done + refinements + TODO-2), ARCHITECTURE §1/§5/§6/§12/§13,
  FUNCTIONAL (+_ru), TESTING, backend/gateway READMEs.
2026-06-04 09:24:59 +02:00

201 lines
4.3 KiB
Go

package adminconsole
// The *View types are the display models the gin handlers fill and the templates
// render. Time values are pre-formatted to strings by the handlers so the
// templates stay logic-free.
// Pager is the shared list pagination state.
type Pager struct {
Page int
PageSize int
Total int
HasPrev bool
HasNext bool
PrevPage int
NextPage int
}
// NewPager builds the pagination state for a 1-based page of pageSize over total
// items.
func NewPager(page, pageSize, total int) Pager {
if page < 1 {
page = 1
}
p := Pager{Page: page, PageSize: pageSize, Total: total, PrevPage: page - 1, NextPage: page + 1}
p.HasPrev = page > 1
p.HasNext = page*pageSize < total
return p
}
// VariantVersions lists the dictionary versions resident for one variant.
type VariantVersions struct {
Variant string
Latest string
Versions []string
}
// DashboardView is the landing-page summary.
type DashboardView struct {
Accounts int
Games int
ActiveGames int
OpenComplaints int
PendingChanges int
Variants []VariantVersions
}
// UsersView is the paginated account list.
type UsersView struct {
Items []UserRow
Pager Pager
}
// UserRow is one account row in the list.
type UserRow struct {
ID string
DisplayName string
Kind string
Language string
Guest bool
CreatedAt string
}
// UserDetailView is one account with its stats, identities and recent games.
type UserDetailView struct {
ID string
DisplayName string
Language string
TimeZone string
Guest bool
NotificationsInAppOnly bool
HintBalance int
CreatedAt string
HasStats bool
Stats StatsRow
Identities []IdentityRow
Games []GameRow
TelegramID string
ConnectorEnabled bool
}
// StatsRow is an account's lifetime statistics.
type StatsRow struct {
Wins int
Losses int
Draws int
MaxGamePoints int
MaxWordPoints int
}
// IdentityRow is one platform/email identity of an account.
type IdentityRow struct {
Kind string
ExternalID string
Confirmed bool
CreatedAt string
}
// GameRow is one game row in a list.
type GameRow struct {
ID string
Variant string
Status string
Players int
UpdatedAt string
}
// GamesView is the paginated games list, optionally filtered by status.
type GamesView struct {
Items []GameRow
Status string
Pager Pager
}
// GameDetailView is one game with its seats.
type GameDetailView struct {
ID string
Variant string
DictVersion string
Status string
Players int
ToMove int
EndReason string
MoveCount int
CreatedAt string
UpdatedAt string
FinishedAt string
Seats []SeatRow
}
// SeatRow is one seat of a game.
type SeatRow struct {
Seat int
DisplayName string
AccountID string
Score int
HintsUsed int
Winner bool
}
// ComplaintsView is the paginated complaint review queue.
type ComplaintsView struct {
Items []ComplaintRow
Status string
Pager Pager
}
// ComplaintRow is one complaint row in the queue.
type ComplaintRow struct {
ID string
Word string
Variant string
WasValid bool
Status string
Disposition string
CreatedAt string
}
// ComplaintDetailView is one complaint with its resolution state and form.
type ComplaintDetailView struct {
ID string
Word string
Variant string
DictVersion string
WasValid bool
Note string
Status string
Disposition string
ResolutionNote string
CreatedAt string
ResolvedAt string
GameID string
Resolved bool
}
// DictionaryView lists the resident versions per variant and the pending
// wordlist changes from accepted complaints.
type DictionaryView struct {
Variants []VariantVersions
Changes []DictChangeRow
}
// DictChangeRow is one pending wordlist edit.
type DictChangeRow struct {
Variant string
Word string
Action string
ResolvedAt string
}
// BroadcastView is the operator-broadcast form page.
type BroadcastView struct {
ConnectorEnabled bool
}
// MessageView is the result page shown after a POST action.
type MessageView struct {
Heading string
Body string
Back string
}