Add Go rewrite with Postgres 17 and Dokploy Docker Compose
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
{{define "page_admin_configs"}}
|
||||
{{template "admin_shell_open" dict "Active" .Active "Admin" .Admin "Title" "Конфигурации"}}
|
||||
{{template "admin_flash" .}}
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header"><h2><i class="fa-solid fa-filter"></i> Фильтр</h2></div>
|
||||
<form method="get" action="{{appURL "admin/configs"}}" class="d-flex flex-wrap gap-2 align-items-end">
|
||||
<div>
|
||||
<label class="form-label">ID ссылки</label>
|
||||
<input class="form-control input-inline" type="number" name="link_id" value="{{if .LinkIDFilter}}{{.LinkIDFilter}}{{end}}">
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">ID сервера</label>
|
||||
<input class="form-control input-inline" type="number" name="server_id" value="{{if .ServerIDFilter}}{{.ServerIDFilter}}{{end}}">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-ghost"><i class="fa-solid fa-magnifying-glass"></i> Показать</button>
|
||||
<a class="btn btn-ghost" href="{{appURL "admin/configs"}}">Сбросить</a>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header"><h2><i class="fa-solid fa-key"></i> Конфигурации ({{len .Rows}}{{if .Truncated}}+{{end}})</h2></div>
|
||||
{{if .Truncated}}<p class="hint">Показаны не все записи — сузьте фильтр по ссылке или серверу.</p>{{end}}
|
||||
{{if not .Rows}}
|
||||
<p class="hint">Ничего не найдено.</p>
|
||||
{{else}}
|
||||
<div class="tbl-wrap">
|
||||
<table class="table table-sm mb-0">
|
||||
<thead><tr><th>ID</th><th>Ссылка</th><th>Сервер</th><th>Протокол</th><th>Имя</th><th>Создана</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
{{range .Rows}}
|
||||
<tr>
|
||||
<td>#{{.ID}}</td>
|
||||
<td><a href="{{appURL "admin/links"}}?id={{.LinkID}}" class="mono">#{{.LinkID}}</a></td>
|
||||
<td>{{.ServerID}}</td>
|
||||
<td>{{.Protocol}}</td>
|
||||
<td class="mono">{{.ConnectionName}}</td>
|
||||
<td class="sl-muted">{{.CreatedAt.Format "2006-01-02 15:04"}}</td>
|
||||
<td>
|
||||
<form method="post" action="{{appURL "admin/configs"}}" onsubmit="return confirm('Удалить конфигурацию #{{.ID}}?');">
|
||||
{{template "admin_csrf_field"}}
|
||||
<input type="hidden" name="action" value="delete_creation">
|
||||
<input type="hidden" name="link_id" value="{{.LinkID}}">
|
||||
<input type="hidden" name="creation_id" value="{{.ID}}">
|
||||
<button type="submit" class="btn btn-sm btn-danger"><i class="fa-solid fa-trash"></i></button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{template "admin_shell_close" .}}
|
||||
{{end}}
|
||||
@@ -0,0 +1,39 @@
|
||||
{{define "page_admin_index"}}
|
||||
{{template "admin_shell_open" dict "Active" .Active "Admin" .Admin "Title" "Дашборд"}}
|
||||
{{template "admin_flash" .}}
|
||||
|
||||
{{if .Maintenance}}<div class="alert-err"><i class="fa-solid fa-screwdriver-wrench"></i> Включён режим технических работ. <a href="{{appURL "admin/settings"}}">Отключить в настройках</a></div>{{end}}
|
||||
|
||||
<div class="stat-grid">
|
||||
<div class="stat-tile"><i class="fa-solid fa-link"></i><div><span class="stat-val">{{.TotalLinks}}</span><span class="stat-label">Всего ссылок</span></div></div>
|
||||
<div class="stat-tile"><i class="fa-solid fa-circle-check"></i><div><span class="stat-val">{{.ActiveLinks}}</span><span class="stat-label">Активные</span></div></div>
|
||||
<div class="stat-tile"><i class="fa-solid fa-hourglass-end"></i><div><span class="stat-val">{{.ExpiredLinks}}</span><span class="stat-label">Истёкшие</span></div></div>
|
||||
<div class="stat-tile"><i class="fa-solid fa-broom"></i><div><span class="stat-val">{{.CleanedLinks}}</span><span class="stat-label">Очищенные</span></div></div>
|
||||
</div>
|
||||
<div class="stat-grid stat-grid-compact">
|
||||
<div class="stat-tile"><i class="fa-solid fa-ban"></i><div><span class="stat-val">{{.LimitLinks}}</span><span class="stat-label">Лимит исчерпан</span></div></div>
|
||||
<div class="stat-tile"><i class="fa-solid fa-rotate"></i><div><span class="stat-val">{{.CodesCount}}</span><span class="stat-label">Кодов продления</span></div></div>
|
||||
<div class="stat-tile">
|
||||
<i class="fa-solid fa-server"></i>
|
||||
<div>
|
||||
<span class="stat-val">{{if .PanelOK}}OK{{else if .PanelURL}}Ошибка{{else}}—{{end}}</span>
|
||||
<span class="stat-label">Панель Amnezia</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{if .PanelErr}}<div class="alert-err"><i class="fa-solid fa-triangle-exclamation"></i> Панель недоступна: {{.PanelErr}}</div>{{end}}
|
||||
{{if not .PanelURL}}<div class="alert-err"><i class="fa-solid fa-triangle-exclamation"></i> URL панели и токен не заданы. <a href="{{appURL "admin/settings"}}">Перейти в настройки</a></div>{{end}}
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header"><h2><i class="fa-solid fa-bolt"></i> Быстрые действия</h2></div>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<a class="btn btn-primary" href="{{appURL "admin/links"}}"><i class="fa-solid fa-plus"></i> Ссылки</a>
|
||||
<a class="btn btn-ghost" href="{{appURL "admin/configs"}}"><i class="fa-solid fa-key"></i> Конфигурации</a>
|
||||
<a class="btn btn-ghost" href="{{appURL "admin/renewal"}}"><i class="fa-solid fa-rotate"></i> Коды продления</a>
|
||||
<a class="btn btn-ghost" href="{{appURL "admin/servers"}}"><i class="fa-solid fa-server"></i> Серверы</a>
|
||||
<a class="btn btn-ghost" href="{{appURL "admin/settings"}}"><i class="fa-solid fa-gear"></i> Настройки</a>
|
||||
</div>
|
||||
</div>
|
||||
{{template "admin_shell_close" .}}
|
||||
{{end}}
|
||||
@@ -0,0 +1,180 @@
|
||||
{{define "page_admin_links"}}
|
||||
{{template "admin_shell_open" dict "Active" .Active "Admin" .Admin "Title" "Ссылки"}}
|
||||
{{template "admin_flash" .}}
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header"><h2><i class="fa-solid fa-plus"></i> Новая ссылка</h2></div>
|
||||
<form method="post" action="{{appURL "admin/links"}}" class="form-row-2">
|
||||
{{template "admin_csrf_field"}}
|
||||
<input type="hidden" name="action" value="create">
|
||||
<div>
|
||||
<label class="form-label">Срок (дней от первого конфига)</label>
|
||||
<select class="form-select" name="days">
|
||||
{{range .AllowedDurations}}<option value="{{.}}">{{.}} дн.</option>{{end}}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Лимит конфигов</label>
|
||||
<input class="form-control" type="number" name="max_uses" value="5" min="1" max="1000">
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Подписка до (необязательно)</label>
|
||||
<input class="form-control" type="date" name="subscription_until">
|
||||
</div>
|
||||
<div class="d-flex align-items-end">
|
||||
<button type="submit" class="btn btn-primary w-100"><i class="fa-solid fa-plus"></i> Создать</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header"><h2><i class="fa-solid fa-filter"></i> Список ссылок ({{.Total}})</h2></div>
|
||||
<form method="get" action="{{appURL "admin/links"}}" class="links-toolbar mb-2">
|
||||
<select class="form-select form-select-sm" name="status" style="max-width:220px;" onchange="this.form.submit()">
|
||||
<option value="" {{if eq2 .StatusFilter ""}}selected{{end}}>Все статусы</option>
|
||||
<option value="active" {{if eq2 .StatusFilter "active"}}selected{{end}}>Активные</option>
|
||||
<option value="expired" {{if eq2 .StatusFilter "expired"}}selected{{end}}>Истёкшие</option>
|
||||
<option value="limit" {{if eq2 .StatusFilter "limit"}}selected{{end}}>Лимит исчерпан</option>
|
||||
<option value="cleaned" {{if eq2 .StatusFilter "cleaned"}}selected{{end}}>Очищенные</option>
|
||||
</select>
|
||||
</form>
|
||||
|
||||
{{if not .Links}}
|
||||
<p class="hint">Ссылок не найдено.</p>
|
||||
{{else}}
|
||||
<div class="tbl-wrap">
|
||||
<table class="table table-sm mb-0">
|
||||
<thead><tr><th>ID</th><th>Токен</th><th>Статус</th><th>Использовано</th><th>Срок</th><th>Конфигов</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
{{range .Links}}
|
||||
<tr>
|
||||
<td class="sl-td-id">#{{.ID}}</td>
|
||||
<td><a class="mono sl-url-link" href="{{appURL "share"}}?k={{.Token}}" target="_blank">{{.Token}}</a></td>
|
||||
<td>
|
||||
{{if eq2 .Status "active"}}<span class="badge badge-ok">активна</span>{{end}}
|
||||
{{if eq2 .Status "expired"}}<span class="badge badge-warn">истекла</span>{{end}}
|
||||
{{if eq2 .Status "limit"}}<span class="badge badge-info">лимит</span>{{end}}
|
||||
{{if eq2 .Status "cleaned"}}<span class="badge badge-err">очищена</span>{{end}}
|
||||
</td>
|
||||
<td>{{.UseCount}} / {{.MaxUses}}</td>
|
||||
<td class="sl-muted">{{.ExpiresLabel}}</td>
|
||||
<td>{{.ActiveConfigs}}</td>
|
||||
<td class="sl-td-actions">
|
||||
<a class="btn btn-sm btn-ghost" href="{{appURL "admin/links"}}?id={{.ID}}"><i class="fa-solid fa-eye"></i></a>
|
||||
<form method="post" action="{{appURL "admin/links"}}" style="display:inline" onsubmit="return confirm('Удалить ссылку #{{.ID}}?');">
|
||||
{{template "admin_csrf_field"}}
|
||||
<input type="hidden" name="action" value="delete">
|
||||
<input type="hidden" name="link_id" value="{{.ID}}">
|
||||
<button type="submit" class="btn btn-sm btn-danger"><i class="fa-solid fa-trash"></i></button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="sl-pager mt-2">
|
||||
{{if gt .Page 1}}<a class="btn btn-sm btn-ghost" href="{{appURL "admin/links"}}?status={{.StatusFilter}}&page={{sub .Page 1}}">« Назад</a>{{end}}
|
||||
<span class="hint d-inline-block mx-2">Страница {{.Page}}</span>
|
||||
{{if gt .Total (mul .Page .PerPage)}}<a class="btn btn-sm btn-ghost" href="{{appURL "admin/links"}}?status={{.StatusFilter}}&page={{add .Page 1}}">Вперёд »</a>{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
{{if .Detail}}
|
||||
<div class="card">
|
||||
<div class="card-header"><h2><i class="fa-solid fa-link"></i> Ссылка #{{.Detail.ID}}</h2></div>
|
||||
<p class="mono">{{appURL "share"}}?k={{.Detail.Token}}</p>
|
||||
|
||||
<form method="post" action="{{appURL "admin/links"}}" class="form-row-2">
|
||||
{{template "admin_csrf_field"}}
|
||||
<input type="hidden" name="action" value="update">
|
||||
<input type="hidden" name="link_id" value="{{.Detail.ID}}">
|
||||
<div>
|
||||
<label class="form-label">Срок (дней)</label>
|
||||
<input class="form-control" type="number" name="validity_days" value="{{if .Detail.ValidityDays}}{{.Detail.ValidityDays}}{{else}}30{{end}}" min="1" max="3650">
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Лимит конфигов</label>
|
||||
<input class="form-control" type="number" name="max_uses" value="{{.Detail.MaxUses}}" min="1" max="1000">
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Явная дата истечения (необязательно)</label>
|
||||
<input class="form-control" type="date" name="expires_at">
|
||||
<div class="form-check mt-1">
|
||||
<input class="form-check-input" type="checkbox" name="floating_expiry" value="1" id="floating_expiry" checked>
|
||||
<label class="form-check-label hint" for="floating_expiry">Плавающий срок (от первого конфига)</label>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Подписка до</label>
|
||||
<input class="form-control" type="date" name="subscription_until">
|
||||
</div>
|
||||
<div class="d-flex align-items-end">
|
||||
<button type="submit" class="btn btn-primary w-100"><i class="fa-solid fa-floppy-disk"></i> Сохранить</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<form method="post" action="{{appURL "admin/links"}}" class="d-flex gap-2 align-items-end mt-3">
|
||||
{{template "admin_csrf_field"}}
|
||||
<input type="hidden" name="action" value="extend">
|
||||
<input type="hidden" name="link_id" value="{{.Detail.ID}}">
|
||||
<input type="hidden" name="back_id" value="{{.Detail.ID}}">
|
||||
<div>
|
||||
<label class="form-label">Продлить на, дней</label>
|
||||
<input class="form-control input-inline" type="number" name="days" value="30" min="1" max="3650">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-ghost"><i class="fa-solid fa-rotate"></i> Продлить</button>
|
||||
</form>
|
||||
|
||||
{{if .AllServers}}
|
||||
<form method="post" action="{{appURL "admin/links"}}" class="mt-3">
|
||||
{{template "admin_csrf_field"}}
|
||||
<input type="hidden" name="action" value="set_allowed_servers">
|
||||
<input type="hidden" name="link_id" value="{{.Detail.ID}}">
|
||||
<input type="hidden" name="back_id" value="{{.Detail.ID}}">
|
||||
<label class="form-label d-block">Разрешённые серверы (пусто = все)</label>
|
||||
<div class="tn-servers__scroll d-flex flex-wrap gap-2 mb-2">
|
||||
{{$allowed := .AllowedServerIDs}}
|
||||
{{range .AllServers}}
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="server_ids[]" value="{{.ID}}" id="srv{{.ID}}" {{if intIn $allowed .ID}}checked{{end}}>
|
||||
<label class="form-check-label" for="srv{{.ID}}">{{.Label}}</label>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
<button type="submit" class="btn btn-sm btn-ghost"><i class="fa-solid fa-floppy-disk"></i> Сохранить список серверов</button>
|
||||
</form>
|
||||
{{end}}
|
||||
|
||||
<h3 class="config-section-title mt-4">Активные конфигурации ({{len .Creations}})</h3>
|
||||
{{if not .Creations}}<p class="hint">Пока нет.</p>{{end}}
|
||||
{{range .Creations}}
|
||||
<div class="creation-card">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="mono">#{{.ID}} · {{.ConnectionName}} · сервер {{.ServerID}} · {{.Protocol}}</span>
|
||||
<form method="post" action="{{appURL "admin/links"}}" onsubmit="return confirm('Удалить конфигурацию #{{.ID}}?');">
|
||||
{{template "admin_csrf_field"}}
|
||||
<input type="hidden" name="action" value="delete_creation">
|
||||
<input type="hidden" name="link_id" value="{{$.Detail.ID}}">
|
||||
<input type="hidden" name="creation_id" value="{{.ID}}">
|
||||
<input type="hidden" name="back_id" value="{{$.Detail.ID}}">
|
||||
<button type="submit" class="btn btn-sm btn-danger"><i class="fa-solid fa-trash"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{if .DeletedCreations}}
|
||||
<h3 class="config-section-title config-section-title--deleted mt-3">Удалённые ({{len .DeletedCreations}})</h3>
|
||||
{{range .DeletedCreations}}
|
||||
<div class="creation-card creation-card--deleted">
|
||||
<span class="mono">#{{.ID}} · {{.ConnectionName}} · сервер {{.ServerID}} · {{.Protocol}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{template "admin_shell_close" .}}
|
||||
{{end}}
|
||||
@@ -0,0 +1,81 @@
|
||||
{{define "page_admin_renewal"}}
|
||||
{{template "admin_shell_open" dict "Active" .Active "Admin" .Admin "Title" "Коды продления"}}
|
||||
{{template "admin_flash" .}}
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header"><h2><i class="fa-solid fa-plus"></i> Новый код</h2></div>
|
||||
<form method="post" action="{{appURL "admin/renewal"}}" class="form-row-2">
|
||||
{{template "admin_csrf_field"}}
|
||||
<input type="hidden" name="action" value="create">
|
||||
<div>
|
||||
<label class="form-label">Код (пусто = сгенерировать)</label>
|
||||
<input class="form-control mono" name="code" placeholder="ABCD1234" maxlength="32">
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Дней продления</label>
|
||||
<input class="form-control" type="number" name="add_days" value="30" min="1" max="3650">
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Лимит активаций</label>
|
||||
<input class="form-control" type="number" name="max_uses" value="1" min="1" max="100000">
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Назначение</label>
|
||||
<select class="form-select" name="code_target">
|
||||
<option value="guest">Гостевая ссылка</option>
|
||||
<option value="member">Личный кабинет</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">ID гостевой ссылки (необязательно)</label>
|
||||
<input class="form-control" type="number" name="share_link_id" min="1">
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Срок действия кода (необязательно)</label>
|
||||
<input class="form-control" type="date" name="code_expires_at">
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Заметка</label>
|
||||
<input class="form-control" name="note" maxlength="255">
|
||||
</div>
|
||||
<div class="d-flex align-items-end">
|
||||
<button type="submit" class="btn btn-primary w-100"><i class="fa-solid fa-plus"></i> Создать</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header"><h2><i class="fa-solid fa-list"></i> Список кодов ({{len .Codes}})</h2></div>
|
||||
{{if not .Codes}}
|
||||
<p class="hint">Кодов пока нет.</p>
|
||||
{{else}}
|
||||
<div class="tbl-wrap">
|
||||
<table class="table table-sm mb-0">
|
||||
<thead><tr><th>Код</th><th>Дней</th><th>Активаций</th><th>Назначение</th><th>Ссылка</th><th>Срок кода</th><th>Заметка</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
{{range .Codes}}
|
||||
<tr>
|
||||
<td class="mono">{{.Code}}</td>
|
||||
<td>{{.AddDays}}</td>
|
||||
<td>{{.UseCount}} / {{.MaxUses}}</td>
|
||||
<td>{{if eq2 .CodeTarget "member"}}<span class="badge badge-info">кабинет</span>{{else}}<span class="badge badge-ok">гость</span>{{end}}</td>
|
||||
<td>{{if .ShareLinkID}}#{{.ShareLinkID}}{{else}}—{{end}}</td>
|
||||
<td class="sl-muted">{{if .CodeExpiresAt}}{{.CodeExpiresAt.Format "2006-01-02"}}{{else}}—{{end}}</td>
|
||||
<td>{{if .Note}}{{.Note}}{{else}}—{{end}}</td>
|
||||
<td>
|
||||
<form method="post" action="{{appURL "admin/renewal"}}" onsubmit="return confirm('Удалить код {{.Code}}?');">
|
||||
{{template "admin_csrf_field"}}
|
||||
<input type="hidden" name="action" value="delete">
|
||||
<input type="hidden" name="code_id" value="{{.ID}}">
|
||||
<button type="submit" class="btn btn-sm btn-danger"><i class="fa-solid fa-trash"></i></button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{template "admin_shell_close" .}}
|
||||
{{end}}
|
||||
@@ -0,0 +1,82 @@
|
||||
{{define "page_admin_servers"}}
|
||||
{{template "admin_shell_open" dict "Active" .Active "Admin" .Admin "Title" "Серверы"}}
|
||||
{{template "admin_flash" .}}
|
||||
|
||||
{{if .PanelErr}}<div class="alert-err"><i class="fa-solid fa-triangle-exclamation"></i> Панель недоступна: {{.PanelErr}}</div>{{end}}
|
||||
{{if not .PanelReachable}}<p class="hint">Панель Amnezia сейчас не отвечает — список серверов основан на сохранённых названиях (panel_server_labels) и настройках.</p>{{end}}
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header"><h2><i class="fa-solid fa-plus"></i> Добавить сервер вручную</h2></div>
|
||||
<form method="post" action="{{appURL "admin/servers"}}" class="d-flex flex-wrap gap-2 align-items-end">
|
||||
{{template "admin_csrf_field"}}
|
||||
<input type="hidden" name="action" value="add">
|
||||
<div>
|
||||
<label class="form-label">ID сервера в панели</label>
|
||||
<input class="form-control input-inline" type="number" name="new_server_id" min="0" required>
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Название</label>
|
||||
<input class="form-control" name="new_server_title" required placeholder="🇩🇪 Германия">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-ghost"><i class="fa-solid fa-plus"></i> Добавить</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header"><h2><i class="fa-solid fa-server"></i> Серверы ({{len .Rows}})</h2></div>
|
||||
{{if not .Rows}}
|
||||
<p class="hint">Серверов пока нет. Задайте URL панели и токен в настройках, либо добавьте сервер вручную.</p>
|
||||
{{else}}
|
||||
<form id="deleteServerForm" method="post" action="{{appURL "admin/servers"}}" class="d-none">
|
||||
{{template "admin_csrf_field"}}
|
||||
<input type="hidden" name="action" value="delete_label">
|
||||
<input type="hidden" id="delServerId" name="server_id" value="">
|
||||
</form>
|
||||
|
||||
<form method="post" action="{{appURL "admin/servers"}}">
|
||||
{{template "admin_csrf_field"}}
|
||||
<input type="hidden" name="action" value="save">
|
||||
{{range .Rows}}<input type="hidden" name="server_ids" value="{{.ID}}">{{end}}
|
||||
|
||||
<div class="tbl-wrap">
|
||||
<table class="table table-sm mb-0 align-middle">
|
||||
<thead><tr><th>ID</th><th>Название</th><th>Протоколы</th><th>Флаг</th><th>Скорость</th><th>Откл.</th><th>Панель</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
{{range .Rows}}
|
||||
{{$row := .}}
|
||||
<tr>
|
||||
<td class="mono">#{{.ID}}</td>
|
||||
<td><input class="form-control form-control-sm" name="servers[{{.ID}}][title]" value="{{.Title}}"></td>
|
||||
<td>
|
||||
{{range $.AllProtocols}}
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="servers[{{$row.ID}}][protocols][]" value="{{.}}" id="p{{$row.ID}}_{{.}}" {{if strIn $row.Protocols .}}checked{{end}}>
|
||||
<label class="form-check-label hint" for="p{{$row.ID}}_{{.}}">{{.}}</label>
|
||||
</div>
|
||||
{{end}}
|
||||
</td>
|
||||
<td><input class="form-control form-control-sm input-inline" name="servers[{{.ID}}][flag]" value="{{.Flag}}" placeholder="🇩🇪"></td>
|
||||
<td><input class="form-control form-control-sm input-inline" name="servers[{{.ID}}][speed]" value="{{.Speed}}" placeholder="1 Gbps"></td>
|
||||
<td class="text-center"><input class="form-check-input" type="checkbox" name="servers[{{.ID}}][disabled]" value="1" {{if .Disabled}}checked{{end}}></td>
|
||||
<td class="sl-muted">{{if .PanelName}}{{.PanelName}}{{else}}—{{end}}</td>
|
||||
<td>
|
||||
<button type="submit" form="deleteServerForm" formnovalidate
|
||||
onclick="document.getElementById('delServerId').value='{{.ID}}'; return confirm('Удалить сервер #{{.ID}} из списка?');"
|
||||
class="btn btn-sm btn-danger">
|
||||
<i class="fa-solid fa-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<button type="submit" class="btn btn-primary"><i class="fa-solid fa-floppy-disk"></i> Сохранить всё</button>
|
||||
</div>
|
||||
</form>
|
||||
{{end}}
|
||||
</div>
|
||||
{{template "admin_shell_close" .}}
|
||||
{{end}}
|
||||
@@ -0,0 +1,80 @@
|
||||
{{define "page_admin_settings"}}
|
||||
{{template "admin_shell_open" dict "Active" .Active "Admin" .Admin "Title" "Настройки"}}
|
||||
{{template "admin_flash" .}}
|
||||
|
||||
{{if .TestResult}}<div class="alert-ok"><i class="fa-solid fa-circle-check"></i> {{.TestResult}}</div>{{end}}
|
||||
{{if .TestError}}<div class="alert-err"><i class="fa-solid fa-triangle-exclamation"></i> {{.TestError}}</div>{{end}}
|
||||
|
||||
<form method="post" action="{{appURL "admin/settings"}}">
|
||||
{{template "admin_csrf_field"}}
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header"><h2><i class="fa-solid fa-server"></i> Панель Amnezia</h2></div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">URL панели</label>
|
||||
<input class="form-control" name="panel_url" value="{{.PanelURL}}" placeholder="https://panel.example.com">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">API-токен</label>
|
||||
<input class="form-control mono" name="api_token" value="{{.APIToken}}" placeholder="awp_...">
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" name="action" value="save" class="btn btn-primary"><i class="fa-solid fa-floppy-disk"></i> Сохранить</button>
|
||||
<button type="submit" name="action" value="test" formnovalidate class="btn btn-ghost"><i class="fa-solid fa-plug-circle-check"></i> Проверить соединение</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header"><h2><i class="fa-solid fa-screwdriver-wrench"></i> Режим работы</h2></div>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" role="switch" id="maintenance" name="maintenance" value="1" {{if .Maintenance}}checked{{end}}>
|
||||
<label class="form-check-label" for="maintenance">Технические работы (запретить создание/перенос конфигов)</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header"><h2><i class="fa-solid fa-key"></i> Вход через Pocket ID (OIDC)</h2></div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">URL Pocket ID</label>
|
||||
<input class="form-control" name="pocket_url" value="{{.PocketURL}}" placeholder="https://id.example.com">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Client ID</label>
|
||||
<input class="form-control mono" name="pocket_client_id" value="{{.PocketClientID}}">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Client Secret</label>
|
||||
<input class="form-control mono" type="password" name="pocket_secret" value="{{.PocketSecret}}" autocomplete="off">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header"><h2><i class="fa-solid fa-address-card"></i> Личный кабинет — значения по умолчанию</h2></div>
|
||||
<div class="form-row-2">
|
||||
<div>
|
||||
<label class="form-label">Дней подписки по умолчанию</label>
|
||||
<input class="form-control" type="number" name="member_days" value="{{.MemberDays}}" min="1" max="3650" placeholder="30">
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Лимит конфигов по умолчанию</label>
|
||||
<input class="form-control" type="number" name="member_max_uses" value="{{.MemberMaxUses}}" min="1" max="1000" placeholder="5">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-compact">
|
||||
<div class="card-header"><h2><i class="fa-solid fa-code"></i> Дополнительно (JSON)</h2></div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label hint-tight">Метки серверов, переопределение (server_labels_json)</label>
|
||||
<textarea class="form-control tn-textarea mono" name="server_labels_json" rows="3" placeholder='{"1":"Германия"}'>{{.ServerLabelsJSON}}</textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label hint-tight">Уведомления о трафике (traffic_notices_json)</label>
|
||||
<textarea class="form-control tn-textarea mono" name="traffic_notices_json" rows="3" placeholder='{"1":"Ограничение 100GB/мес"}'>{{.TrafficNotices}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" name="action" value="save" class="btn btn-primary"><i class="fa-solid fa-floppy-disk"></i> Сохранить все настройки</button>
|
||||
</form>
|
||||
{{template "admin_shell_close" .}}
|
||||
{{end}}
|
||||
Reference in New Issue
Block a user