Add dynamic button labels via ui_config.json and /api/config #12

Open
developer wants to merge 4 commits from feature/9_dynamic_button_labels into main
Collaborator

Что сделано

Реализация динамических названий кнопок по подтверждённой схеме (вариант A + ui_config.json, обсуждение в #9).

Бекенд (go-emu)

  • Новый ключ конфигурации UI_CONFIG_FILE (по умолчанию — ui_config.json рядом с исполняемым файлом; относительный путь трактуется от бинарника).
  • Файл хранит переопределения подписей {"version":1,"labels":{...}}. Запись атомарная (tmp + rename + fsync) — переживает рестарт процесса и выключение питания.
  • Приоритет источников подписей: встроенные дефолты фронта < BUTTON_LABEL_* из .env (админские) < ui_config.json (пользовательские). Пустое значение = явный сброс к дефолту.
  • API с той же bearer-авторизацией, что у /api/buttons:
    • GET /api/config — эффективные переопределения (env + файл);
    • PUT /api/config/labels — валидация + атомарная запись файла + обновление копии в памяти.
  • Валидация (по требованию заказчика): только известные имена кнопок (POWER, MOVE_OUT, MOVE_IN, ACK, PU); trim; лимит длины подписи 24 символа; лимит записей 32; лимит размера тела/файла 64 КБ (413 при превышении); белый список символов (отсекает управляющие/неожидаемые); битый JSON/неверсионный формат файла не роняют процесс — событие в лог и откат к env-дефолтам. Маски (протокол), внутренние имена и хоткеи не переопределяются — только подпись.

Фронт (web)

  • resolveButtons() — чистый мерж дефолтной таблицы BUTTONS с переопределениями из /api/config (без мутации глобального состояния).
  • fetchLabelOverrides() — загрузка при старте с bearer-токеном; при недоступности API/битом ответе тихо используются дефолты.
  • ButtonPanel/useButtonMask получают эффективную таблицу через пропсы/аргумент вместо глобального импорта.

Документация

  • README: раздел «Конфигурация UI (названия кнопок)» с примерами curl и описанием лимитов.
  • go-emu/.env.example: UI_CONFIG_FILE + примеры BUTTON_LABEL_*.

Как проверить

make build-go web-build && cd go-emu && cp .env.example .env
./ioboard-emu &
# посмотреть текущие переопределения
curl http://localhost:8080/api/config
# сохранить пользовательские подписи
curl -X PUT http://localhost:8080/api/config/labels -H 'Content-Type: application/json' -d '{"labels":{"POWER":"Питание","PU":"ПУ"}}'
# файл создан, переживает рестарт:
cat ui_config.json && # перезапустить эмулятор
# после рестарта GET /api/config возвращает сохранённое

Тесты: make test-go, make web-test — все зелёные (Go: sanitize/env-merge/persistence/HTTP-хендлеры; web: мерж/фолбэки/интеграция App с мок-API).

Closes #9

## Что сделано Реализация динамических названий кнопок по подтверждённой схеме (вариант A + `ui_config.json`, обсуждение в #9). ### Бекенд (go-emu) - Новый ключ конфигурации `UI_CONFIG_FILE` (по умолчанию — `ui_config.json` рядом с исполняемым файлом; относительный путь трактуется от бинарника). - Файл хранит переопределения подписей `{"version":1,"labels":{...}}`. Запись **атомарная** (tmp + rename + fsync) — переживает рестарт процесса и выключение питания. - Приоритет источников подписей: встроенные дефолты фронта < `BUTTON_LABEL_*` из `.env` (админские) < `ui_config.json` (пользовательские). Пустое значение = явный сброс к дефолту. - API с той же bearer-авторизацией, что у `/api/buttons`: - `GET /api/config` — эффективные переопределения (env + файл); - `PUT /api/config/labels` — валидация + атомарная запись файла + обновление копии в памяти. - **Валидация (по требованию заказчика):** только известные имена кнопок (POWER, MOVE_OUT, MOVE_IN, ACK, PU); trim; лимит длины подписи 24 символа; лимит записей 32; лимит размера тела/файла 64 КБ (413 при превышении); белый список символов (отсекает управляющие/неожидаемые); битый JSON/неверсионный формат файла не роняют процесс — событие в лог и откат к env-дефолтам. Маски (протокол), внутренние имена и хоткеи не переопределяются — только подпись. ### Фронт (web) - `resolveButtons()` — чистый мерж дефолтной таблицы `BUTTONS` с переопределениями из `/api/config` (без мутации глобального состояния). - `fetchLabelOverrides()` — загрузка при старте с bearer-токеном; при недоступности API/битом ответе тихо используются дефолты. - `ButtonPanel`/`useButtonMask` получают эффективную таблицу через пропсы/аргумент вместо глобального импорта. ### Документация - README: раздел «Конфигурация UI (названия кнопок)» с примерами curl и описанием лимитов. - `go-emu/.env.example`: `UI_CONFIG_FILE` + примеры `BUTTON_LABEL_*`. ## Как проверить ```bash make build-go web-build && cd go-emu && cp .env.example .env ./ioboard-emu & # посмотреть текущие переопределения curl http://localhost:8080/api/config # сохранить пользовательские подписи curl -X PUT http://localhost:8080/api/config/labels -H 'Content-Type: application/json' -d '{"labels":{"POWER":"Питание","PU":"ПУ"}}' # файл создан, переживает рестарт: cat ui_config.json && # перезапустить эмулятор # после рестарта GET /api/config возвращает сохранённое ``` Тесты: `make test-go`, `make web-test` — все зелёные (Go: sanitize/env-merge/persistence/HTTP-хендлеры; web: мерж/фолбэки/интеграция App с мок-API). Closes #9
- new UI_CONFIG_FILE setting (default ui_config.json next to the binary)
- atomic tmp+rename+fsync persistence for user label overrides
- label validation: known button names only, trim, length/count limits,
  size limits, character whitelist
- admin defaults via BUTTON_LABEL_* env keys merged under file overrides
- GET /api/config and PUT /api/config/labels endpoints (bearer auth,
  same as /api/buttons)
- unit tests for sanitize/env merge/persistence and HTTP handlers
- resolveButtons(): pure merge of default BUTTONS with /api/config
  overrides; only labels are overridable, masks/keys/names stay intact
- fetchLabelOverrides(): GET /api/config with bearer auth, resolves to
  null on error so defaults are always shown
- App holds the effective button table in state; ButtonPanel/useButtonMask
  receive it via props instead of importing the global BUTTONS
- unit tests for merge/fallbacks and App integration with mocked API

На windows машине я не смогу это проверить без изменения кода, верно?

На windows машине я не смогу это проверить без изменения кода, верно?
Author
Collaborator

Да, вы правы, но с важным уточнением: это ограничение не из этого PR — оно было и до него.

Эмулятор go-emu построен вокруг PTY (creack/pty) и использует golang.org/x/sys/unix (unix.Poll) — это Linux/macOS-only, на «голой» Windows он без изменений кода не запускался и раньше. Это даже зафиксировано в Makefile: «Go targets require Linux/macOS (uses PTY). Windows users should use WSL».

То, что добавлено в этом PR (UI_CONFIG_FILE, ui_config.json, GET /api/config, PUT /api/config/labels), написано на кроссплатформенных примитивах Go (os.ReadFile, os.CreateTemp + os.Rename, filepath, обычный TCP-listener) — платформозависимого кода нет. То есть Windows-совместимость этот PR не ухудшает, но и PTY-ядро под Windows не чинит.

Как проверить на Windows-машине без изменений кода:

  1. WSL — рекомендуемый путь для всего эмулятора: собрать и запустить go-emu внутри WSL, панель открыть браузером Windows. Механизм подписей проверяется полностью (curl из README).
  2. Только фронт — cd web && npm install && npm run dev работает на Windows нативно (Vite). Но без бекенда /api/config недоступен, фронт покажет дефолтные подписи (это отдельный протестированный кейс — фолбэк), сам механизм пользовательских подписей так не проверить.

Если нужна нативная работа эмулятора под Windows (named pipes вместо PTY) — это отдельная задача, готов оценить отдельно.

Да, вы правы, но с важным уточнением: это ограничение не из этого PR — оно было и до него. Эмулятор go-emu построен вокруг PTY (`creack/pty`) и использует `golang.org/x/sys/unix` (`unix.Poll`) — это Linux/macOS-only, на «голой» Windows он без изменений кода не запускался и раньше. Это даже зафиксировано в Makefile: «Go targets require Linux/macOS (uses PTY). Windows users should use WSL». То, что добавлено в этом PR (`UI_CONFIG_FILE`, `ui_config.json`, `GET /api/config`, `PUT /api/config/labels`), написано на кроссплатформенных примитивах Go (`os.ReadFile`, `os.CreateTemp` + `os.Rename`, `filepath`, обычный TCP-listener) — платформозависимого кода нет. То есть Windows-совместимость этот PR не ухудшает, но и PTY-ядро под Windows не чинит. Как проверить на Windows-машине без изменений кода: 1. **WSL** — рекомендуемый путь для всего эмулятора: собрать и запустить go-emu внутри WSL, панель открыть браузером Windows. Механизм подписей проверяется полностью (curl из README). 2. **Только фронт** — `cd web && npm install && npm run dev` работает на Windows нативно (Vite). Но без бекенда `/api/config` недоступен, фронт покажет дефолтные подписи (это отдельный протестированный кейс — фолбэк), сам механизм пользовательских подписей так не проверить. Если нужна нативная работа эмулятора под Windows (named pipes вместо PTY) — это отдельная задача, готов оценить отдельно.
Resolve conflicts between server-side label overrides (this branch) and
fullscreen mode merged to main (PR #24):
- App.tsx: keep both the buttons state (resolveButtons + fetchLabelOverrides)
  and the fullscreen/support state from main; pass the effective button table
  to useButtonMask.
- App.test.tsx: keep restoreDefaultLabels() and both sides' tests; fullscreen
  tests now seed the keba-panel-settings key instead of mocking localStorage.get,
  so they no longer break when label overrides are enabled.
- FullscreenPanel: render the buttons prop instead of the global BUTTONS, so
  custom labels from /api/config also apply in fullscreen mode.
Author
Collaborator

Ветка обновлена — в main смержили fullscreen (#24), из-за чего PR перестал сливаться (конфликты в web/src/App.tsx и App.test.tsx). Смержил main в ветку (коммит 121a9c5), конфликт разрешён так:

  • App.tsx: сохранены обе фичи — подписи с /api/config (этот PR) и fullscreen/поддержка из main;
  • FullscreenPanel: кнопки теперь рендерит из переданной таблицы (проп buttons), а не из глобального BUTTONS — пользовательские подписи теперь работают и в fullscreen-режиме;
  • App.test.tsx: оставлены тесты обеих сторон; fullscreen-тесты переведены на ключ keba-panel-settings (как в main), чтобы не ломаться при включённых оверрайдах подписей.

Проверка: web-build — ok, npx tsc -b — 0 ошибок, make web-test — 54/54 зелёные, включая интеграционный кейс «подписи POWER→Питание + fullscreen-грид 2×3». Go-часть merge не затрагивает (файлы go-emu/ вне диффа), окружение без Go-тулчейна — протестировать серверные тесты локально не могу.

PR снова mergeable — готов к мержу.

Ветка обновлена — в `main` смержили fullscreen (#24), из-за чего PR перестал сливаться (конфликты в `web/src/App.tsx` и `App.test.tsx`). Смержил `main` в ветку (коммит `121a9c5`), конфликт разрешён так: - **App.tsx**: сохранены обе фичи — подписи с `/api/config` (этот PR) и fullscreen/поддержка из `main`; - **FullscreenPanel**: кнопки теперь рендерит из переданной таблицы (проп `buttons`), а не из глобального `BUTTONS` — пользовательские подписи теперь работают и в fullscreen-режиме; - **App.test.tsx**: оставлены тесты обеих сторон; fullscreen-тесты переведены на ключ `keba-panel-settings` (как в `main`), чтобы не ломаться при включённых оверрайдах подписей. Проверка: `web-build` — ok, `npx tsc -b` — 0 ошибок, `make web-test` — **54/54 зелёные**, включая интеграционный кейс «подписи POWER→Питание + fullscreen-грид 2×3». Go-часть merge не затрагивает (файлы `go-emu/` вне диффа), окружение без Go-тулчейна — протестировать серверные тесты локально не могу. PR снова mergeable — готов к мержу.
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin feature/9_dynamic_button_labels:feature/9_dynamic_button_labels
git switch feature/9_dynamic_button_labels

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch main
git merge --no-ff feature/9_dynamic_button_labels
git switch feature/9_dynamic_button_labels
git rebase main
git switch main
git merge --ff-only feature/9_dynamic_button_labels
git switch feature/9_dynamic_button_labels
git rebase main
git switch main
git merge --no-ff feature/9_dynamic_button_labels
git switch main
git merge --squash feature/9_dynamic_button_labels
git switch main
git merge --ff-only feature/9_dynamic_button_labels
git switch main
git merge feature/9_dynamic_button_labels
git push origin main
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
forgejo-admin/keba-hhs!12
No description provided.