419ea11b14
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 12s
CI / ui (pull_request) Successful in 47s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m12s
User-facing Feedback screen (Settings -> Info, registered accounts only): a message (<=1024 runes) plus one optional attachment, an anti-spam gate (one unreviewed message at a time), and the operator's inline reply with a Settings/Info badge. Server-rendered admin console section (/_gm/feedback): unread/read/archived queue with per-user search, detail with read/reply/ archive/delete/delete-all, safe attachment serving (nosniff, images inline via <img>, others download-only). Introduces account_roles, the first per-account role table; feedback_banned blocks only feedback submission, granted/revoked from /users and the delete-with-block action. - migration 00004_feedback (feedback_messages + account_roles) + jetgen - backend internal/feedback (store+service), internal/account/roles.go - wire: FlatBuffers feedback.submit/get/unread; gateway guest gate (Op.NonGuest, is_guest via session resolve) -> guest_forbidden before any backend call - reply push reuses NotificationEvent with a new admin_reply sub-kind - UI: /feedback route + screen, attachment picker, badge, channel detection, i18n - tests: feedback unit (Go+UI), gateway guest-gate, inttest lifecycle, e2e - docs: PLAN stage 19, ARCHITECTURE s15, FUNCTIONAL(+ru), TESTING, READMEs
55 lines
3.1 KiB
SQL
55 lines
3.1 KiB
SQL
-- +goose Up
|
|
-- User feedback ("Обратная связь"): a flat list of messages a registered player sends to the
|
|
-- operators from Settings -> Info, each with an optional single attachment, plus the operator's
|
|
-- inline reply shown back to the player. Distinct from the in-game chat_messages (peer-to-peer)
|
|
-- and from the out-of-app Telegram messages. Also introduces account_roles, the project's first
|
|
-- per-account role table, replacing per-feature boolean flags. See docs/ARCHITECTURE.md.
|
|
SET search_path = backend, pg_catalog;
|
|
|
|
-- One feedback message. read_at marks "the operator has dealt with this" (set by every console
|
|
-- action: read / reply / archive; never by merely opening the detail). The anti-spam gate forbids
|
|
-- a new submission while the player has any read_at IS NULL message. archived_at files a handled
|
|
-- message away. The reply lives on the same row: reply_body/replied_at are stamped when the
|
|
-- operator answers; reply_read_at is stamped when the player's app first fetches the reply for
|
|
-- display ("delivered = read"), and the reply is hidden from the player one week after that.
|
|
-- attachment is the raw bytes (capped at 1,000,000 in Go); attachment_name carries the original
|
|
-- file name (its extension drives the safe content-type at serve time). sender_ip is the
|
|
-- gateway-forwarded client IP (validated, like chat); channel is the submitting platform
|
|
-- (telegram/ios/android/web, validated in Go).
|
|
CREATE TABLE feedback_messages (
|
|
message_id uuid PRIMARY KEY,
|
|
account_id uuid NOT NULL REFERENCES accounts (account_id) ON DELETE CASCADE,
|
|
body text NOT NULL,
|
|
attachment bytea,
|
|
attachment_name text,
|
|
sender_ip text,
|
|
channel text NOT NULL,
|
|
read_at timestamptz,
|
|
archived_at timestamptz,
|
|
reply_body text,
|
|
replied_at timestamptz,
|
|
reply_read_at timestamptz,
|
|
created_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
-- The per-player probes (anti-spam "has unread", "latest message", "latest reply") and the
|
|
-- console's user-scoped search all look an account up newest-first.
|
|
CREATE INDEX feedback_messages_account_idx ON feedback_messages (account_id, created_at DESC);
|
|
-- The console queue lists messages newest-first across all accounts.
|
|
CREATE INDEX feedback_messages_created_idx ON feedback_messages (created_at DESC);
|
|
|
|
-- Named per-account roles. A reusable replacement for per-feature boolean flags: the first role,
|
|
-- 'feedback_banned', forbids only feedback submission (not the whole account, unlike a
|
|
-- suspension). Roles are validated in Go (no CHECK here) so new ones need no migration. Granted
|
|
-- from the feedback console (the "block" checkbox on delete) and granted/revoked from /users.
|
|
CREATE TABLE account_roles (
|
|
account_id uuid NOT NULL REFERENCES accounts (account_id) ON DELETE CASCADE,
|
|
role text NOT NULL,
|
|
granted_at timestamptz NOT NULL DEFAULT now(),
|
|
PRIMARY KEY (account_id, role)
|
|
);
|
|
|
|
-- +goose Down
|
|
SET search_path = backend, pg_catalog;
|
|
DROP TABLE IF EXISTS account_roles;
|
|
DROP TABLE IF EXISTS feedback_messages;
|