166 lines
7.6 KiB
HTML
166 lines
7.6 KiB
HTML
{% extends "admin/layout.html" %}
|
|
{% block admin_title %}Серверы{% endblock %}
|
|
{% block admin_content %}
|
|
<div class="admin-top">
|
|
<h1>Серверы</h1>
|
|
</div>
|
|
|
|
{% if flash %}
|
|
{% if flash.startswith('error:') %}
|
|
<div class="flash flash-error">{{ flash[6:] }}</div>
|
|
{% elif flash == 'created' %}
|
|
<div class="flash">Сервер подключён по SSH и сохранён</div>
|
|
{% elif flash == 'saved' %}
|
|
<div class="flash">Сохранено</div>
|
|
{% elif flash == 'deleted' %}
|
|
<div class="flash">Сервер удалён</div>
|
|
{% elif flash == 'ssh_ok' %}
|
|
<div class="flash">SSH проверка успешна</div>
|
|
{% elif flash == 'vpn_installed' %}
|
|
<div class="flash">VPN установлен на VPS — можно создавать клиентов и скачивать .conf</div>
|
|
{% endif %}
|
|
{% endif %}
|
|
|
|
<div class="panel" style="margin-bottom:1.25rem">
|
|
<div class="panel-head"><strong>Подключить сервер</strong></div>
|
|
<div class="panel-body">
|
|
<p class="muted" style="margin-top:0">
|
|
Как в Amnezia-Web-Panel: укажите IP/домен, логин и пароль или ключ.
|
|
Панель сначала проверит SSH, затем сохранит сервер.
|
|
</p>
|
|
<form method="post" action="/admin/servers" class="stack">
|
|
<div class="form-grid-2">
|
|
<label>Название (опционально)
|
|
<input name="name" placeholder="Мой VPS" />
|
|
</label>
|
|
<label>Протокол
|
|
<select name="protocol" required>
|
|
<option value="awg2">AmneziaWG 2.0</option>
|
|
<option value="wireguard">WireGuard</option>
|
|
</select>
|
|
</label>
|
|
</div>
|
|
|
|
<h3 class="form-section">SSH доступ</h3>
|
|
<div class="form-grid-2">
|
|
<label>Host / IP
|
|
<input name="ssh_host" placeholder="1.2.3.4" required autocomplete="off" />
|
|
</label>
|
|
<label>SSH порт
|
|
<input type="number" name="ssh_port" value="22" required />
|
|
</label>
|
|
<label>Логин
|
|
<input name="ssh_username" value="root" required autocomplete="username" />
|
|
</label>
|
|
<label>Пароль
|
|
<input type="password" name="ssh_password" placeholder="если без ключа" autocomplete="new-password" />
|
|
</label>
|
|
</div>
|
|
<label>Или приватный ключ
|
|
<textarea name="ssh_private_key" rows="5" placeholder="-----BEGIN OPENSSH PRIVATE KEY-----"></textarea>
|
|
</label>
|
|
<p class="muted" style="margin:0">Нужен пароль <em>или</em> приватный ключ. Сервер Ubuntu 20.04/22.04/24.04.</p>
|
|
|
|
<h3 class="form-section">VPN endpoint</h3>
|
|
<div class="form-grid-2">
|
|
<label>Public host (пусто = SSH host)
|
|
<input name="public_host" placeholder="тот же IP/домен" />
|
|
</label>
|
|
<label>VPN порт
|
|
<input type="number" name="public_port" placeholder="авто" />
|
|
</label>
|
|
<label>DNS
|
|
<input name="dns" value="1.1.1.1" />
|
|
</label>
|
|
</div>
|
|
<button class="btn btn-accent" type="submit">Проверить SSH и добавить</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
{% for s in servers %}
|
|
{% set probe = probes.get(s.id, {}) %}
|
|
<div class="panel" style="margin-bottom:1rem">
|
|
<div class="panel-head">
|
|
<strong>
|
|
<span class="ping {% if probe.get('online') %}ping-on{% else %}ping-off{% endif %}" title="{% if probe.get('online') %}online {{ probe.get('latency_ms') }}ms{% else %}offline{% endif %}"></span>
|
|
{{ s.name }}
|
|
<span class="badge badge-proto">{{ s.protocol }}</span>
|
|
{% if s.ssh_reachable %}<span class="badge badge-ok">SSH ok</span>{% else %}<span class="badge badge-off">SSH ?</span>{% endif %}
|
|
{% if s.vpn_installed %}<span class="badge badge-ok">VPN installed</span>{% else %}<span class="badge badge-off">VPN not installed</span>{% endif %}
|
|
</strong>
|
|
<div class="actions">
|
|
<form method="post" action="/admin/servers/{{ s.id }}/check-ssh" class="inline-form">
|
|
<button class="btn btn-sm btn-ghost" type="submit">Проверить SSH</button>
|
|
</form>
|
|
<form method="post" action="/admin/servers/{{ s.id }}/install-vpn" class="inline-form" onsubmit="return confirm('Установить Docker + VPN контейнер на VPS? Это займёт 1–3 минуты.')">
|
|
<button class="btn btn-sm btn-accent" type="submit">{% if s.vpn_installed %}Переустановить VPN{% else %}Установить VPN на сервер{% endif %}</button>
|
|
</form>
|
|
<form method="post" action="/admin/servers/{{ s.id }}/sync" class="inline-form">
|
|
<button class="btn btn-sm btn-ghost" type="submit">Синхр. conf → VPS</button>
|
|
</form>
|
|
<form method="post" action="/admin/servers/{{ s.id }}/delete" class="inline-form" onsubmit="return confirm('Удалить сервер и всех клиентов?')">
|
|
<button class="btn btn-sm btn-danger" type="submit">Удалить</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<div class="panel-body">
|
|
<p class="muted" style="margin-top:0">
|
|
{{ s.ssh_username or '—' }}@{{ s.ssh_host or '—' }}:{{ s.ssh_port or 22 }}
|
|
· VPN {{ s.public_host }}:{{ s.public_port }}
|
|
· {{ s.interface_name }} / {{ s.subnet }}
|
|
· клиентов: {{ s.clients|length }}
|
|
{% if probe.get('latency_ms') is not none %}· ping {{ probe.get('latency_ms') }} ms{% endif %}
|
|
</p>
|
|
|
|
{% if s.ssh_server_info %}
|
|
<pre class="mono server-info">{{ s.ssh_server_info }}</pre>
|
|
{% endif %}
|
|
|
|
<form method="post" action="/admin/servers/{{ s.id }}" class="stack">
|
|
<div class="form-grid-2">
|
|
<label>Название
|
|
<input name="name" value="{{ s.name }}" />
|
|
</label>
|
|
<label>DNS
|
|
<input name="dns" value="{{ s.dns }}" />
|
|
</label>
|
|
<label>Public host
|
|
<input name="public_host" value="{{ s.public_host }}" required />
|
|
</label>
|
|
<label>VPN порт
|
|
<input type="number" name="public_port" value="{{ s.public_port }}" required />
|
|
</label>
|
|
</div>
|
|
|
|
<h3 class="form-section">SSH доступ</h3>
|
|
<div class="form-grid-2">
|
|
<label>Host
|
|
<input name="ssh_host" value="{{ s.ssh_host or '' }}" required />
|
|
</label>
|
|
<label>SSH порт
|
|
<input type="number" name="ssh_port" value="{{ s.ssh_port or 22 }}" />
|
|
</label>
|
|
<label>Логин
|
|
<input name="ssh_username" value="{{ s.ssh_username or '' }}" required />
|
|
</label>
|
|
<label>Новый пароль
|
|
<input type="password" name="ssh_password" placeholder="{% if s.ssh_password %}сохранён — введите новый{% else %}не задан{% endif %}" autocomplete="new-password" />
|
|
</label>
|
|
</div>
|
|
<label>Новый приватный ключ
|
|
<textarea name="ssh_private_key" rows="4" placeholder="{% if s.ssh_private_key %}ключ сохранён — вставьте новый{% else %}не задан{% endif %}"></textarea>
|
|
</label>
|
|
<div class="actions">
|
|
<label class="check"><input type="checkbox" name="clear_ssh_password" value="1" /> очистить пароль</label>
|
|
<label class="check"><input type="checkbox" name="clear_ssh_private_key" value="1" /> очистить ключ</label>
|
|
</div>
|
|
<button class="btn btn-accent" type="submit">Сохранить</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
{% else %}
|
|
<p class="muted">Серверов пока нет — подключите первый VPS через форму выше.</p>
|
|
{% endfor %}
|
|
{% endblock %}
|