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.
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user