158 lines
5.0 KiB
TypeScript
158 lines
5.0 KiB
TypeScript
import { ActionForm } from "@/components/action-form";
|
|
import { CopyButton } from "@/components/copy-button";
|
|
import { Input } from "@/components/ui/input";
|
|
import { SubmitButton } from "@/components/submit-button";
|
|
import { getRequestOrigin } from "@/lib/request-origin";
|
|
import {
|
|
buildSubscriptionUrl,
|
|
getDefaultClashTemplate,
|
|
} from "@/lib/subscription";
|
|
import {
|
|
getAdminMirrorUser,
|
|
getNodeProxyName,
|
|
getSubscriptionSettings,
|
|
listSubscriptionNodes,
|
|
} from "@/lib/store";
|
|
|
|
import { saveSubscriptionSettingsAction } from "./actions";
|
|
import { ProxyGroupEditor } from "./proxy-group-editor";
|
|
|
|
export default async function AdminSubscriptionsPage() {
|
|
const [settings, adminUser, nodes, origin] = await Promise.all([
|
|
getSubscriptionSettings(),
|
|
getAdminMirrorUser(),
|
|
listSubscriptionNodes(),
|
|
getRequestOrigin(),
|
|
]);
|
|
const proxyGroupNodes = nodes.map((node) => ({
|
|
slug: node.slug,
|
|
label: getNodeProxyName(node),
|
|
meta:
|
|
node.node_type === "custom"
|
|
? `${node.name} / ${node.slug} / 自定义`
|
|
: node.client_name && node.client_name !== node.name
|
|
? `${node.name} / ${node.slug}`
|
|
: node.slug,
|
|
}));
|
|
|
|
return (
|
|
<main className="page-stack">
|
|
<section className="hero-panel">
|
|
<div className="hero-copy">
|
|
<span className="eyebrow">订阅</span>
|
|
<h2>订阅与模板</h2>
|
|
<div className="badge-row">
|
|
<span className="status-pill status-pill--ok">
|
|
节点 {nodes.length}
|
|
</span>
|
|
<span className="status-pill status-pill--ink">
|
|
分组 {settings.proxyGroups.length}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section className="panel-card">
|
|
<div className="section-title">
|
|
<h3>管理员订阅</h3>
|
|
</div>
|
|
{adminUser?.subscription_token ? (
|
|
<div className="instruction-block no-margin">
|
|
<div className="copy-line">
|
|
<code>
|
|
{buildSubscriptionUrl(adminUser.subscription_token, origin)}
|
|
</code>
|
|
<CopyButton
|
|
value={buildSubscriptionUrl(
|
|
adminUser.subscription_token,
|
|
origin,
|
|
)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<p className="muted">管理员订阅链接暂不可用。</p>
|
|
)}
|
|
</section>
|
|
|
|
<section className="panel-card">
|
|
<div className="section-title section-title--top">
|
|
<div className="stack-tight">
|
|
<h3>代理分组</h3>
|
|
</div>
|
|
</div>
|
|
<div className="stack-lg">
|
|
<ProxyGroupEditor
|
|
mode="main"
|
|
group={{
|
|
id: "main",
|
|
name: settings.profileName,
|
|
memberSlugs: settings.mainProxyGroupMemberSlugs,
|
|
hiddenSlugs: settings.mainProxyGroupHiddenSlugs,
|
|
}}
|
|
nodes={proxyGroupNodes}
|
|
/>
|
|
|
|
{settings.proxyGroups.map((group) => (
|
|
<details key={group.id} className="group-disclosure">
|
|
<summary className="group-summary">
|
|
<strong>{group.name}</strong>
|
|
<span>{group.memberSlugs.length} 个节点 · 编辑分组</span>
|
|
</summary>
|
|
<ProxyGroupEditor group={group} nodes={proxyGroupNodes} />
|
|
</details>
|
|
))}
|
|
|
|
<details
|
|
key={`new-${settings.proxyGroups.length}`}
|
|
className="group-disclosure"
|
|
>
|
|
<summary className="group-summary">
|
|
<strong>新建分组</strong>
|
|
</summary>
|
|
<ProxyGroupEditor nodes={proxyGroupNodes} />
|
|
</details>
|
|
</div>
|
|
</section>
|
|
|
|
<details className="panel-card group-disclosure">
|
|
<summary className="group-summary">
|
|
<strong>订阅模板</strong>
|
|
<span>编辑 YAML 与默认组名</span>
|
|
</summary>
|
|
<ActionForm
|
|
action={saveSubscriptionSettingsAction}
|
|
className="stack-lg"
|
|
>
|
|
<label className="field">
|
|
<span>订阅文件名 / 默认主组名</span>
|
|
<Input
|
|
name="profile_name"
|
|
defaultValue={settings.profileName}
|
|
required
|
|
/>
|
|
</label>
|
|
<label className="field">
|
|
<span>基础模板</span>
|
|
<textarea
|
|
name="template"
|
|
rows={18}
|
|
defaultValue={settings.template || getDefaultClashTemplate()}
|
|
/>
|
|
</label>
|
|
<label className="field">
|
|
<span>附加 proxy-groups YAML</span>
|
|
<textarea
|
|
name="extra_proxy_groups"
|
|
rows={12}
|
|
defaultValue={settings.extraProxyGroups}
|
|
placeholder={` - name: ChatGPT\n type: select\n proxies: ["US 01 - hy2", "JP 01 - hy2"]`}
|
|
/>
|
|
</label>
|
|
<SubmitButton variant="default">保存订阅模板</SubmitButton>
|
|
</ActionForm>
|
|
</details>
|
|
</main>
|
|
);
|
|
}
|