Stage 10: admin console & dictionary ops (complaint review, hot-reload, broadcasts) (#11)
Tests · Go / test (push) Successful in 7s
Tests · Integration / integration (push) Successful in 13s

This commit was merged in pull request #11.
This commit is contained in:
2026-06-04 07:27:49 +00:00
parent 4c4beace85
commit 3a640a17a4
49 changed files with 2548 additions and 200 deletions
@@ -13,14 +13,18 @@ import (
)
type Complaints struct {
ComplaintID uuid.UUID `sql:"primary_key"`
ComplainantID uuid.UUID
GameID uuid.UUID
Variant string
DictVersion string
Word string
WasValid bool
Note string
Status string
CreatedAt time.Time
ComplaintID uuid.UUID `sql:"primary_key"`
ComplainantID uuid.UUID
GameID uuid.UUID
Variant string
DictVersion string
Word string
WasValid bool
Note string
Status string
CreatedAt time.Time
Disposition string
ResolutionNote string
ResolvedAt *time.Time
AppliedInVersion string
}
@@ -17,16 +17,20 @@ type complaintsTable struct {
postgres.Table
// Columns
ComplaintID postgres.ColumnString
ComplainantID postgres.ColumnString
GameID postgres.ColumnString
Variant postgres.ColumnString
DictVersion postgres.ColumnString
Word postgres.ColumnString
WasValid postgres.ColumnBool
Note postgres.ColumnString
Status postgres.ColumnString
CreatedAt postgres.ColumnTimestampz
ComplaintID postgres.ColumnString
ComplainantID postgres.ColumnString
GameID postgres.ColumnString
Variant postgres.ColumnString
DictVersion postgres.ColumnString
Word postgres.ColumnString
WasValid postgres.ColumnBool
Note postgres.ColumnString
Status postgres.ColumnString
CreatedAt postgres.ColumnTimestampz
Disposition postgres.ColumnString
ResolutionNote postgres.ColumnString
ResolvedAt postgres.ColumnTimestampz
AppliedInVersion postgres.ColumnString
AllColumns postgres.ColumnList
MutableColumns postgres.ColumnList
@@ -68,35 +72,43 @@ func newComplaintsTable(schemaName, tableName, alias string) *ComplaintsTable {
func newComplaintsTableImpl(schemaName, tableName, alias string) complaintsTable {
var (
ComplaintIDColumn = postgres.StringColumn("complaint_id")
ComplainantIDColumn = postgres.StringColumn("complainant_id")
GameIDColumn = postgres.StringColumn("game_id")
VariantColumn = postgres.StringColumn("variant")
DictVersionColumn = postgres.StringColumn("dict_version")
WordColumn = postgres.StringColumn("word")
WasValidColumn = postgres.BoolColumn("was_valid")
NoteColumn = postgres.StringColumn("note")
StatusColumn = postgres.StringColumn("status")
CreatedAtColumn = postgres.TimestampzColumn("created_at")
allColumns = postgres.ColumnList{ComplaintIDColumn, ComplainantIDColumn, GameIDColumn, VariantColumn, DictVersionColumn, WordColumn, WasValidColumn, NoteColumn, StatusColumn, CreatedAtColumn}
mutableColumns = postgres.ColumnList{ComplainantIDColumn, GameIDColumn, VariantColumn, DictVersionColumn, WordColumn, WasValidColumn, NoteColumn, StatusColumn, CreatedAtColumn}
defaultColumns = postgres.ColumnList{NoteColumn, StatusColumn, CreatedAtColumn}
ComplaintIDColumn = postgres.StringColumn("complaint_id")
ComplainantIDColumn = postgres.StringColumn("complainant_id")
GameIDColumn = postgres.StringColumn("game_id")
VariantColumn = postgres.StringColumn("variant")
DictVersionColumn = postgres.StringColumn("dict_version")
WordColumn = postgres.StringColumn("word")
WasValidColumn = postgres.BoolColumn("was_valid")
NoteColumn = postgres.StringColumn("note")
StatusColumn = postgres.StringColumn("status")
CreatedAtColumn = postgres.TimestampzColumn("created_at")
DispositionColumn = postgres.StringColumn("disposition")
ResolutionNoteColumn = postgres.StringColumn("resolution_note")
ResolvedAtColumn = postgres.TimestampzColumn("resolved_at")
AppliedInVersionColumn = postgres.StringColumn("applied_in_version")
allColumns = postgres.ColumnList{ComplaintIDColumn, ComplainantIDColumn, GameIDColumn, VariantColumn, DictVersionColumn, WordColumn, WasValidColumn, NoteColumn, StatusColumn, CreatedAtColumn, DispositionColumn, ResolutionNoteColumn, ResolvedAtColumn, AppliedInVersionColumn}
mutableColumns = postgres.ColumnList{ComplainantIDColumn, GameIDColumn, VariantColumn, DictVersionColumn, WordColumn, WasValidColumn, NoteColumn, StatusColumn, CreatedAtColumn, DispositionColumn, ResolutionNoteColumn, ResolvedAtColumn, AppliedInVersionColumn}
defaultColumns = postgres.ColumnList{NoteColumn, StatusColumn, CreatedAtColumn, DispositionColumn, ResolutionNoteColumn, AppliedInVersionColumn}
)
return complaintsTable{
Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
//Columns
ComplaintID: ComplaintIDColumn,
ComplainantID: ComplainantIDColumn,
GameID: GameIDColumn,
Variant: VariantColumn,
DictVersion: DictVersionColumn,
Word: WordColumn,
WasValid: WasValidColumn,
Note: NoteColumn,
Status: StatusColumn,
CreatedAt: CreatedAtColumn,
ComplaintID: ComplaintIDColumn,
ComplainantID: ComplainantIDColumn,
GameID: GameIDColumn,
Variant: VariantColumn,
DictVersion: DictVersionColumn,
Word: WordColumn,
WasValid: WasValidColumn,
Note: NoteColumn,
Status: StatusColumn,
CreatedAt: CreatedAtColumn,
Disposition: DispositionColumn,
ResolutionNote: ResolutionNoteColumn,
ResolvedAt: ResolvedAtColumn,
AppliedInVersion: AppliedInVersionColumn,
AllColumns: allColumns,
MutableColumns: mutableColumns,
@@ -0,0 +1,30 @@
-- +goose Up
-- Stage 10 admin & dictionary ops: the word-check complaint resolution lifecycle.
-- Stage 3 created complaints with a free-form status (only ever 'open'); the admin
-- review queue (this stage) resolves them with a disposition that also feeds the
-- offline dictionary-rebuild pipeline: an accepted complaint records whether the
-- word should be added or removed, and is marked applied once a rebuilt dictionary
-- version is hot-reloaded. No operator identity is recorded (the gateway gates the
-- console behind Basic-Auth; the backend keeps no admin principal). Adds columns, so
-- the generated jet code is regenerated (cmd/jetgen).
SET search_path = backend, pg_catalog;
ALTER TABLE complaints
ADD COLUMN disposition text NOT NULL DEFAULT '',
ADD COLUMN resolution_note text NOT NULL DEFAULT '',
ADD COLUMN resolved_at timestamptz,
ADD COLUMN applied_in_version text NOT NULL DEFAULT '',
ADD CONSTRAINT complaints_status_chk CHECK (status IN ('open', 'resolved')),
ADD CONSTRAINT complaints_disposition_chk
CHECK (disposition IN ('', 'reject', 'accept_add', 'accept_remove'));
-- +goose Down
SET search_path = backend, pg_catalog;
ALTER TABLE complaints
DROP CONSTRAINT complaints_disposition_chk,
DROP CONSTRAINT complaints_status_chk,
DROP COLUMN applied_in_version,
DROP COLUMN resolved_at,
DROP COLUMN resolution_note,
DROP COLUMN disposition;