hy2-panel/app/me/page.tsx
2026-05-06 00:10:45 +08:00

139 lines
5.0 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type { Metadata } from "next";
import { redirect } from "next/navigation";
import { formatLocalDateTime } from "@/lib/datetime";
import { getRequestOrigin } from "@/lib/request-origin";
import { buildSubscriptionUrl, describeRemainingTraffic } from "@/lib/subscription";
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([
getUserById(session.user_id),
getRequestOrigin(),
]);
if (!user) {
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="me-shell">
<section className="me-stage">
<article className="me-panel me-hero">
<div className="me-hero-copy">
<span className="eyebrow"></span>
<h1>{user.username}</h1>
<div className="badge-row">
<span
className={`status-pill ${
user.enabled ? "status-pill--ok" : "status-pill--danger"
}`}
>
{user.enabled ? "账号已启用" : "账号已禁用"}
</span>
<span className="status-pill status-pill--neutral">
{formatLocalDateTime(user.expires_at, "长期有效")}
</span>
<span className="status-pill status-pill--warm">
{describeRemainingTraffic(user)}
</span>
</div>
</div>
<div className="me-identity-card">
<span className="feature-kicker"> ID</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"></span>
<h2></h2>
</div>
<span className="status-pill status-pill--ink">Clash / Mihomo</span>
</div>
<div className="me-action-row">
{clashImportUrl ? (
<a className="primary-button me-action-button" href={clashImportUrl}>
</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"></span>
<p className="mono break-all">{subscriptionUrl ?? "暂不可用"}</p>
</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"></span>
<h2></h2>
<form action="/logout" method="post">
<button className="ghost-button me-action-button me-action-button--full" type="submit">
退
</button>
</form>
</article>
</div>
</section>
</section>
</main>
);
}