93f3ec240c
Новый раздел /admin/news — лента событий системы (окна техработ НРД, обновления документации, переустановка сертификатов УЦ). Каждая новость со временем, типом (maintenance/feature/doc-update/system/ manual), опциональным окном действительности (ValidFrom..ValidTo) и ссылкой на источник. Лента не очищается — служит журналом для аудита. На дашборде /admin/ — компактный блок «📢 Новости»: показывает максимум 3 актуальных события (активных сейчас или с окном, начинающимся в ближайшие 7 дней; в остатке — самые свежие). Окна техработ при наступлении становятся жёлтыми (border-left, ValidFrom..ValidTo). В ленте можно добавлять новости вручную (форма на /admin/news), скрывать (soft-delete через Dismissed). Дедуп по ID. Doc-watcher: горутина в bj-server, раз в сутки качает страницы НРД (дефолтные источники — moex-most, программы НРД, криптосервис), парсит HTML на ссылки .pdf, скачивает новые версии в DOC/ (со старыми переименовывая в .YYYY-MM-DD.pdf.bak для аудита), и публикует новость «Обновлена документация: <file>». Sha256-дедуп — пере-импорта неизменённого PDF не будет. Cacerts.go: FetchCACertificates теперь принимает *RuntimeConfig и при успешной переустановке сертификата эмитирует NewsItem «Обновлён сертификат УЦ: <CN>». Если сертификат истекает в ближайшие 14 дней — отдельная новость-предупреждение. Это закрывает запрос «получает в авто режиме и предупреждает об этом» из обсуждения. SeedDefaultNews публикует при старте bj-server две известные новости: - TEST3 недоступен 18.05.2026 — 22.05.2026 (НРД письмо НРД-И-2026-8452) - Робот-автотест MOEX МОСТ доступен на TEST3 с 12.05.2026 Скачаны три свежие инструкции с nsd.ru/services/novye-servisy/moex-most-dlya-m2m/: - DOC/instruktsiya-po-testirovaniyu-s-robotom.pdf (новая, 12.05.2026) - DOC/instruktsiya-dlya-osuschestvleniya-obmena-soobscheniyami-...-fizicheskim-litsom-samomu-sebe.pdf (новая, 12.05.2026) - DOC/servis-most-m2m.pdf (актуальная общая инструкция) Mastered tasks: #46, #47, #48.
127 lines
6.9 KiB
HTML
127 lines
6.9 KiB
HTML
{{define "content"}}
|
||
<style>
|
||
.news-item { background:var(--card); border:1px solid var(--border); border-radius:6px; padding:14px; margin-bottom:10px; }
|
||
.news-item.dismissed { opacity:0.5; }
|
||
.news-item.kind-maintenance { border-left:4px solid var(--warn); }
|
||
.news-item.kind-feature { border-left:4px solid var(--ok); }
|
||
.news-item.kind-doc-update { border-left:4px solid var(--accent); }
|
||
.news-item.kind-system { border-left:4px solid var(--err); }
|
||
.news-item.kind-manual { border-left:4px solid var(--muted); }
|
||
.news-meta { font-size:11px; color:var(--muted); margin-bottom:6px; text-transform:uppercase; letter-spacing:0.04em; }
|
||
.news-title { font-size:15px; font-weight:600; margin:0 0 6px 0; }
|
||
.news-body { font-size:13px; white-space:pre-wrap; }
|
||
.news-validity { margin-top:6px; padding:4px 8px; background:var(--bg); border-radius:4px; display:inline-block; font-size:12px; }
|
||
.news-validity.active { background:rgba(232,177,58,0.15); color:var(--warn); }
|
||
</style>
|
||
|
||
{{if .Flash}}<div class="card" style="border-left:3px solid var(--accent)"><p style="margin:0">{{.Flash}}</p></div>{{end}}
|
||
|
||
<div class="card">
|
||
<h2>Новости и события</h2>
|
||
<p class="muted">События системы, окна техработ НРД, обновления документации и сертификатов. Лента не очищается — служит журналом для аудита. Скрытые новости можно посмотреть, сняв галочку «Только активные».</p>
|
||
<form method="post" action="/admin/news/check-docs" style="margin-top:10px;display:flex;gap:8px;align-items:center;flex-wrap:wrap">
|
||
<button type="submit" class="btn">🔄 Проверить обновления документации НРД сейчас</button>
|
||
{{if not .Settings.News.LastDocCheck.IsZero}}
|
||
<span class="muted" style="font-size:12px">Последняя проверка: {{.Settings.News.LastDocCheck.Format "02.01.2006 15:04:05"}}</span>
|
||
{{end}}
|
||
</form>
|
||
{{if .Settings.News.DocSources}}
|
||
<details style="margin-top:8px">
|
||
<summary style="cursor:pointer;color:var(--accent);font-size:13px">Источники документации, которые отслеживает doc-watcher</summary>
|
||
<table style="margin-top:8px;font-size:13px">
|
||
<thead><tr><th>Имя</th><th>URL</th><th>PDF найдено</th><th>Последняя проверка</th></tr></thead>
|
||
<tbody>
|
||
{{range .Settings.News.DocSources}}
|
||
<tr>
|
||
<td>{{.Name}}</td>
|
||
<td><a href="{{.URL}}" target="_blank"><code style="font-size:11px">{{.URL}}</code></a></td>
|
||
<td>{{len .KnownPDFs}}</td>
|
||
<td>{{if .LastChecked.IsZero}}—{{else}}{{.LastChecked.Format "02.01.2006 15:04"}}{{end}}</td>
|
||
</tr>
|
||
{{end}}
|
||
</tbody>
|
||
</table>
|
||
</details>
|
||
{{end}}
|
||
</div>
|
||
|
||
<div class="card">
|
||
<h2>Добавить вручную</h2>
|
||
<form method="post" action="/admin/news/add" style="display:grid;gap:8px;grid-template-columns:1fr 1fr;align-items:end">
|
||
<div style="grid-column:1 / 3">
|
||
<label class="muted" style="font-size:12px">Заголовок</label>
|
||
<input type="text" name="title" required placeholder="Например: TEST3 будет недоступен 01.06—03.06" style="width:100%;padding:8px;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px">
|
||
</div>
|
||
<div style="grid-column:1 / 3">
|
||
<label class="muted" style="font-size:12px">Тело (опционально)</label>
|
||
<textarea name="body" rows="2" placeholder="Подробности, ссылка на письмо, контакт" style="width:100%;padding:8px;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px;font-family:inherit"></textarea>
|
||
</div>
|
||
<div>
|
||
<label class="muted" style="font-size:12px">Тип</label>
|
||
<select name="kind" style="padding:8px;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px;width:100%">
|
||
<option value="manual">manual — ручная заметка</option>
|
||
<option value="maintenance">maintenance — окно техработ</option>
|
||
<option value="feature">feature — новая возможность</option>
|
||
<option value="system">system — внимание</option>
|
||
</select>
|
||
</div>
|
||
<div></div>
|
||
<div>
|
||
<label class="muted" style="font-size:12px">Действует с (опц.)</label>
|
||
<input type="date" name="valid_from" style="padding:8px;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px;width:100%">
|
||
</div>
|
||
<div>
|
||
<label class="muted" style="font-size:12px">Действует по (опц.)</label>
|
||
<input type="date" name="valid_to" style="padding:8px;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px;width:100%">
|
||
</div>
|
||
<div style="grid-column:1 / 3">
|
||
<button type="submit" class="btn">Добавить</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<h2 style="margin:24px 0 12px 0">Лента ({{len .Settings.News.Items}})</h2>
|
||
|
||
{{if not .Settings.News.Items}}
|
||
<div class="card"><p class="muted" style="margin:0">Пока ничего нет. Doc-watcher запустится через минуту после старта bj-server и заполнит ленту автоматически.</p></div>
|
||
{{end}}
|
||
|
||
{{range .Settings.News.Items}}
|
||
<div class="news-item kind-{{.Kind}} {{if .Dismissed}}dismissed{{end}}">
|
||
<div class="news-meta">
|
||
{{.At.Format "02.01.2006 15:04"}}
|
||
· <strong>{{.Kind}}</strong>
|
||
{{if .URL}}· <a href="{{.URL}}" target="_blank" rel="noopener">источник</a>{{end}}
|
||
</div>
|
||
<h3 class="news-title">{{.Title}}</h3>
|
||
{{if .Body}}<div class="news-body">{{.Body}}</div>{{end}}
|
||
{{if or (not .ValidFrom.IsZero) (not .ValidTo.IsZero)}}
|
||
{{$now := now}}
|
||
{{$active := false}}
|
||
{{if and (not .ValidFrom.IsZero) (not .ValidTo.IsZero)}}
|
||
{{if and (gt $now.Unix .ValidFrom.Unix) (lt $now.Unix .ValidTo.Unix)}}{{$active = true}}{{end}}
|
||
{{end}}
|
||
<div class="news-validity {{if $active}}active{{end}}">
|
||
{{if not .ValidFrom.IsZero}}С {{.ValidFrom.Format "02.01.2006"}}{{end}}
|
||
{{if not .ValidTo.IsZero}} по {{.ValidTo.Format "02.01.2006"}}{{end}}
|
||
{{if $active}} — <strong>сейчас активно</strong>{{end}}
|
||
</div>
|
||
{{end}}
|
||
{{if not .Dismissed}}
|
||
<form method="post" action="/admin/news/dismiss" style="margin-top:10px">
|
||
<input type="hidden" name="id" value="{{.ID}}">
|
||
<button type="submit" class="btn" style="background:var(--border);color:var(--text);padding:4px 10px;font-size:12px">Скрыть</button>
|
||
</form>
|
||
{{end}}
|
||
</div>
|
||
{{end}}
|
||
|
||
{{if .Settings.News.DocCheckResult}}
|
||
<div class="card" style="margin-top:20px">
|
||
<h2>Журнал последней проверки документации</h2>
|
||
<pre>{{.Settings.News.DocCheckResult}}</pre>
|
||
</div>
|
||
{{end}}
|
||
|
||
{{end}}
|