feat(feedback): in-app user feedback with admin review and account roles
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
This commit is contained in:
Ilia Denisov
2026-06-15 12:23:10 +02:00
parent fc848157d6
commit 419ea11b14
75 changed files with 3638 additions and 55 deletions
+11
View File
@@ -170,6 +170,17 @@ export const en = {
'about.description': 'A multiplatform Scrabble game.',
'about.version': 'Version {v}',
'feedback.title': 'Feedback',
'feedback.placeholder': 'Describe the problem or share your suggestion…',
'feedback.attach': 'Attach file',
'feedback.removeFile': 'Remove',
'feedback.send': 'Send',
'feedback.fileRejected': 'This file cant be attached.',
'feedback.sent': 'Your message has been sent.',
'feedback.waiting': 'We are reviewing your last message.',
'feedback.banned': 'Feedback submission is unavailable.',
'feedback.replyTitle': 'Reply to your last message',
'landing.tagline': 'Play Scrabble with friends or a smart robot — in your browser or on Telegram.',
'landing.playTelegram': 'Play in Telegram',
+11
View File
@@ -171,6 +171,17 @@ export const ru: Record<MessageKey, string> = {
'about.description': 'Мультиплатформенная игра в скрабл.',
'about.version': 'Версия {v}',
'feedback.title': 'Обратная связь',
'feedback.placeholder': 'Опишите проблему или поделитесь предложением…',
'feedback.attach': 'Прикрепить файл',
'feedback.removeFile': 'Удалить',
'feedback.send': 'Отправить',
'feedback.fileRejected': 'Этот файл нельзя прикрепить.',
'feedback.sent': 'Ваше сообщение отправлено',
'feedback.waiting': 'Ожидаем рассмотрения вашего последнего обращения',
'feedback.banned': 'Отправка обратной связи недоступна',
'feedback.replyTitle': 'Ответ на ваше последнее сообщение',
'landing.tagline': 'Играй в Скрэббл с друзьями или умным роботом — в браузере или в Telegram.',
'landing.playTelegram': 'Играть в Telegram',