CI / test (push) Successful in 1m29s
- сервер отдаёт агенту итоговый каталог (встроенный каталог сервера + переопределения клиента); если каталог не подходит версии агента — встроенный каталог агента и предупреждение - самообновление перед бэкапом: версия сервера новее → скачивание с того же сервера (только /dl/...), проверка sha256 и запуска новой версии, подмена с откатом, перезапуск через exec; при ошибке бэкап идёт текущей версией - галочка «Автообновление» у клиента (по умолчанию включена), версия агента в списке и карточке, отставшие подсвечены; версия сервера в шапке - миграция существующей базы: колонки auto_update и agent_version - backupctl backup --no-update Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
68 lines
3.8 KiB
HTML
68 lines
3.8 KiB
HTML
{{define "title"}}Клиенты — backup-master{{end}}
|
||
{{define "content"}}
|
||
{{$v := .V}}{{$q := .V.Query}}
|
||
<div class="head">
|
||
<h1>Клиенты <span class="muted small">{{$v.Total}}</span></h1>
|
||
<a class="button primary" href="/clients/new">Добавить клиента</a>
|
||
</div>
|
||
{{if $v.Total}}
|
||
<form method="get" action="/" class="toolbar">
|
||
<input type="search" name="q" value="{{$q.Q}}" placeholder="Поиск по домену или названию" autofocus>
|
||
{{if $q.Status}}<input type="hidden" name="status" value="{{$q.Status}}">{{end}}
|
||
{{if ne $q.Sort "domain"}}<input type="hidden" name="sort" value="{{$q.Sort}}">{{end}}
|
||
{{with $q.DirParam}}<input type="hidden" name="dir" value="{{.}}">{{end}}
|
||
<select name="per" onchange="this.form.submit()" title="Строк на странице">
|
||
{{range $v.PerOpts}}<option value="{{.}}" {{if eq . $q.Per}}selected{{end}}>по {{.}}</option>{{end}}
|
||
</select>
|
||
<button>Найти</button>
|
||
{{if or $q.Q $q.Status}}<a class="button" href="/">Сбросить</a>{{end}}
|
||
</form>
|
||
<div class="chips toolbar">
|
||
{{range $v.Chips}}<a class="chip {{if .Active}}active{{end}}" href="{{.URL}}">{{.Title}} <span class="count">{{.Count}}</span></a>{{end}}
|
||
</div>
|
||
|
||
{{if $v.Rows}}
|
||
<div class="table-wrap">
|
||
<table>
|
||
<thead><tr>
|
||
<th><a href="{{$q.SortURL "domain"}}" {{if eq $q.Sort "domain"}}class="sorted"{{end}}>Домен {{$q.SortMark "domain"}}</a></th>
|
||
<th><a href="{{$q.SortURL "name"}}" {{if eq $q.Sort "name"}}class="sorted"{{end}}>Название {{$q.SortMark "name"}}</a></th>
|
||
<th><a href="{{$q.SortURL "status"}}" {{if eq $q.Sort "status"}}class="sorted"{{end}}>Статус {{$q.SortMark "status"}}</a></th>
|
||
<th><a href="{{$q.SortURL "last_backup"}}" {{if eq $q.Sort "last_backup"}}class="sorted"{{end}}>Последний бэкап {{$q.SortMark "last_backup"}}</a></th>
|
||
<th class="num"><a href="{{$q.SortURL "size"}}" {{if eq $q.Sort "size"}}class="sorted"{{end}}>Размер {{$q.SortMark "size"}}</a></th>
|
||
<th><a href="{{$q.SortURL "last_seen"}}" {{if eq $q.Sort "last_seen"}}class="sorted"{{end}}>Агент на связи {{$q.SortMark "last_seen"}}</a></th>
|
||
<th>Версия</th>
|
||
</tr></thead>
|
||
<tbody>
|
||
{{range $v.Rows}}
|
||
<tr>
|
||
<td><a href="/clients/{{.ID}}">{{.Domain}}</a></td>
|
||
<td>{{if ne .Name .Domain}}{{.Name}}{{end}}</td>
|
||
<td><span class="badge {{.StatusClass}}">{{.StatusText}}</span>{{if and (eq .StatusClass "fail") .LastError}} <span class="muted small" title="{{.LastError}}">{{.LastError}}</span>{{end}}</td>
|
||
<td title="{{ts .LastBackupAt}}">{{ago .LastBackupAt}}</td>
|
||
<td class="num">{{if .LastSize}}{{bytes .LastSize}}{{end}}</td>
|
||
<td title="{{ts .LastSeenAt}}">{{ago .LastSeenAt}}</td>
|
||
<td>{{if .Outdated}}<span class="badge stale" title="{{if .AutoUpdate}}обновится при следующем бэкапе{{else}}автообновление выключено{{end}}">{{.AgentVersion}}</span>{{else}}<span class="muted small">{{.AgentVersion}}</span>{{end}}</td>
|
||
</tr>
|
||
{{end}}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="pager">
|
||
<span>{{$v.From}}–{{$v.To}} из {{$v.Found}}{{if ne $v.Found $v.Total}} (всего {{$v.Total}}){{end}}</span>
|
||
{{if $v.Pages}}
|
||
<div class="pages">
|
||
{{if $v.Prev}}<a href="{{$v.Prev}}">←</a>{{end}}
|
||
{{range $v.Pages}}{{if .Gap}}<span class="gap">…</span>{{else if .Current}}<span class="current">{{.N}}</span>{{else}}<a href="{{.URL}}">{{.N}}</a>{{end}}{{end}}
|
||
{{if $v.Next}}<a href="{{$v.Next}}">→</a>{{end}}
|
||
</div>
|
||
{{end}}
|
||
</div>
|
||
{{else}}
|
||
<p class="empty">Ничего не найдено. <a href="/">Сбросить фильтры</a></p>
|
||
{{end}}
|
||
{{else}}
|
||
<p class="empty">Клиентов пока нет. Добавьте первый — получите команду установки агента.</p>
|
||
{{end}}
|
||
{{end}}
|