feat: improve admin experience and ignore local data
This commit is contained in:
+130
-39
@@ -1,3 +1,4 @@
|
||||
import type { Metadata } from "next";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { getRequestOrigin } from "@/lib/request-origin";
|
||||
@@ -5,6 +6,10 @@ import { buildSubscriptionUrl, describeRemainingTraffic } from "@/lib/subscripti
|
||||
import { requireUserSession } from "@/lib/session";
|
||||
import { formatBytes, getUserById } from "@/lib/store";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "我的订阅",
|
||||
};
|
||||
|
||||
export default async function MePage() {
|
||||
const session = await requireUserSession();
|
||||
const [user, origin] = await Promise.all([
|
||||
@@ -16,59 +21,145 @@ export default async function MePage() {
|
||||
redirect("/login");
|
||||
}
|
||||
|
||||
const usedBytes = user.used_tx_bytes + user.used_rx_bytes;
|
||||
const subscriptionUrl = user.subscription_token
|
||||
? buildSubscriptionUrl(user.subscription_token, origin)
|
||||
: null;
|
||||
const clashImportUrl = subscriptionUrl
|
||||
? `clash://install-config?url=${encodeURIComponent(subscriptionUrl)}`
|
||||
: null;
|
||||
|
||||
return (
|
||||
<main className="login-shell">
|
||||
<section className="login-card wide-card">
|
||||
<div className="hero-panel hero-panel--compact">
|
||||
<div className="hero-copy">
|
||||
<span className="eyebrow">SELF SERVICE</span>
|
||||
<main className="me-shell">
|
||||
<div aria-hidden="true" className="me-backdrop">
|
||||
<span className="me-glow me-glow--warm" />
|
||||
<span className="me-glow me-glow--cool" />
|
||||
<span className="me-glow me-glow--light" />
|
||||
</div>
|
||||
<section className="me-stage">
|
||||
<article className="me-panel me-hero">
|
||||
<div className="me-hero-copy">
|
||||
<span className="eyebrow">FEIEPROXY SELF SERVICE</span>
|
||||
<h1>{user.username}</h1>
|
||||
<p className="page-lede">
|
||||
已用 {formatBytes(user.used_tx_bytes + user.used_rx_bytes)},{describeRemainingTraffic(user)}
|
||||
鹅梯 FeieProxy 用户面板。这里可以查看配额状态、获取订阅链接,并通过
|
||||
Clash URL Scheme 一键导入当前配置。
|
||||
</p>
|
||||
<div className="badge-row">
|
||||
<span className={`status-pill ${user.enabled ? "status-pill--ok" : "status-pill--danger"}`}>
|
||||
{user.enabled ? "已启用" : "已禁用"}
|
||||
<span
|
||||
className={`status-pill ${
|
||||
user.enabled ? "status-pill--ok" : "status-pill--danger"
|
||||
}`}
|
||||
>
|
||||
{user.enabled ? "账号已启用" : "账号已禁用"}
|
||||
</span>
|
||||
<span className="status-pill status-pill--neutral">
|
||||
{user.expires_at ?? "长期有效"}
|
||||
</span>
|
||||
<span className="status-pill status-pill--warm">
|
||||
{describeRemainingTraffic(user)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="hero-aside">
|
||||
<div className="stat-tile">
|
||||
<span>auth_id</span>
|
||||
<strong className="mono break-all">{user.auth_id}</strong>
|
||||
<div className="me-identity-card">
|
||||
<span className="feature-kicker">ACCOUNT SNAPSHOT</span>
|
||||
<strong className="mono break-all">{user.auth_id}</strong>
|
||||
<p className="muted">
|
||||
订阅名称:{user.subscription_label || user.username}
|
||||
</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<section className="me-grid">
|
||||
<article className="me-panel me-subscription-card">
|
||||
<div className="section-title section-title--top">
|
||||
<div>
|
||||
<span className="feature-kicker">SUBSCRIPTION</span>
|
||||
<h2>一键导入配置</h2>
|
||||
</div>
|
||||
<span className="status-pill status-pill--ink">Clash / Mihomo</span>
|
||||
</div>
|
||||
<div className="stat-tile">
|
||||
<span>订阅链接</span>
|
||||
<strong className="mono break-all">
|
||||
{user.subscription_token
|
||||
? buildSubscriptionUrl(user.subscription_token, origin)
|
||||
: "暂不可用"}
|
||||
</strong>
|
||||
<p className="muted">
|
||||
推荐直接通过按钮唤起支持 `clash://` 的客户端导入。若浏览器拦截了自定义协议,允许一次即可。
|
||||
</p>
|
||||
<div className="me-action-row">
|
||||
{clashImportUrl ? (
|
||||
<a className="primary-button me-action-button" href={clashImportUrl}>
|
||||
一键导入 Clash
|
||||
</a>
|
||||
) : (
|
||||
<span className="status-pill status-pill--off">订阅暂不可用</span>
|
||||
)}
|
||||
{subscriptionUrl ? (
|
||||
<a
|
||||
className="ghost-button me-action-button"
|
||||
href={subscriptionUrl}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
打开订阅链接
|
||||
</a>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="me-link-card">
|
||||
<span className="feature-kicker">SUBSCRIPTION URL</span>
|
||||
<p className="mono break-all">{subscriptionUrl ?? "暂不可用"}</p>
|
||||
</div>
|
||||
<div className="me-tip-grid">
|
||||
<div className="me-tip-card">
|
||||
<span className="feature-kicker">TIP 01</span>
|
||||
<strong>客户端未唤起时</strong>
|
||||
<p className="muted">确认已安装支持 `clash://` 的客户端,并允许浏览器打开外部应用。</p>
|
||||
</div>
|
||||
<div className="me-tip-card">
|
||||
<span className="feature-kicker">TIP 02</span>
|
||||
<strong>手动导入也可用</strong>
|
||||
<p className="muted">复制上方订阅链接,在 Clash / Mihomo 客户端中粘贴导入即可。</p>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<div className="me-side-stack">
|
||||
<article className="me-panel">
|
||||
<div className="section-title">
|
||||
<h2>流量概览</h2>
|
||||
</div>
|
||||
<div className="me-metrics-grid">
|
||||
<div className="me-metric-card me-metric-card--paper">
|
||||
<span>总用量</span>
|
||||
<strong>{formatBytes(usedBytes)}</strong>
|
||||
</div>
|
||||
<div className="me-metric-card me-metric-card--warm">
|
||||
<span>剩余额度</span>
|
||||
<strong>{describeRemainingTraffic(user)}</strong>
|
||||
</div>
|
||||
<div className="me-metric-card me-metric-card--ink">
|
||||
<span>上传</span>
|
||||
<strong>{formatBytes(user.used_tx_bytes)}</strong>
|
||||
</div>
|
||||
<div className="me-metric-card me-metric-card--sage">
|
||||
<span>下载</span>
|
||||
<strong>{formatBytes(user.used_rx_bytes)}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article className="me-panel me-support-card">
|
||||
<span className="feature-kicker">ACCESS</span>
|
||||
<h2>使用说明</h2>
|
||||
<ul className="me-note-list">
|
||||
<li>域名入口适合网页登录与获取订阅。</li>
|
||||
<li>现有节点鉴权仍可继续使用原来的 `IP:3000` 直连地址。</li>
|
||||
<li>如果订阅内容更新,重新点击导入按钮即可刷新客户端配置。</li>
|
||||
</ul>
|
||||
<form action="/logout" method="post">
|
||||
<button className="ghost-button me-action-button me-action-button--full" type="submit">
|
||||
退出登录
|
||||
</button>
|
||||
</form>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
<div className="summary-grid">
|
||||
<div className="metric-inline">
|
||||
<span>上传</span>
|
||||
<strong>{formatBytes(user.used_tx_bytes)}</strong>
|
||||
</div>
|
||||
<div className="metric-inline">
|
||||
<span>下载</span>
|
||||
<strong>{formatBytes(user.used_rx_bytes)}</strong>
|
||||
</div>
|
||||
<div className="metric-inline">
|
||||
<span>剩余额度</span>
|
||||
<strong>{describeRemainingTraffic(user)}</strong>
|
||||
</div>
|
||||
</div>
|
||||
<form action="/logout" method="post">
|
||||
<button className="ghost-button" type="submit">
|
||||
退出登录
|
||||
</button>
|
||||
</form>
|
||||
</section>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user