Show subscription inbounds as Remnawave-style host cards.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+30
-1
@@ -77,7 +77,36 @@ code {
|
||||
.btn-sm { padding: 0.45rem 0.9rem; font-size: 0.9rem; }
|
||||
.btn-block { width: 100%; }
|
||||
|
||||
.page-home { min-height: 100vh; }
|
||||
.page-sub { min-height: 100vh; }
|
||||
.sub-wrap {
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem 1.25rem 3rem;
|
||||
animation: rise .55s ease both;
|
||||
}
|
||||
.sub-header { margin-bottom: 1.5rem; }
|
||||
.sub-brand {
|
||||
font-size: clamp(1.8rem, 5vw, 2.6rem) !important;
|
||||
margin-bottom: 0.35rem !important;
|
||||
}
|
||||
.sub-header h1 { margin: 0 0 0.35rem; font-size: 1.5rem; }
|
||||
.sub-meta {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 0.85rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
.sub-host details.sub-link { margin-top: 0.75rem; }
|
||||
.sub-host summary {
|
||||
cursor: pointer;
|
||||
color: var(--accent);
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
.sub-meta { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
.home-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
@@ -49,14 +49,38 @@
|
||||
|
||||
<section class="panel-section">
|
||||
<h2>Subscription</h2>
|
||||
<p class="muted">Ссылка для клиента (нужны online-ноды с назначенным профилем и включёнными inbound):</p>
|
||||
<p class="muted">Ссылка подписки (в браузере — страница с инбаундами, в клиенте — base64):</p>
|
||||
<pre class="code-block">{{.SubURL}}</pre>
|
||||
<div class="cta-row" style="margin:0.75rem 0 1rem">
|
||||
<a class="btn btn-sm btn-primary" href="{{.SubURL}}" target="_blank" rel="noopener">Открыть страницу</a>
|
||||
<a class="btn btn-sm btn-ghost" href="{{.SubURL}}?format=plain" target="_blank" rel="noopener">Plain</a>
|
||||
<a class="btn btn-sm btn-ghost" href="{{.SubURL}}/info" target="_blank" rel="noopener">JSON</a>
|
||||
</div>
|
||||
|
||||
<h3 style="margin:0 0 0.75rem;font-size:1.05rem">Инбаунды в подписке</h3>
|
||||
{{if .Hosts}}
|
||||
<div class="proto-grid">
|
||||
{{range .Hosts}}
|
||||
<article class="proto-card {{if .Available}}is-on{{else}}is-off{{end}}">
|
||||
<div class="proto-top">
|
||||
<h3>{{.Title}}</h3>
|
||||
<span class="badge {{if .Available}}on{{else}}off{{end}}">{{if .Available}}ON{{else}}OFF{{end}}</span>
|
||||
</div>
|
||||
<p><code>{{.Tag}}</code> · {{.ProtocolName}} · {{.Network}}/{{.Security}} :{{.Port}}</p>
|
||||
<div class="proto-meta">
|
||||
{{if .Address}}<span>{{.NodeName}}</span><code>{{.Address}}</code>{{else}}<span class="muted">offline / нет ноды</span>{{end}}
|
||||
</div>
|
||||
</article>
|
||||
{{end}}
|
||||
</div>
|
||||
{{else}}
|
||||
<p class="hint">Нет инбаундов — отметьте их ниже в форме.</p>
|
||||
{{end}}
|
||||
|
||||
{{if .Links}}
|
||||
<h3 style="margin-top:1rem;font-size:1rem">Сгенерированные ссылки</h3>
|
||||
<h3 style="margin-top:1.25rem;font-size:1rem">Ссылки</h3>
|
||||
<pre class="code-block log">{{range .Links}}{{.}}
|
||||
{{end}}</pre>
|
||||
{{else}}
|
||||
<p class="hint">Пока нет ссылок — назначьте inbound пользователю, профиль ноде, и дождитесь Online.</p>
|
||||
{{end}}
|
||||
</section>
|
||||
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{{.Title}} · Subscription</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-sub">
|
||||
<div class="home-bg" aria-hidden="true"></div>
|
||||
<main class="sub-wrap">
|
||||
<header class="sub-header">
|
||||
<p class="brand-mark sub-brand">VPN Panel</p>
|
||||
<h1>{{.Info.Client.Username}}</h1>
|
||||
<p class="muted">Подписка · инбаунды / хосты</p>
|
||||
{{if .Expired}}
|
||||
<div class="alert">Подписка неактивна или истекла</div>
|
||||
{{else}}
|
||||
<span class="badge {{statusClass (printf "%s" .Info.Client.Status)}}">{{.Info.Client.StatusLabel}}</span>
|
||||
{{end}}
|
||||
</header>
|
||||
|
||||
<section class="sub-meta">
|
||||
<div class="stat">
|
||||
<span class="stat-label">Трафик</span>
|
||||
<span class="stat-value" style="font-size:1.2rem">
|
||||
{{if gt .Info.Client.TrafficLimitBytes 0}}
|
||||
использовано / лимит задан
|
||||
{{else}}
|
||||
∞ без лимита
|
||||
{{end}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span class="stat-label">Истекает</span>
|
||||
<span class="stat-value" style="font-size:1.2rem">
|
||||
{{if .Info.Client.ExpireAt}}{{.Info.Client.ExpireAt.Format "02.01.2006 15:04"}}{{else}}бессрочно{{end}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span class="stat-label">Хостов</span>
|
||||
<span class="stat-value accent" style="font-size:1.2rem">{{len .Info.Hosts}}</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel-section">
|
||||
<h2>Subscription URL</h2>
|
||||
<pre class="code-block">{{.SubURL}}</pre>
|
||||
<p class="hint">В клиенте добавьте эту ссылку как подписку. В браузере открывается эта страница с инбаундами.</p>
|
||||
<div class="cta-row">
|
||||
<a class="btn btn-ghost btn-sm" href="{{.SubURL}}?format=base64">Raw base64</a>
|
||||
<a class="btn btn-ghost btn-sm" href="{{.SubURL}}?format=plain">Plain links</a>
|
||||
<a class="btn btn-ghost btn-sm" href="{{.SubURL}}/info">JSON info</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel-section">
|
||||
<h2>Инбаунды / Hosts</h2>
|
||||
{{if .Info.Hosts}}
|
||||
<div class="proto-grid">
|
||||
{{range .Info.Hosts}}
|
||||
<article class="proto-card sub-host {{if .Available}}is-on{{else}}is-off{{end}}">
|
||||
<div class="proto-top">
|
||||
<h3>{{.Title}}</h3>
|
||||
<span class="badge {{if .Available}}on{{else}}off{{end}}">{{if .Available}}ON{{else}}OFF{{end}}</span>
|
||||
</div>
|
||||
<p>
|
||||
<code>{{.Tag}}</code> · {{.ProtocolName}}<br>
|
||||
{{.Network}} / {{.Security}} · порт {{.Port}}
|
||||
</p>
|
||||
<div class="proto-meta">
|
||||
{{if .Address}}
|
||||
<span>{{.NodeName}}</span>
|
||||
<code>{{.Address}}:{{.Port}}</code>
|
||||
{{else}}
|
||||
<span class="muted">Нет online-ноды с этим inbound</span>
|
||||
{{end}}
|
||||
</div>
|
||||
{{if .Link}}
|
||||
<details class="sub-link">
|
||||
<summary>Показать ссылку</summary>
|
||||
<pre class="code-block log">{{.Link}}</pre>
|
||||
</details>
|
||||
{{end}}
|
||||
</article>
|
||||
{{end}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="empty-state">
|
||||
<p>Инбаунды не назначены. Админ должен выбрать inbound у пользователя.</p>
|
||||
</div>
|
||||
{{end}}
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user