Files
Bridge-and-Join-s/internal/lkgateway/web/templates/part_fancy.html
T
fontvielle eef6c9d996 DEPO: процедуры ставятся в мастере «Подготовить» + правка под клиента
- «Подготовить» в мастере (step 3, режим «дать логин администратора») теперь ставит
  и процедуры обработки bj2depo (CREATE OR ALTER) + GRANT, не только логин/таблицы/джобу.
- Скачиваемый скрипт мастера включает процедуры.
- Тексты процедур редактируются под клиента (Настройка → Обмен DEPO → Процедуры):
  сохранённый вариант используется при установке и при «Подготовить». Пусто/совпадение
  со встроенным — исходная версия.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q7eREaYwUt1i4fNLZcGnjn
2026-10-20 14:12:54 +03:00

251 lines
34 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{/* Раздел «Обмен Fancy» — один и тот же в «Настройка → Обмен Fancy» и на шаге 3 мастера
настройки (данные — SetupData). Правки — только здесь. */}}
{{define "fancySection"}}
<div class="card" id="sec-fancy-conn">
<h2><span class="dot {{if .Settings.Fancy.DepoTablesCheckOK}}ok{{else if .Settings.Fancy.Server}}warn{{else}}err{{end}}"></span>Подключение к серверу БД клиента</h2>
<p class="muted">Сервер учётной базы Fancy DEPO: картотека лиц, счета и договоры, остатки, опции (признак квалифицированного инвестора), трансферные таблицы обмена. Поддерживаются <strong>Microsoft SQL Server</strong> и <strong>PostgreSQL</strong>. База указывается ниже, в карточке «Трансферные таблицы DEPO».</p>
<form method="post" action="/admin/setup/fancy" style="margin-top:12px;display:grid;gap:10px;max-width:560px">
<label>Тип СУБД
<select name="db_type" id="fancy-dbtype" style="width:100%" onchange="var p=document.getElementById('fancy-port');if(!p.value||p.value=='1433'||p.value=='5432'){p.value=this.value=='postgres'?'5432':'1433';}">
<option value="mssql" {{if not .Settings.Fancy.IsPostgres}}selected{{end}}>Microsoft SQL Server</option>
<option value="postgres" {{if .Settings.Fancy.IsPostgres}}selected{{end}}>PostgreSQL</option>
</select></label>
<label>Сервер (host или host\instance)
<input type="text" name="server" value="{{.Settings.Fancy.Server}}" placeholder="10.10.10.x" style="width:100%" required></label>
<label>Порт
<input type="number" id="fancy-port" name="port" value="{{if .Settings.Fancy.Port}}{{.Settings.Fancy.Port}}{{else}}{{.Settings.Fancy.DefaultPort}}{{end}}" style="width:100%"></label>
<label>Пользователь (логин БД)
<input type="text" name="user" value="{{.Settings.Fancy.User}}" placeholder="bj_user" style="width:100%" required></label>
<label>Пароль
<input type="password" name="password" placeholder="{{if .Settings.Fancy.Password}}•••••• — оставьте пустым, чтобы не менять{{else}}пароль SQL-логина{{end}}" autocomplete="off" style="width:100%"></label>
<label>Шифрование канала
<select name="encrypt" style="width:100%">
<option value="disable" {{if eq .Settings.Fancy.Encrypt "disable"}}selected{{end}}>disable — без TLS (типично для LAN)</option>
<option value="true" {{if eq .Settings.Fancy.Encrypt "true"}}selected{{end}}>true — с TLS</option>
</select></label>
<div style="display:flex;gap:8px;flex-wrap:wrap"><button type="submit" class="btn btn-ok">Сохранить</button><button type="submit" formaction="/admin/setup/fancy/test" class="btn">Проверить соединение</button></div>
</form>
{{if .Settings.Fancy.Server}}{{template "accPrefix" .}}{{end}}
</div>
<div class="card" id="sec-fancy-depo-tables">
<h2><span class="dot {{if .Settings.Fancy.DepoTablesCheckOK}}ok{{else if .Settings.Fancy.Server}}warn{{else}}err{{end}}"></span>Трансферные таблицы DEPO</h2>
<p class="muted">Через эти таблицы bj обменивается с учётной системой DEPO: поручения <code>FTV_M2M</code> и их строки <code>FTV_M2M_LINES</code>, события <code>FTV_M2M_EVENTS</code>, справочник участников НРД <code>FTV_M2M_HANDBOOK</code>. Таблицы создаются в базе DEPO на сервере из подключения выше ({{if .DepoTables.Postgres}}PostgreSQL{{else}}Microsoft SQL Server{{end}}). Создание можно повторять: добавляются только недостающие таблицы, колонки и индексы, данные не меняются.</p>
<form method="post" action="/admin/setup/fancy/depo-tables" style="margin-top:10px;display:grid;gap:10px;max-width:720px">
<input type="hidden" name="section" value="sec-fancy-depo-tables">
<label>База DEPO
<input type="text" name="depo_database" value="{{.Settings.Fancy.DepoDatabase}}" placeholder="{{.DepoTables.DB}}" pattern="[A-Za-z][A-Za-z0-9_]*" style="width:100%"></label>
<label>Признак ИИС в названии брокерского договора <span class="muted" style="font-weight:400">(с чего начинается номер договора ИИС в DEPO; несколько — через запятую)</span>
<input type="text" name="iis_contract_marker" value="{{.Settings.Fancy.IISContractMarker}}" placeholder="БРИИС" style="width:100%"></label>
<label style="display:flex;gap:8px;align-items:flex-start;font-weight:600"><input type="radio" name="depo_tables_mode" value="auto" {{if eq .Settings.Fancy.DepoTablesMode "auto"}}checked{{end}}> <span>Создаёт bj <span class="muted" style="font-weight:400">— вводите логин с правами на создание таблиц в базе DEPO. Он используется один раз и не сохраняется; bj заодно выдаст права своему логину.</span></span></label>
<label style="display:flex;gap:8px;align-items:flex-start;font-weight:600"><input type="radio" name="depo_tables_mode" value="script" {{if eq .Settings.Fancy.DepoTablesMode "script"}}checked{{end}}> <span>Создаёт администратор СУБД по скрипту <span class="muted" style="font-weight:400">— скачайте скрипт и передайте администратору, после выполнения нажмите «Проверить таблицы».</span></span></label>
<div><button type="submit" class="btn">Сохранить</button></div>
</form>
{{if eq .Settings.Fancy.DepoTablesMode "auto"}}
<form method="post" action="/admin/setup/fancy/depo-tables/create" style="margin-top:12px;display:grid;gap:10px;max-width:720px">
<input type="hidden" name="section" value="sec-fancy-depo-tables">
<div><b>Логин с правами на базу {{.DepoTables.DB}}</b><br>
<span class="muted" style="font-size:12px">{{if .DepoTables.Postgres}}Владелец базы или суперпользователь.{{else}}Роль db_owner в базе (или db_ddladmin + db_securityadmin).{{end}} Если оставить поля пустыми, bj попробует своим логином {{.DepoTables.User}}.</span></div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:10px">
<label>Логин <input type="text" name="admin_user" autocomplete="off" placeholder="{{if .DepoTables.Postgres}}postgres{{else}}sa{{end}}" style="width:100%"></label>
<label>Пароль <input type="password" name="admin_password" autocomplete="new-password" style="width:100%"></label>
</div>
<div><button type="submit" class="btn btn-ok" onclick="return confirm('Создать трансферные таблицы в базе {{.DepoTables.DB}}?')">⚙ Создать таблицы</button></div>
</form>
{{else if eq .Settings.Fancy.DepoTablesMode "script"}}
<div style="margin-top:12px"><b>Скрипт для администратора СУБД</b> — <a href="/admin/setup/fancy/depo-tables/script.sql">скачать {{.DepoTables.ScriptFile}}</a>
<pre class="copyable" style="max-height:360px;overflow:auto">{{.DepoTables.Script}}</pre></div>
{{end}}
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-top:12px">
<form method="post" action="/admin/setup/fancy/depo-tables/check" style="margin:0"><input type="hidden" name="section" value="sec-fancy-depo-tables"><button type="submit" class="btn btn-ok">✓ Проверить таблицы</button></form>
<span class="muted" style="font-size:12px">логином bj: таблицы на месте, колонки совпадают, есть права на чтение и запись</span>
</div>
{{if .Settings.Fancy.DepoTablesCheck}}<p style="margin-top:10px;font-size:13px;{{if .Settings.Fancy.DepoTablesCheckOK}}color:var(--ok){{else}}color:var(--err){{end}}">{{if .Settings.Fancy.DepoTablesCheckOK}}✓{{else}}✗{{end}} {{.Settings.Fancy.DepoTablesCheck}} <span class="muted">({{.Settings.Fancy.DepoTablesCheckAt.Format "02.01.2006 15:04"}})</span></p>{{end}}
</div>
<div class="card" id="sec-fancy-depo-job">
<h2><span class="dot {{if .Settings.Fancy.DepoJobCheckOK}}ok{{else if .Settings.Fancy.DepoJobMode}}warn{{else}}err{{end}}"></span>Где запускается обработка трансферных таблиц DEPO</h2>
<p class="muted">Поручения и события, которые bj пишет в <code>FTV_M2M*</code>, обрабатывает процедура <code>{{.DepoJob.Proc}}</code> в базе DEPO. Выберите, кто её запускает, выполните инструкцию и нажмите «Проверить готовность» — без зелёной отметки обмен с DEPO работать не будет.</p>
<form method="post" action="/admin/setup/fancy/depo-job" style="margin-top:10px;display:grid;gap:10px;max-width:720px">
<input type="hidden" name="section" value="sec-fancy-depo-job">
<label style="display:flex;gap:8px;align-items:flex-start;font-weight:600"><input type="radio" name="depo_job_mode" value="bj" {{if eq .Settings.Fancy.DepoJobMode "bj"}}checked{{end}}> <span>Внутри bj <span class="muted" style="font-weight:400">— bj сам вызывает процедуру по расписанию. На сервере клиента нужно одно право на процедуру. Для SQL Server Express (там нет SQL Agent) и PostgreSQL.</span></span></label>
<label style="display:flex;gap:8px;align-items:flex-start;font-weight:600"><input type="radio" name="depo_job_mode" value="sql-auto" {{if or (eq .Settings.Fancy.DepoJobMode "sql-auto") (not .Settings.Fancy.DepoJobMode)}}checked{{end}}> <span>Джоба SQL Agent, создаёт bj <span class="muted" style="font-weight:400">— рекомендуется: работает независимо от bj. bj создаёт джобу кнопкой — логином администратора SQL (вводится один раз и не сохраняется) или своим логином, если ему выдана роль оператора SQL Agent.</span></span></label>
<label style="display:flex;gap:8px;align-items:flex-start;font-weight:600"><input type="radio" name="depo_job_mode" value="sql-manual" {{if eq .Settings.Fancy.DepoJobMode "sql-manual"}}checked{{end}}> <span>Джоба SQL Agent, настраивает администратор <span class="muted" style="font-weight:400">— передаёте скрипт, bj проверяет, что джоба работает.</span></span></label>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px">
<label>Интервал, секунд <input type="number" name="depo_job_every_sec" min="5" max="3600" value="{{.DepoJob.Every}}" style="width:100%"></label>
<label>Процедура <input type="text" name="depo_job_proc" value="{{.Settings.Fancy.DepoJobProc}}" placeholder="dbo.FTV_M2M_JOB" style="width:100%"></label>
<label>Работает от имени <input type="text" name="depo_job_run_as" value="{{.Settings.Fancy.DepoJobRunAs}}" placeholder="SYSDBA" style="width:100%"></label>
</div>
<p class="muted" style="font-size:12px;margin:0">Процедура обработки выполняется от имени пользователя базы DEPO (у разработчика Fancy это <code>SYSDBA</code>). У клиента такого пользователя может не быть или он называется иначе: «Проверить готовность» проверит, что он есть в базе.</p>
<div><button type="submit" class="btn">Сохранить способ</button></div>
</form>
{{if eq .Settings.Fancy.DepoJobMode "bj"}}
<div style="margin-top:12px"><b>Инструкция для администратора SQL клиента</b> — выполнить один раз:
<pre class="copyable">{{.DepoJob.GrantScript}}</pre></div>
{{else if eq .Settings.Fancy.DepoJobMode "sql-auto"}}
<form method="post" action="/admin/setup/fancy/depo-job/create" style="margin-top:12px;display:grid;gap:8px;max-width:720px">
<input type="hidden" name="section" value="sec-fancy-depo-job">
<div><b>Создать джобу {{.DepoJob.Name}}</b> <span class="muted" style="font-size:12px">— SQL Server Agent должен быть запущен. Логин администратора SQL (sysadmin, например sa) используется один раз и не сохраняется; пусто — bj попробует своим логином.</span></div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:10px">
<label>Логин <input type="text" name="admin_user" autocomplete="off" placeholder="sa" style="width:100%"></label>
<label>Пароль <input type="password" name="admin_password" autocomplete="new-password" style="width:100%"></label>
</div>
<div><button type="submit" class="btn btn-ok" onclick="return confirm('Создать джобу SQL Agent {{.DepoJob.Name}} на сервере клиента?')">⚙ Создать джобу и проверить</button></div>
</form>
<details style="margin-top:8px"><summary class="muted" style="cursor:pointer;font-size:12px">Без логина администратора: выдать логину bj роль оператора SQL Agent (скрипт)</summary>
<pre class="copyable">{{.DepoJob.AgentRights}}</pre></details>
{{else if eq .Settings.Fancy.DepoJobMode "sql-manual"}}
<div style="margin-top:12px"><b>Инструкция для администратора SQL клиента</b> — создать джобу (SQL Agent должен быть запущен, служебная учётная запись Agent должна иметь право выполнять процедуру):
<a href="/admin/setup/fancy/depo-job/script.sql">Скачать скрипт bj-depo-job-mssql.sql</a>
<pre class="copyable">{{.DepoJob.AdminScript}}</pre></div>
{{end}}
{{if .Settings.Fancy.DepoJobMode}}
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-top:12px">
<form method="post" action="/admin/setup/fancy/depo-job/check" style="margin:0"><input type="hidden" name="section" value="sec-fancy-depo-job"><button type="submit" class="btn btn-ok">✓ Проверить готовность</button></form>
<span class="muted" style="font-size:12px">для вариантов с джобой bj пишет служебное событие BJ_PING и ждёт до 40 с, пока обработчик его отметит</span>
</div>
{{if .Settings.Fancy.DepoJobCheck}}<p style="margin-top:10px;font-size:13px;{{if .Settings.Fancy.DepoJobCheckOK}}color:var(--ok){{else}}color:var(--err){{end}}">{{if .Settings.Fancy.DepoJobCheckOK}}✓{{else}}✗{{end}} {{.Settings.Fancy.DepoJobCheck}} <span class="muted">({{.Settings.Fancy.DepoJobCheckAt.Format "02.01.2006 15:04"}})</span></p>{{end}}
{{if .Settings.Fancy.DepoProcCheck}}<p style="margin-top:4px;font-size:13px;{{if .Settings.Fancy.DepoProcCheckOK}}color:var(--ok){{else}}color:var(--warn){{end}}">Обработка событий (процедуры): {{if .Settings.Fancy.DepoProcCheckOK}}✓{{else}}⚠{{end}} {{.Settings.Fancy.DepoProcCheck}}</p>{{end}}
{{if and (eq .Settings.Fancy.DepoJobMode "bj") .Settings.Fancy.DepoJobLast}}<p class="muted" style="font-size:12px;margin-top:6px">Последний вызов процедуры — {{.Settings.Fancy.DepoJobLast}}</p>{{end}}
{{end}}
</div>
<div class="card" id="sec-fancy-depo-procs">
<h2><span class="dot {{if .Settings.Fancy.DepoProcCheckOK}}ok{{else}}warn{{end}}"></span>Процедуры обработки bj2depo</h2>
<p class="muted">Процедуры <code>FTV_M2M_PROCESS_*</code> и <code>FTV_M2M_JOB</code> встроены в bj. Сначала «Проверить возможности» (ничего не меняет), затем «Установить» — bj выполнит их в базе DEPO в порядке WIRING&rarr;OPERATION&rarr;EVENTS&rarr;CREATE&rarr;JOB и выдаст логину bj право запуска.</p>
<form method="post" style="display:grid;gap:10px;max-width:720px">
<input type="hidden" name="section" value="sec-fancy-depo-procs">
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:10px">
<label>Логин администратора SQL <input type="text" name="admin_user" autocomplete="off" placeholder="sa" style="width:100%"></label>
<label>Пароль <input type="password" name="admin_password" autocomplete="new-password" style="width:100%"></label>
</div>
<p class="muted" style="font-size:12px;margin:0">Логин sysadmin (например sa) вводится один раз и не сохраняется. Пусто — bj попробует своим логином (прав на создание процедур у него обычно нет).</p>
<div style="display:flex;gap:10px;flex-wrap:wrap">
<button type="submit" class="btn" formaction="/admin/setup/fancy/depo-procs/check">Проверить возможности</button>
<button type="submit" class="btn btn-ok" formaction="/admin/setup/fancy/depo-procs/install" onclick="return confirm('Установить процедуры обработки в базу DEPO {{.Settings.Fancy.DepoDB}}?')">⚙ Установить процедуры</button>
</div>
</form>
<p style="margin-top:8px"><a href="/admin/setup/fancy/depo-procs/script.sql">Скачать скрипт bj-depo-procs-mssql.sql</a> <span class="muted" style="font-size:12px">— для ручной установки администратором в SSMS</span></p>
<details style="margin-top:10px"><summary class="muted" style="cursor:pointer">Тексты процедур — изменить под клиента</summary>
<form method="post" action="/admin/setup/fancy/depo-procs/save-text" style="display:grid;gap:10px;margin-top:10px">
<input type="hidden" name="section" value="sec-fancy-depo-procs">
{{range depoProcList}}<label style="font-weight:600;font-size:13px">{{.Name}}<textarea name="proc_{{.Name}}" rows="10" spellcheck="false" style="width:100%;font-family:ui-monospace,monospace;font-size:11.5px">{{depoProcText $.Settings.Fancy .Name}}</textarea></label>{{end}}
<div><button type="submit" class="btn">Сохранить тексты</button> <span class="muted" style="font-size:12px">пустое поле или совпадение со встроенным — вернуть исходную версию; изменённые ставятся при «Установить» и при «Подготовить» в мастере</span></div>
</form></details>
{{if .Settings.Fancy.DepoProcCheck}}<p style="margin-top:6px;font-size:13px;{{if .Settings.Fancy.DepoProcCheckOK}}color:var(--ok){{else}}color:var(--warn){{end}}">{{if .Settings.Fancy.DepoProcCheckOK}}✓{{else}}⚠{{end}} {{.Settings.Fancy.DepoProcCheck}}</p>{{end}}
</div>
{{end}}
{{define "verline"}}{{if .Title}}<p class="muted" style="font-size:12.5px;margin:-6px 0 12px">{{.Title}}: {{if .Version}}<strong>{{.Version}}</strong>{{else}}не определена{{end}}{{with .Detail}} · {{.}}{{end}}{{with .Err}} · <span style="color:var(--err)">{{.}}</span>{{end}}</p>{{end}}{{end}}
{{/* Сводка «Обмен с DEPO» (Настройка): что подключено и проверено — одним взглядом; формы — под «Перенастроить». */}}
{{define "fancyStatus"}}{{$f := .Settings.Fancy}}{{$ok := and $f.Server $f.DepoTablesCheckOK $f.DepoJobCheckOK}}
<div class="card" id="sec-fancy-status">
<div class="stc {{if $ok}}stc-ok{{else if $f.Server}}stc-err{{else}}stc-warn{{end}}"><span class="stc-ic"></span>
<div class="stc-b"><b>{{if $ok}}Обмен с DEPO работает{{else if $f.Server}}Обмен с DEPO настроен не до конца{{else}}Учёт DEPO не подключён{{end}}</b>
<small>{{if $ok}}Подключение, трансферные таблицы и обработка проверены — менять ничего не нужно. Если что-то изменилось на сервере DEPO — «Перенастроить» ниже.{{else if $f.Server}}Что не так — в строках ниже с ✗; поправить — в «Перенастроить».{{else}}Подключите базу DEPO — мастером настройки (шаг «Учёт DEPO») или в «Перенастроить».{{end}}</small></div>
{{if not $f.Server}}<a class="btn" href="/admin/wizard?step=3" style="flex:none">Открыть мастер</a>{{end}}</div>
{{if $f.Server}}
<dl class="kv">
<dt>База DEPO</dt><dd><b>{{$f.DepoDB}}</b> на {{$f.Server}}{{if $f.Port}}:{{$f.Port}}{{end}} · {{if eq $f.DBType "postgres"}}PostgreSQL{{else}}Microsoft SQL Server{{end}}</dd>
<dt>Логин bj</dt><dd><code>{{$f.User}}</code></dd>
<dt>Торговые счета клиентов</dt><dd>номер начинается с <code>{{depoAccPrefix}}</code></dd>
<dt>Признак ИИС</dt><dd>договор начинается с <code>{{if $f.IISContractMarker}}{{$f.IISContractMarker}}{{else}}БРИИС{{end}}</code> — такие переводы отклоняются кодом M2M06</dd>
<dt>Трансферные таблицы</dt><dd>{{if $f.DepoTablesCheckOK}}<span class="kv-ok">✓</span>{{else}}<span class="kv-no">✗</span>{{end}} {{if $f.DepoTablesCheck}}{{$f.DepoTablesCheck}}{{else}}ещё не проверялись{{end}}{{if not $f.DepoTablesCheckAt.IsZero}} <span class="muted">· {{msk $f.DepoTablesCheckAt}}</span>{{end}}</dd>
<dt>Обработка событий</dt><dd>{{if $f.DepoJobCheckOK}}<span class="kv-ok">✓</span>{{else}}<span class="kv-no">✗</span>{{end}} {{if eq $f.DepoJobMode "bj"}}запускает bj{{if $f.DepoJobEverySec}} каждые {{$f.DepoJobEverySec}} с{{end}}{{else if eq $f.DepoJobMode "sql-auto"}}задание SQL Server Agent <code>{{depoJobNameOf $f}}</code>{{else if eq $f.DepoJobMode "sql-manual"}}задание SQL Server Agent (создано администратором){{else}}не выбрано, кто запускает{{end}}{{if $f.DepoJobCheck}}<div class="muted" style="font-size:12.5px;margin-top:2px">{{$f.DepoJobCheck}}{{if not $f.DepoJobCheckAt.IsZero}} · {{msk $f.DepoJobCheckAt}}{{end}}</div>{{end}}</dd>
{{if $f.DepoProcCheck}}<dt>Процедуры обработки</dt><dd>{{if $f.DepoProcCheckOK}}<span class="kv-ok">✓</span>{{else}}<span class="kv-warn">⚠</span>{{end}} {{$f.DepoProcCheck}}{{if not $f.DepoProcCheckAt.IsZero}} <span class="muted">· {{msk $f.DepoProcCheckAt}}</span>{{end}}</dd>{{end}}
</dl>
{{end}}
</div>
{{end}}
{{/* Секция «Обновления»: общий блок для страницы настроек и шага 14 мастера. */}}
{{define "updateSection"}}
{{if .InWizard}}<div id="sec-update">{{else}}<section class="settings-section" id="sec-update">{{end}}
{{if not .InWizard}}<h1>Обновления</h1>{{end}}
{{template "verline" (.Versions.Get "bj-server")}}
<div class="card" id="upd-status" data-last="{{if not .Settings.Update.LastCheck.IsZero}}{{.Settings.Update.LastCheck.Unix}}{{else}}0{{end}}" data-has="{{if .UpdatesAvailable}}1{{else}}0{{end}}">
{{if .UpdatesAvailable}}
<div class="stc stc-warn"><span class="stc-ic"></span><div class="stc-b"><b>Доступно обновление{{with .Settings.Update.Available}} {{.}}{{end}}</b><small>{{if .Settings.Update.Notes}}{{.Settings.Update.Notes}}{{else}}новые версии компонентов — список ниже, в «Компоненты и версии»{{end}}</small></div>
<form method="post" action="/admin/setup/update/apply" style="margin:0" onsubmit="return confirm('Установить обновление? Службы, которые обновляются, перезапустятся на несколько секунд.');"><input type="hidden" name="section" value="sec-update"><button type="submit" class="btn">Установить обновление</button></form></div>
{{else if and (not .Settings.Update.LastCheck.IsZero) .UpdateFailed}}
<div class="stc stc-err"><span class="stc-ic"></span><div class="stc-b"><b>Проверить обновления не удалось</b><small>{{.Settings.Update.LastResult}} · {{msk .Settings.Update.LastCheck}}</small></div>
<button type="button" class="btn btn-secondary" data-upd-check>Проверить ещё раз</button></div>
{{else}}
<div class="stc stc-ok"><span class="stc-ic"></span><div class="stc-b"><b>Установлены последние версии</b><small id="upd-when">{{if .Settings.Update.LastCheck.IsZero}}ещё не проверялось{{else}}проверено {{msk .Settings.Update.LastCheck}}{{end}}{{if .Settings.Update.AutoCheck}} · bj проверяет сам каждые 6 часов{{end}}</small></div>
<button type="button" class="btn btn-secondary" data-upd-check>Проверить сейчас</button></div>
{{end}}
{{if and .License.Present (not .License.AllowsUpdates)}}<p class="muted" style="margin-top:6px;font-size:12px;color:var(--warn)">В лицензии нет модуля «Обновления».</p>{{end}}
</div>
<script>(function(){
var box=document.getElementById('upd-status'), sec=document.getElementById('sec-update'), busy=false;
function check(force){
if(busy) return; var last=+box.dataset.last||0, now=Date.now()/1000, tried=0;
try{ tried=+sessionStorage.getItem('bj-upd-auto')||0; }catch(e){}
if(!force && (now-last<600 || now-tried<600)) return; // проверяли меньше 10 минут назад (или уже пробовали сами)
try{ sessionStorage.setItem('bj-upd-auto',String(Math.floor(now))); }catch(e){}
busy=true; var b=box.querySelector('.stc'); if(b){ b.className='stc stc-run'; var t=b.querySelector('b'); if(t) t.textContent='Проверяю обновления…'; }
fetch('/admin/setup/update/check',{method:'POST',headers:{'X-BJ-Ajax':'1'},cache:'no-store'}).then(function(r){return r.json()}).then(function(d){
box.dataset.last=String(Math.floor(Date.now()/1000));
location.reload(); // состояние компонентов — с сервера
}).catch(function(){ busy=false; location.reload(); });
}
box.addEventListener('click',function(e){ if(e.target.closest('[data-upd-check]')) check(true); });
document.querySelectorAll('.settings-nav [data-sec="update"]').forEach(function(b){ b.addEventListener('click',function(){ setTimeout(function(){ check(false); },0); }); });
window.addEventListener('load',function(){ if(sec.classList.contains('active')) check(false); });
})();</script>
<div class="card">
<details class="adv-box" {{if .UpdatesAvailable}}open{{end}}>
<summary><span class="sum-h">Компоненты и версии</span><span class="muted">что установлено на этом сервере и что есть в выпуске; можно обновить один компонент</span></summary>
<div style="overflow-x:auto"><table style="margin-top:8px">
<thead><tr><th>Компонент</th><th>Установлена</th><th>Доступна</th><th>Служба</th><th></th></tr></thead>
<tbody>
{{range .Components}}<tr>
<td><strong>{{.Title}}</strong><div class="muted" style="font-size:11px"><code>{{.Name}}</code>{{if eq .Name "crypto-service.jar"}}{{with $.Versions.Get "validata"}}{{if .Version}} · работает с «Валидата Клиент L» {{.Version}}{{end}}{{end}}{{end}}</div></td>
<td>{{if .Present}}{{if .Installed}}{{.Installed}}{{else}}<span class="muted">неизвестна</span>{{end}}{{else}}<span class="muted">не установлен</span>{{end}}</td>
<td>{{if .Available}}{{.Available}}{{if .New}} <span class="hero-status warn">к установке</span>{{else if .Update}} <span class="hero-status warn">новее</span>{{end}}{{if .Blocked}}<div class="muted" style="font-size:11px;color:var(--warn)">{{.Blocked}}</div>{{end}}{{else}}<span class="muted">нет данных</span>{{end}}</td>
<td><code style="font-size:11px">{{.Unit}}</code></td>
<td>{{if .Update}}<form method="post" action="/admin/setup/update/apply" style="margin:0" onsubmit="return confirm('{{if .New}}Установить модуль {{.Available}}? Будет включена его служба.{{else}}Обновить компонент до {{.Available}}? Его служба перезапустится.{{end}}');"><input type="hidden" name="section" value="sec-update"><input type="hidden" name="component" value="{{.Name}}"><button type="submit" class="btn btn-ok">{{if .New}}Установить{{else}}Обновить{{end}}</button></form>{{end}}</td>
</tr>{{end}}
</tbody>
</table></div>
</details>
</div>
<div class="card">
<details class="adv-box">
<summary><span class="sum-h">Расширенные настройки</span><span class="muted">источник и канал обновлений, обновление из файла для сервера без выхода в интернет</span></summary>
<h3 class="adv-h">Обновление из файла</h3>
<p class="muted" style="margin:0 0 8px;font-size:12.5px"><b>Закрытый контур.</b> Если у сервера нет выхода к серверу обновлений, скачайте пакет обновления на портале клиента (lic.zetit.ru → Установка → «Пакет обновления») и загрузите его кнопкой «Обновить из файла». Система сама распознаёт, что внутри — ядро, модули или всё вместе — и ставит в правильном порядке. Модуль, которому нужно более новое ядро, чем установлено, будет пропущен с подсказкой обновить сначала ядро.</p>
<div style="display:flex;gap:10px;flex-wrap:wrap">
<form method="post" action="/admin/setup/update/upload" enctype="multipart/form-data" style="margin:0;display:flex;gap:8px;align-items:center;flex-wrap:wrap" onsubmit="return this.bundle.files.length>0 && confirm('Установить обновление из файла? Подпись пакета будет проверена; поставятся компоненты новее текущих — сначала ядро, затем модули. Службы перезапустятся.');">
<input type="hidden" name="section" value="sec-update">
<input type="file" name="bundle" accept=".zip,application/zip" required style="max-width:260px">
<button type="submit" class="btn btn-secondary" title="Пакет обновления с портала клиента — для сервера без выхода к серверу обновлений">Обновить из файла</button>
</form>
</div>
<h3 class="adv-h">Источник обновлений</h3>
<form method="post" action="/admin/setup/update" style="margin-top:12px;display:grid;gap:10px;max-width:680px">
<label>URL артефактории</label>
<input type="text" name="base_url" value="{{.Settings.Update.BaseURL}}" placeholder="https://lic.zetit.ru/updates (по умолчанию)">
<label>Канал</label>
<select name="channel">
<option value="stable" {{if eq .Settings.Update.Channel "stable"}}selected{{end}}>stable — стабильный</option>
<option value="beta" {{if eq .Settings.Update.Channel "beta"}}selected{{end}}>beta — предварительный</option>
</select>
<label>Публичный ключ издателя (base64 Ed25519)</label>
<input type="text" name="public_key" value="{{.Settings.Update.PublicKey}}" placeholder="зашит в релиз; переопределить здесь">
<label style="display:flex;align-items:center;gap:8px;cursor:pointer"><input type="checkbox" name="auto_check" {{if .Settings.Update.AutoCheck}}checked{{end}} style="width:auto"> Проверять автоматически (раз в 6 часов)</label>
<div><button type="submit" class="btn">Сохранить</button></div>
</form>
</details>
</div>
{{if .InWizard}}</div>{{else}}</section>{{end}}
{{end}}