Apply inbounds on nodes via Xray with Reality settings and client sync.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
orohi
2026-07-29 05:11:45 +03:00
co-authored by Cursor
parent c41bae9852
commit 20a20168b6
15 changed files with 1081 additions and 63 deletions
+141
View File
@@ -0,0 +1,141 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{.Title}} · VPN Panel</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/static/css/app.css">
</head>
<body class="page-admin">
<aside class="sidebar">
<a class="brand" href="/admin">VPN Panel</a>
<nav>
<a href="/admin">Обзор</a>
<a href="/admin/clients">Пользователи</a>
<a href="/admin/nodes">Ноды</a>
<a class="active" href="/admin/profiles">Профили</a>
<a href="/admin/protocols">Протоколы</a>
</nav>
<div class="sidebar-foot">
<span class="user-chip">{{.UserName}}</span>
<form method="POST" action="/logout"><button type="submit" class="link-btn">Выйти</button></form>
</div>
</aside>
<main class="admin-main">
<header class="admin-header row-head">
<div>
<h1>Inbound <code>{{.Inbound.Tag}}</code></h1>
<p class="muted">Профиль {{.Profile.Name}}</p>
</div>
<a class="btn btn-ghost" href="/admin/profiles/{{.Profile.ID}}">К профилю</a>
</header>
{{if .Flash}}<div class="alert ok">{{.Flash}}</div>{{end}}
{{if .Error}}<div class="alert">{{.Error}}</div>{{end}}
<section class="panel-section">
<form class="form-card" method="POST" action="/admin/profiles/{{.Profile.ID}}/inbounds/{{.Inbound.ID}}">
<div class="form-grid">
<label>Протокол
<select name="protocol_id" required>
{{range .Protocols}}
<option value="{{.ID}}" {{if eq .ID $.Inbound.ProtocolID}}selected{{end}}>{{.Name}} ({{.Code}})</option>
{{end}}
</select>
</label>
<label>Tag
<input name="tag" required value="{{.Inbound.Tag}}">
</label>
<label>Порт
<input name="port" type="number" value="{{.Inbound.Port}}">
</label>
<label>Listen
<input name="listen" value="{{.Inbound.Listen}}">
</label>
<label>Network
<select name="network">
<option value="tcp" {{if eq .Inbound.Network "tcp"}}selected{{end}}>tcp</option>
<option value="ws" {{if eq .Inbound.Network "ws"}}selected{{end}}>ws</option>
<option value="grpc" {{if eq .Inbound.Network "grpc"}}selected{{end}}>grpc</option>
<option value="quic" {{if eq .Inbound.Network "quic"}}selected{{end}}>quic</option>
<option value="udp" {{if eq .Inbound.Network "udp"}}selected{{end}}>udp</option>
</select>
</label>
<label>Security
<select name="security">
<option value="none" {{if eq .Inbound.Security "none"}}selected{{end}}>none</option>
<option value="tls" {{if eq .Inbound.Security "tls"}}selected{{end}}>tls</option>
<option value="reality" {{if eq .Inbound.Security "reality"}}selected{{end}}>reality</option>
</select>
</label>
<label>Path / serviceName
<input name="path" value="{{.Inbound.Path}}" placeholder="/ws или grpc service">
</label>
<label>Host header
<input name="host" value="{{.Inbound.Host}}">
</label>
<label>SNI
<input name="sni" value="{{.Inbound.SNI}}">
</label>
<label>Fingerprint
<input name="fingerprint" value="{{.Inbound.Fingerprint}}" placeholder="chrome">
</label>
<label>Flow
<input name="flow" value="{{.Inbound.Flow}}" placeholder="xtls-rprx-vision">
</label>
<label>ALPN
<input name="alpn" value="{{.Inbound.ALPN}}" placeholder="h2,http/1.1">
</label>
<label>Порядок
<input name="sort_order" type="number" value="{{.Inbound.SortOrder}}">
</label>
<label>Заметка
<input name="remark" value="{{.Inbound.Remark}}">
</label>
<label>SS method
<input name="ss_method" value="{{.Inbound.SSMethod}}" placeholder="aes-128-gcm">
</label>
<label>Password (trojan shared, optional)
<input name="password" value="{{.Inbound.Password}}">
</label>
</div>
<h3 class="section-sub">Reality</h3>
<p class="muted">Пустые ключи сгенерируются автоматически при security=reality.</p>
<div class="form-grid">
<label>Dest
<input name="reality_dest" value="{{.Inbound.RealityDest}}" placeholder="www.microsoft.com:443">
</label>
<label>Server names
<input name="reality_server_names" value="{{.Inbound.RealityServerNames}}" placeholder="www.microsoft.com">
</label>
<label>Private key
<input name="reality_private_key" value="{{.Inbound.RealityPrivateKey}}" class="mono">
</label>
<label>Public key (для клиентов)
<input name="reality_public_key" value="{{.Inbound.RealityPublicKey}}" class="mono">
</label>
<label>Short ID
<input name="reality_short_id" value="{{.Inbound.RealityShortID}}" class="mono">
</label>
</div>
<label class="check-row">
<input type="hidden" name="enabled" value="off">
<input type="checkbox" name="enabled" value="on" {{if .Inbound.Enabled}}checked{{end}}>
Включён в профиле
</label>
<div class="cta-row">
<button class="btn btn-primary" type="submit">Сохранить</button>
<a class="btn btn-ghost" href="/admin/profiles/{{.Profile.ID}}">Отмена</a>
</div>
</form>
</section>
</main>
</body>
</html>
+30 -1
View File
@@ -81,6 +81,7 @@
<td>{{.Security}}</td>
<td><span class="badge {{if .Enabled}}on{{else}}off{{end}}">{{if .Enabled}}ON{{else}}OFF{{end}}</span></td>
<td class="actions actions-multi">
<a class="btn btn-sm btn-ghost" href="/admin/profiles/{{$.Profile.ID}}/inbounds/{{.ID}}">Изменить</a>
<form method="POST" action="/admin/profiles/{{$.Profile.ID}}/inbounds/{{.ID}}/toggle">
<button class="btn btn-sm btn-ghost" type="submit">{{if .Enabled}}Выкл{{else}}Вкл{{end}}</button>
</form>
@@ -130,16 +131,44 @@
<select name="security">
<option value="none">none</option>
<option value="tls">tls</option>
<option value="reality">reality</option>
<option value="reality" selected>reality</option>
</select>
</label>
<label>Path / serviceName
<input name="path" placeholder="/ws или grpc service">
</label>
<label>Host header
<input name="host" placeholder="для ws">
</label>
<label>SNI
<input name="sni" placeholder="авто из reality names">
</label>
<label>Fingerprint
<input name="fingerprint" value="chrome">
</label>
<label>Flow
<input name="flow" placeholder="xtls-rprx-vision для VLESS+Reality">
</label>
<label>ALPN
<input name="alpn" placeholder="h2,http/1.1">
</label>
<label>Порядок
<input name="sort_order" type="number" value="0">
</label>
<label>Заметка
<input name="remark" placeholder="опционально">
</label>
<label>Reality dest
<input name="reality_dest" placeholder="www.microsoft.com:443">
</label>
<label>Reality server names
<input name="reality_server_names" placeholder="www.microsoft.com">
</label>
<label>SS method
<input name="ss_method" value="aes-128-gcm">
</label>
</div>
<p class="muted">Для Reality ключи и shortId генерируются автоматически. После назначения профиля ноде Xray поднимется на агенте.</p>
<button class="btn btn-primary" type="submit">Добавить inbound</button>
</form>
</section>