Track site-created 3x-ui clients and per-panel inbound whitelist for users

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-25 22:32:26 +03:00
co-authored by Cursor
parent 1b2fea4539
commit ad7e1db6b7
9 changed files with 746 additions and 13 deletions
+108 -2
View File
@@ -20,6 +20,10 @@
<div class="flash flash-error">{{ flash[6:] }}</div>
{% elif flash == 'client_created' %}
<div class="flash">Клиент создан в 3x-ui</div>
{% elif flash == 'inbounds_synced' %}
<div class="flash">Inbound’ы синхронизированы с 3x-ui</div>
{% elif flash == 'inbounds_saved' %}
<div class="flash">Доступные пользователям inbound’ы сохранены</div>
{% elif flash == 'ok' %}
<div class="flash">Данные обновлены</div>
{% endif %}
@@ -65,6 +69,60 @@ PublicKey = {{ created.publicKey }}
</div>
{% endif %}
<div class="panel">
<div class="panel-head">
<strong>Создано через сайт</strong>
<span class="muted">только клиенты, которых вы создали в этой панели</span>
</div>
<table>
<thead>
<tr>
<th>Email</th>
<th>Протокол</th>
<th>Inbound</th>
<th>Срок</th>
<th>Лимиты</th>
<th>Ссылка / ключи</th>
<th>Когда</th>
</tr>
</thead>
<tbody>
{% for c in site_clients %}
<tr>
<td>
<strong>{{ c.email }}</strong>
{% if c.comment %}<div class="muted">{{ c.comment }}</div>{% endif %}
</td>
<td><span class="badge badge-proto">{{ c.protocol }}</span></td>
<td class="muted">#{{ c.inbound_id }} {% if c.inbound_remark %}{{ c.inbound_remark }}{% endif %}</td>
<td class="muted">
{% if c.expiry_days and c.expiry_days > 0 %}
{{ c.expiry_days }} дн.
{% if c.start_after_first_use %}<br><span class="badge badge-ok">после 1-го входа</span>{% endif %}
{% else %}
без срока
{% endif %}
</td>
<td class="muted">
{{ c.total_gb or 0 }} GB
{% if c.limit_ip %}<br>IP ≤ {{ c.limit_ip }}{% endif %}
</td>
<td>
{% if c.link %}
<div class="link-box" style="margin:0;max-width:280px;font-size:.78rem;word-break:break-all;">{{ c.link }}</div>
{% elif c.private_key %}
<div class="muted" style="font-size:.78rem;word-break:break-all;">priv: {{ c.private_key[:24] }}…</div>
{% else %}—{% endif %}
</td>
<td class="muted">{{ c.created_at }}</td>
</tr>
{% else %}
<tr><td colspan="7" class="muted">Пока никого не создавали через сайт</td></tr>
{% endfor %}
</tbody>
</table>
</div>
{% if data %}
<div class="stats">
<div class="stat">
@@ -165,10 +223,55 @@ PublicKey = {{ created.publicKey }}
</div>
</div>
</div>
{% endif %}
<div class="panel" style="margin-bottom:1rem">
<div class="panel-head">
<strong>Доступные inbound’ы (VLESS / WireGuard)</strong>
<strong>Inbound’ы для пользователей</strong>
<span class="muted">отметьте, какие будут доступны при создании клиентов</span>
</div>
<form method="post" action="/admin/xui/{{ panel.id }}/inbounds/users">
<table>
<thead>
<tr>
<th>Для пользователей</th>
<th>ID</th>
<th>Remark</th>
<th>Protocol</th>
<th>Port</th>
<th>В 3x-ui</th>
</tr>
</thead>
<tbody>
{% for ib in panel_inbounds %}
<tr>
<td>
<label class="check" style="margin:0">
<input type="checkbox" name="inbound_ids" value="{{ ib.inbound_id }}" {% if ib.is_available_for_users %}checked{% endif %} />
</label>
</td>
<td>{{ ib.inbound_id }}</td>
<td>{{ ib.remark or '—' }}</td>
<td><span class="badge badge-proto">{{ ib.protocol }}</span></td>
<td>{{ ib.port or '—' }}</td>
<td>{% if ib.enable %}<span class="badge badge-ok">on</span>{% else %}<span class="badge badge-off">off</span>{% endif %}</td>
</tr>
{% else %}
<tr><td colspan="6" class="muted">Список пуст — нажмите «Синхронизировать inbound’ы»</td></tr>
{% endfor %}
</tbody>
</table>
<div class="actions" style="padding:1rem;gap:.5rem;display:flex;flex-wrap:wrap">
<button class="btn btn-accent" type="submit" {% if not panel_inbounds %}disabled{% endif %}>Сохранить для пользователей</button>
<button class="btn btn-ghost" type="submit" formaction="/admin/xui/{{ panel.id }}/inbounds/sync" formmethod="post">Синхронизировать с 3x-ui</button>
</div>
</form>
</div>
{% if data %}
<div class="panel" style="margin-bottom:1rem">
<div class="panel-head">
<strong>Все inbound’ы из API (VLESS / WireGuard)</strong>
<span class="muted" id="inbounds-status"></span>
</div>
<table>
@@ -198,7 +301,10 @@ PublicKey = {{ created.publicKey }}
</div>
<div class="panel">
<div class="panel-head"><strong>Clients</strong></div>
<div class="panel-head">
<strong>Все клиенты 3x-ui</strong>
<span class="muted">из API панели (включая созданных не через сайт)</span>
</div>
<table>
<thead>
<tr>