| 2026-10-04 |

Синхронизация с gnexus-handbook: healthz, /mcp-protocol/, блокировка, i18n
...
Часть A — точечные несоответствия:
- GET /healthz (alias к /api/healthz) — требование хендбука 10-platform/health.
Часть C — MCP второго маршрута и блокировка:
- /mcp-protocol/ вторым маршрутом (конвенция 10-platform/mcp.md, слэш значим
для реверс-прокси); FastMCP-саб-аппу скармливается канонический /mcp.
/mcp остаётся историческим alias'ом (docs/07).
- Вебхуки gnexus-auth user.blocked/deleted/archived и user.unblocked/restored
ставят/снимают флаг в новой таблице user_prefs (миграция 0009); resolve_
personal_token гонит заблокированных и отвечает тем же 401, что и при
неверном токене. Статический MCP_TOKEN не затрагивается. SSO-логаут
токены не трогает (решение владельца).
Часть B — i18n по конвенции 10-platform/i18n.md (ru база + en + uk):
- frontend/src/i18n/ — модуль по паттерну gnexus-creds (t() c интерполяцией
и славскими плюрализациями, fallback en) + словари-сообщения (386 slug'ов);
все 15 вьюх и App.vue переведены на t(), module-консты с текстами — в
computed (грабли i18n.md); ui.js fmtTime — на Intl.DateTimeFormat;
push.js/local строки — в словарь.
- Язык: override в user_prefs.locale, эффективный = override → locale аккаунта
(profile.locale, нормализация до 2-буквенного префикса) → en; GET /me
возвращает locale/locale_effective, PATCH /me принимает en|uk|ru|auto.
Выбор языка — в Настройках (всем ролям), опции родными именами.
- Серверная локализация: Accept-Language от SPA; app/i18n_server.py —
каталог точных строк + regex-паттерны для параметризованных detail
(exception-handler StarletteHTTPException в main.py); labels/help реестра
настроек локализуются на лету.
- apiFetch: шлёт Accept-Language; 422 detail (список) собирается в строку.
Хендбук (отдельный репо): notifications.md — конвенция интеграции сервисов
(источник/ключ/конверт v1/client-libs/webhook-цель), mcp.md и auth.md —
референс-варианты Synapse, чеклист нового сервиса ссылается на notifications.md.
Eugene Sukhodolskiy
committed
5 hours ago
|

feat: персональные MCP-ключи — выпуск в SPA, роль владельца у тула
...
Решение владельца (2026-10-04):
- Каждый пользователь выпускает себе MCP-ключ на новой странице /mcp-keys
(доступна любой роли); plaintext показывается один раз, в БД — sha256 + hint.
- Ключ действует ролью владельца на момент выпуска (снейпшот system_role):
admin/superadmin — полный каталог тулов (35), user — только личный набор
(me, my_events_list, my_push_subscriptions_list, my_push_subscription_delete).
- Статический MCP_TOKEN из .env остаётся суперадмин-бэкдором; /mcp монтируется
безусловно (персональные ключи работают и без env-токена).
Backend:
- таблица mcp_tokens (миграция 0008 c9a2d6e71f43) — зеркало ApiKey;
- app/mcp/context.py — ContextVar пользователя, resolve_personal_token;
- McpTokenGuard резолвит стат.токен или mcp_* в БД, пишет last_used_at,
401 на промахе (чистый ASGI, SSE не трогает);
- тулы: user=current_mcp_user() вместо синтетического MCP_USER; админ-тулы
под _admin_run — для user-ключа ToolError с {"error": 403} (не return-данные:
FastMCP валидирует вывод по аннотации, list-тулы бы отвергли dict);
- HTTP: GET/POST /api/v1/me/mcp_tokens (+/revoke), GET/POST
/api/v1/admin/mcp_tokens[.../revoke], лимит 10 активных на пользователя.
SPA: страница McpKeysView (свои ключи + как подключить агент; админ — все
ключи и отзыв), маршрут /mcp-keys (userOk), пункт в навигации обеих ролей.
Доки: docs/07 переписан (три вида доступа, 35 тулов), CLAUDE.md,
docs/08 (каталог 35), .env.example (MCP_TOKEN опционален).
E2e на живом стеке: user-ключ — 403 на админ-тулах и рабочий личный набор;
admin-ключ — полный каталог; last_used_at; бэкдор = superadmin; отзыв -> 401.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
12 hours ago
|
| 2026-10-03 |

feat: раздел Настройки (.env-оверрайды UI) и PWA с web-push каналом
...
Настройки: реестр app/settings_registry.py + хранение оверрайдов в
app_settings (settings_store, без кэша). Дефолт из .env, перебивается из
админки без перезапуска. Секреты (VAPID private, тг-токен, SMTP-пароль)
— write-only: записываются через UI, никогда не возвращаются API.
Эндпоинты GET/PUT /api/v1/admin/settings.
Web-push: канал push (целенаправлен как user — адресат из payload.user_id),
таблица push_subscriptions, самопользовательская подписка браузера через
/me/push* (upsert по endpoint, чужой 404). Воркер send_push: pywebpush
(DER base64url ключи), 404/410 — подписка удаляется, ретраи как у s2s.
PWA: manifest + sw.js (pass-through, push/notificationclick), иконки из
gnexus-mark.svg, /settings с личным пуш-блоком и админ-конфигами.
Миграция 0006 (ck_channel ×3 +push, app_settings, push_subscriptions).
Верификация: 47 бэкенд-проверок + mecheck/retcheck + 17 puppeteer — ALL OK.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
1 day ago
|
| 2026-10-02 |
Схема БД v1 (#34): 7 таблиц + миграция в alembic
...
Конфигурация: sources, api_keys (sha256-хэши, ротация), notification_types
(уникальная тройка source/subject/action), channel_targets (креды каналов —
не здесь, в .env), routing_rules (+actions, условия JSONB, weight).
Поток: events (UUID, dedup-индекс, expires_at, CHECK-статусы), deliveries
(attempts/next_retry_at, rendered_message — аудит).
- alembic upgrade head из пустой базы — проверено в контейнере
- ORM roundtrip всей цепочки — проверен, демо-данные вычищены
- docs/04-database.md: схема, решения, что сознательно не в таблицах
Co-Authored-By: Claude Code <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
1 day ago
|