feat: страница логов /ui/logs с SSE real-time потоком
- log_buffer: RingBufferHandler, кольцевой буфер 600 записей, fan-out SSE - ui_logs: GET /ui/logs (HTML), GET /ui/logs/stream (EventSource) - main: install_log_handler при старте, подключён router логов - nav_rail: ссылка Логи, root_html: кнопка-ссылка Логи - Исправлено: NaN/Inf/NUL в теле вебхука → 500 от PostgreSQL jsonb - Тесты: test_log_buffer, test_json_sanitize; 51 passed Made-with: Cursor
This commit is contained in:
@ -46,6 +46,7 @@ APP_SHELL_CSS = """
|
||||
.gc-subtable th, .gc-subtable td { border: 1px solid #e4e4e7; padding: 0.3rem 0.45rem; }
|
||||
.gc-subtable th { background: #fafafa; }
|
||||
.gc-orphan { margin-top: 1rem; padding: 0.75rem; background: #fffbeb; border: 1px solid #fcd34d; border-radius: 8px; font-size: 0.88rem; }
|
||||
.rail-item--util { border-top: 1px solid #e4e4e7; margin-top: 0.4rem; padding-top: 0.4rem; }
|
||||
"""
|
||||
|
||||
|
||||
@ -72,6 +73,11 @@ def nav_rail_html(current_slug: str | None = None) -> str:
|
||||
items.append(
|
||||
f'<li class="{licls}"><a href="{html.escape(href)}"{cur}>{html.escape(m.title)}</a></li>'
|
||||
)
|
||||
logs_active = current_slug == "__logs__"
|
||||
items.append(
|
||||
'<li class="rail-item rail-item--util' + (" is-active" if logs_active else "") + '">'
|
||||
'<a href="/ui/logs"' + (' aria-current="page"' if logs_active else "") + ">📋 Логи</a></li>"
|
||||
)
|
||||
lis = "".join(items)
|
||||
return (
|
||||
'<aside class="app-rail" role="navigation" aria-label="Разделы приложения">'
|
||||
|
||||
Reference in New Issue
Block a user