-
-
{user.username}
-
auth_id: {user.auth_id}
-
-
-
- {getUserPills(user).map((pill) => (
-
- {pill.label}
-
- ))}
+
+
+
用户列表
+ {users.length} 个账户
+
+
+
+ 用户
+ 状态
+ 用量
+ 剩余额度
+ 在线
+ 操作
+
+ {users.map((user) => {
+ const usedBytes = user.used_tx_bytes + user.used_rx_bytes;
+ const subscriptionUrl = user.subscription_token
+ ? buildSubscriptionUrl(user.subscription_token, origin)
+ : "暂不可用";
+
+ return (
+
+
+
+ {user.username}
+ {user.auth_id}
+
+
+ {getUserPills(user).map((pill) => (
+
+ {pill.label}
+
+ ))}
+
+
+ {formatBytes(usedBytes)}
+ TX {formatBytes(user.used_tx_bytes)} / RX {formatBytes(user.used_rx_bytes)}
+
+
+ {describeRemainingTraffic(user)}
+ {user.traffic_limit_bytes ? "有额度" : "不限量"}
+
+
+ {user.online_connections}
+ 连接
+
+ 编辑
+
+
+
+ 订阅链接
+ {subscriptionUrl}
+
+
+
+
+ {user.is_admin ? null : (
+
+ )}
+
-
- 累计 {formatBytes(user.used_tx_bytes + user.used_rx_bytes)} / 在线{" "}
- {user.online_connections}
-
-
-
-
-
- 总用量
- {formatBytes(user.used_tx_bytes + user.used_rx_bytes)}
-
-
- 剩余额度
- {describeRemainingTraffic(user)}
-
-
- 在线连接
- {user.online_connections}
-
-
-
-
-
订阅链接
-
- {user.subscription_token
- ? buildSubscriptionUrl(user.subscription_token, origin)
- : "暂不可用"}
-
-
-
-
- {user.is_admin ? null : (
-
- )}
-
-
- ))}
+
+ );
+ })}
+
);
-}
+}
\ No newline at end of file
diff --git a/app/globals.css b/app/globals.css
index e4edbc5..e1f6805 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -1,13 +1,13 @@
@import "tailwindcss";
:root {
- --background: #efe5d4;
- --foreground: #18222c;
- --panel: rgba(255, 251, 245, 0.76);
- --panel-strong: rgba(255, 251, 245, 0.92);
+ --background: #f1e7d8;
+ --foreground: #121a22;
+ --panel: rgba(255, 251, 245, 0.9);
+ --panel-strong: rgba(255, 251, 245, 0.98);
--panel-tint: rgba(255, 244, 230, 0.62);
- --border: rgba(24, 34, 44, 0.12);
- --border-strong: rgba(24, 34, 44, 0.22);
+ --border: rgba(18, 26, 34, 0.18);
+ --border-strong: rgba(18, 26, 34, 0.32);
--accent: #c96233;
--accent-deep: #7a3116;
--accent-soft: rgba(201, 98, 51, 0.14);
@@ -15,7 +15,7 @@
--success: #1f7a4f;
--danger: #af2d21;
--warn: #8e5c10;
- --muted: #5e6670;
+ --muted: #3f4b57;
--shadow: 0 26px 90px rgba(51, 35, 18, 0.12);
--shadow-soft: 0 10px 30px rgba(51, 35, 18, 0.06);
}
@@ -24,7 +24,7 @@
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: "Avenir Next", "PingFang SC", "Segoe UI", sans-serif;
- --font-mono: "IBM Plex Mono", "SFMono-Regular", monospace;
+ --font-mono: ui-monospace, "Cascadia Mono", "SFMono-Regular", "Menlo", "Consolas", monospace;
}
* {
@@ -71,12 +71,14 @@ a {
input,
textarea,
+select,
button {
font: inherit;
}
input,
-textarea {
+textarea,
+select {
width: 100%;
border: 1px solid var(--border-strong);
border-radius: 18px;
@@ -88,7 +90,8 @@ textarea {
}
input:focus,
-textarea:focus {
+textarea:focus,
+select:focus {
outline: none;
border-color: rgba(201, 98, 51, 0.55);
box-shadow:
@@ -263,7 +266,7 @@ button:disabled {
.login-card--brand h1 {
font-size: clamp(2.1rem, 5vw, 3.2rem);
- letter-spacing: -0.06em;
+ letter-spacing: 0;
}
.login-form {
@@ -293,12 +296,6 @@ button:disabled {
line-height: 1.35;
}
-.login-note {
- margin: 0;
- color: var(--muted);
- font-size: 0.92rem;
-}
-
.me-shell {
position: relative;
min-height: 100vh;
@@ -377,7 +374,6 @@ button:disabled {
.me-grid,
.me-side-stack,
-.me-tip-grid,
.me-metrics-grid,
.me-hero-copy,
.me-identity-card {
@@ -435,7 +431,7 @@ button:disabled {
.me-hero-copy h1 {
font-size: clamp(2.2rem, 5vw, 3.4rem);
- letter-spacing: -0.06em;
+ letter-spacing: 0;
}
.me-identity-card {
@@ -493,29 +489,6 @@ button:disabled {
line-height: 1.65;
}
-.me-tip-grid {
- grid-template-columns: repeat(2, minmax(0, 1fr));
- gap: 0.9rem;
-}
-
-.me-tip-card {
- display: grid;
- gap: 0.45rem;
- padding: 1rem;
- border-radius: 22px;
- border: 1px solid rgba(24, 34, 44, 0.08);
- background: rgba(255, 255, 255, 0.38);
-}
-
-.me-tip-card strong {
- font-size: 1rem;
-}
-
-.me-tip-card p {
- margin: 0;
- line-height: 1.6;
-}
-
.me-metrics-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0.9rem;
@@ -533,12 +506,13 @@ button:disabled {
.me-metric-card span {
color: var(--muted);
font-size: 0.9rem;
+ font-weight: 700;
}
.me-metric-card strong {
font-size: 1.4rem;
line-height: 1.15;
- letter-spacing: -0.04em;
+ letter-spacing: 0;
}
.me-metric-card--paper {
@@ -563,15 +537,6 @@ button:disabled {
align-content: start;
}
-.me-note-list {
- margin: 0;
- padding-left: 1.15rem;
- display: grid;
- gap: 0.75rem;
- color: var(--muted);
- line-height: 1.6;
-}
-
.wide-card {
width: min(56rem, 100%);
}
@@ -618,10 +583,11 @@ button:disabled {
}
.eyebrow {
- font-family: var(--font-mono);
+ font-family: var(--font-sans);
font-size: 0.77rem;
- letter-spacing: 0.18em;
+ letter-spacing: 0;
color: var(--accent-deep);
+ font-weight: 800;
}
.page-lede {
@@ -636,7 +602,9 @@ button:disabled {
}
.mono {
- font-family: var(--font-mono);
+ font-family: var(--font-sans);
+ color: #202b35;
+ font-weight: 650;
}
.sidebar-note {
@@ -704,16 +672,11 @@ button:disabled {
.nav-link {
display: grid;
- gap: 0.18rem;
+ gap: 0;
}
.nav-link__label {
- font-weight: 700;
-}
-
-.nav-link__meta {
- color: var(--muted);
- font-size: 0.83rem;
+ font-weight: 800;
}
.nav-link.is-active {
@@ -736,12 +699,19 @@ button:disabled {
.primary-button {
background: var(--accent);
color: #fff8f2;
+ font-weight: 800;
box-shadow: 0 12px 24px rgba(201, 98, 51, 0.24);
}
.danger-button {
background: rgba(175, 45, 33, 0.14);
color: var(--danger);
+ font-weight: 800;
+}
+
+.ghost-button {
+ color: var(--foreground);
+ font-weight: 800;
}
.full-width {
@@ -778,6 +748,10 @@ button:disabled {
gap: 1rem;
}
+.action-row--end {
+ justify-content: flex-end;
+}
+
.section-title--top {
align-items: start;
}
@@ -831,11 +805,12 @@ button:disabled {
.metric-card strong {
font-size: 1.9rem;
- letter-spacing: -0.04em;
+ letter-spacing: 0;
}
.metric-card small {
color: var(--muted);
+ font-weight: 650;
}
.metric-card--warm {
@@ -898,7 +873,7 @@ button:disabled {
border-radius: 999px;
padding: 0.4rem 0.75rem;
font-size: 0.82rem;
- font-weight: 700;
+ font-weight: 800;
border: 1px solid transparent;
}
@@ -952,6 +927,7 @@ button:disabled {
.field span {
font-size: 0.92rem;
color: var(--muted);
+ font-weight: 700;
}
.form-grid.compact {
@@ -999,6 +975,7 @@ button:disabled {
.stat-tile span {
color: var(--muted);
font-size: 0.88rem;
+ font-weight: 700;
}
.metric-inline strong,
@@ -1026,10 +1003,11 @@ button:disabled {
}
.feature-kicker {
- font-family: var(--font-mono);
+ font-family: var(--font-sans);
color: var(--accent-deep);
font-size: 0.76rem;
- letter-spacing: 0.16em;
+ letter-spacing: 0;
+ font-weight: 800;
}
.break-all {
@@ -1080,6 +1058,10 @@ button:disabled {
grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
}
+.proxy-group-builder--main {
+ grid-template-columns: minmax(0, 1fr);
+}
+
.proxy-group-panel {
display: grid;
gap: 0.9rem;
@@ -1237,6 +1219,593 @@ button:disabled {
gap: 1rem;
}
+.management-page {
+ gap: 0.85rem;
+}
+
+.management-page .hero-panel {
+ gap: 0.85rem;
+ padding: 0.95rem 1rem;
+ border-radius: 20px;
+}
+
+.management-page .hero-copy,
+.management-page .hero-aside {
+ gap: 0.55rem;
+}
+
+.management-page .hero-copy h2 {
+ font-size: 1.55rem;
+}
+
+.management-page .page-lede {
+ font-size: 0.94rem;
+ line-height: 1.45;
+}
+
+.management-page .stat-tile {
+ min-height: 4.6rem;
+ padding: 0.7rem;
+ border-radius: 14px;
+}
+
+.management-page .panel-card {
+ padding: 0.8rem 0.9rem;
+ border-radius: 16px;
+}
+
+.management-page .status-pill {
+ padding: 0.24rem 0.52rem;
+ font-size: 0.75rem;
+}
+
+.management-page input,
+.management-page textarea,
+.management-page select {
+ border-radius: 10px;
+ padding: 0.5rem 0.65rem;
+ font-size: 0.9rem;
+}
+
+.management-page textarea {
+ min-height: 4.4rem;
+}
+
+.management-page .primary-button,
+.management-page .ghost-button,
+.management-page .danger-button {
+ border-radius: 10px;
+ padding: 0.55rem 0.72rem;
+}
+
+.compact-create-panel {
+ padding: 0;
+}
+
+.compact-create-panel details {
+ display: grid;
+}
+
+.compact-create-summary {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 1rem;
+ padding: 0.78rem 0.9rem;
+ cursor: pointer;
+ list-style: none;
+}
+
+.compact-create-summary::-webkit-details-marker,
+.entity-table__summary::-webkit-details-marker,
+.config-collapse summary::-webkit-details-marker {
+ display: none;
+}
+
+.compact-create-summary h3 {
+ margin: 0;
+ font-size: 1rem;
+}
+
+.compact-create-summary p {
+ margin: 0.18rem 0 0;
+ font-size: 0.85rem;
+}
+
+.dense-form-grid {
+ display: grid;
+ gap: 0.55rem;
+ padding: 0.75rem 0.9rem 0.9rem;
+ border-top: 1px solid var(--border);
+}
+
+.dense-form-grid--users {
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+}
+
+.dense-form-grid--nodes {
+ grid-template-columns: repeat(5, minmax(0, 1fr));
+}
+
+.dense-form-grid .field {
+ gap: 0.25rem;
+}
+
+.dense-form-grid .field span {
+ font-size: 0.78rem;
+}
+
+.dense-field-wide {
+ grid-column: span 2;
+}
+
+.dense-panel {
+ display: grid;
+ gap: 0.65rem;
+}
+
+.entity-table {
+ overflow: auto;
+ border: 1px solid var(--border);
+ border-radius: 12px;
+ background: rgba(255, 255, 255, 0.28);
+}
+
+.entity-table__head,
+.entity-table__summary {
+ display: grid;
+ align-items: center;
+ gap: 0.7rem;
+ min-width: 56rem;
+}
+
+.entity-table__head {
+ position: sticky;
+ top: 0;
+ z-index: 1;
+ padding: 0.52rem 0.75rem;
+ border-bottom: 1px solid var(--border);
+ background: var(--panel-strong);
+ color: var(--foreground);
+ font-size: 0.76rem;
+ font-weight: 800;
+ letter-spacing: 0;
+ text-transform: uppercase;
+}
+
+.entity-table__item {
+ border-bottom: 1px solid var(--border);
+}
+
+.entity-table__item:last-child {
+ border-bottom: 0;
+}
+
+.entity-table__summary {
+ padding: 0.62rem 0.75rem;
+ cursor: pointer;
+ list-style: none;
+ background: rgba(255, 255, 255, 0.18);
+}
+
+.entity-table__summary:hover {
+ background: rgba(255, 255, 255, 0.34);
+}
+
+.entity-table__details {
+ min-width: 56rem;
+ padding: 0 0.75rem 0.75rem;
+ background: rgba(255, 255, 255, 0.18);
+}
+
+.user-row-grid {
+ grid-template-columns: minmax(12rem, 1.25fr) minmax(11rem, 1.1fr) minmax(8.5rem, 0.85fr) minmax(8rem, 0.8fr) minmax(4.5rem, 0.45fr) minmax(4rem, 0.35fr);
+}
+
+.node-row-grid {
+ grid-template-columns: minmax(10rem, 1fr) minmax(8rem, 0.75fr) minmax(10rem, 0.95fr) minmax(12rem, 1.15fr) minmax(5.5rem, 0.55fr) minmax(9rem, 0.9fr) minmax(4rem, 0.35fr);
+}
+
+.audit-row-grid {
+ grid-template-columns: minmax(4.5rem, 0.45fr) minmax(11rem, 1fr) minmax(10rem, 0.95fr) minmax(6.5rem, 0.65fr) minmax(10rem, 1fr) minmax(10rem, 0.8fr);
+}
+
+.entity-main-cell,
+.entity-number-cell,
+.entity-code-cell,
+.entity-muted-cell {
+ display: grid;
+ gap: 0.12rem;
+ min-width: 0;
+}
+
+.entity-main-cell strong,
+.entity-number-cell strong {
+ font-size: 0.94rem;
+ color: var(--foreground);
+}
+
+.entity-main-cell span,
+.entity-number-cell span,
+.entity-number-cell small,
+.entity-code-cell small,
+.entity-muted-cell,
+.entity-muted-cell small {
+ color: var(--muted);
+ font-size: 0.79rem;
+ font-weight: 650;
+}
+
+.entity-code-cell {
+ font-family: var(--font-mono);
+ font-size: 0.78rem;
+ color: #202b35;
+ font-weight: 650;
+}
+
+.entity-status-cell {
+ gap: 0.28rem;
+}
+
+.entity-expand-label {
+ justify-self: end;
+ color: var(--accent-deep);
+ font-size: 0.8rem;
+ font-weight: 800;
+}
+
+.entity-table__item[open] .entity-expand-label {
+ color: var(--muted);
+}
+
+.dense-code-grid {
+ display: grid;
+ gap: 0.45rem;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ padding-top: 0.65rem;
+}
+
+.dense-code-line {
+ display: grid;
+ gap: 0.25rem;
+ padding: 0.55rem 0.65rem;
+ border: 1px solid var(--border);
+ border-radius: 10px;
+ background: rgba(255, 255, 255, 0.36);
+}
+
+.dense-code-line span {
+ color: var(--muted);
+ font-size: 0.76rem;
+ font-weight: 700;
+}
+
+.dense-code-line code {
+ overflow-wrap: anywhere;
+ font-family: var(--font-mono);
+ font-size: 0.78rem;
+ color: #202b35;
+ font-weight: 650;
+}
+
+.entity-action-strip {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 0.65rem;
+ flex-wrap: wrap;
+ padding-top: 0.65rem;
+}
+
+.inline-form--compact {
+ align-items: center;
+ gap: 0.5rem;
+}
+
+.inline-form--compact input:not([type="hidden"]) {
+ width: min(16rem, 100%);
+}
+
+.config-collapse {
+ margin-top: 0.65rem;
+ border: 1px solid var(--border);
+ border-radius: 10px;
+ background: rgba(255, 255, 255, 0.3);
+}
+
+.config-collapse summary {
+ padding: 0.58rem 0.65rem;
+ cursor: pointer;
+ color: var(--accent-deep);
+ font-weight: 800;
+ list-style: none;
+}
+
+.config-collapse pre {
+ overflow: auto;
+ margin: 0;
+ padding: 0.75rem;
+ border-top: 1px solid var(--border);
+ background: #1a1f25;
+ color: #f7eadf;
+ font-family: var(--font-mono);
+ font-size: 0.78rem;
+}
+
+.config-collapse .inline-button {
+ margin: 0.65rem;
+}
+
+.dashboard-page {
+ gap: 0.75rem;
+}
+
+.dashboard-page .hero-panel {
+ gap: 0.75rem;
+ padding: 0.85rem 1rem;
+ border-radius: 18px;
+}
+
+.dashboard-page .hero-copy,
+.dashboard-page .hero-aside {
+ gap: 0.5rem;
+}
+
+.dashboard-page .hero-copy h2 {
+ font-size: 1.45rem;
+}
+
+.dashboard-page .page-lede {
+ font-size: 0.92rem;
+ line-height: 1.4;
+}
+
+.dashboard-page .badge-row {
+ gap: 0.35rem;
+}
+
+.dashboard-page .status-pill {
+ padding: 0.25rem 0.55rem;
+ font-size: 0.76rem;
+}
+
+.dashboard-page .metrics-grid,
+.dashboard-page .panel-grid {
+ gap: 0.65rem;
+}
+
+.dashboard-page .metric-card,
+.dashboard-page .panel-card {
+ border-radius: 16px;
+ padding: 0.75rem 0.85rem;
+}
+
+.dashboard-page .metric-card {
+ gap: 0.25rem;
+ min-height: 5.8rem;
+}
+
+.dashboard-page .metric-card strong {
+ font-size: 1.45rem;
+}
+
+.dashboard-page .metric-card small,
+.dashboard-page .muted,
+.dashboard-page .list-row p {
+ font-size: 0.84rem;
+}
+
+.dashboard-page .section-title {
+ gap: 0.55rem;
+}
+
+.dashboard-page .section-title h3 {
+ font-size: 1rem;
+}
+
+.dashboard-page .stack-md {
+ gap: 0;
+}
+
+.dashboard-page .list-row--card {
+ padding: 0.55rem 0;
+ border: 0;
+ border-bottom: 1px solid var(--border);
+ border-radius: 0;
+ background: transparent;
+}
+
+.dashboard-page .list-row--card:last-child {
+ border-bottom: 0;
+}
+
+.dashboard-page .list-row p {
+ margin: 0.12rem 0 0;
+}
+
+.dashboard-page .stat-tile {
+ min-height: 4.4rem;
+ padding: 0.65rem;
+ border-radius: 14px;
+}
+
+.dashboard-page select {
+ min-height: 2.25rem;
+ border-radius: 10px;
+ padding: 0.45rem 0.65rem;
+ font-size: 0.9rem;
+}
+
+.dashboard-analytics {
+ display: grid;
+ gap: 0.65rem;
+}
+
+.dashboard-toolbar {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 0.65rem;
+ flex-wrap: wrap;
+}
+
+.chart-filter {
+ display: grid;
+ gap: 0.25rem;
+ min-width: min(100%, 15rem);
+}
+
+.chart-filter label {
+ color: var(--muted);
+ font-size: 0.82rem;
+ font-weight: 700;
+ letter-spacing: 0;
+ text-transform: uppercase;
+}
+
+.dashboard-glance-grid,
+.dashboard-chart-grid {
+ display: grid;
+ gap: 0.65rem;
+}
+
+.dashboard-glance-grid {
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+}
+
+.dashboard-chart-grid {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+}
+
+.dashboard-glance-card,
+.dashboard-chart-card {
+ display: grid;
+ gap: 0.5rem;
+ padding: 0.68rem 0.75rem;
+ border-radius: 14px;
+ border: 1px solid var(--border);
+}
+
+.dashboard-glance-card {
+ min-height: 5.6rem;
+}
+
+.dashboard-glance-card span {
+ color: var(--muted);
+ font-size: 0.8rem;
+ font-weight: 700;
+}
+
+.dashboard-glance-card strong {
+ font-size: 1.28rem;
+ line-height: 1.1;
+}
+
+.dashboard-glance-card small {
+ color: var(--muted);
+ font-size: 0.8rem;
+ font-weight: 650;
+}
+
+.dashboard-glance-card--warm {
+ background: linear-gradient(180deg, rgba(247, 225, 203, 0.86), rgba(255, 250, 242, 0.64));
+}
+
+.dashboard-glance-card--ink {
+ background: linear-gradient(180deg, rgba(223, 231, 236, 0.88), rgba(255, 250, 242, 0.66));
+}
+
+.dashboard-glance-card--sage {
+ background: linear-gradient(180deg, rgba(224, 236, 228, 0.88), rgba(255, 250, 242, 0.66));
+}
+
+.dashboard-glance-card--paper {
+ background: linear-gradient(180deg, rgba(255, 252, 247, 0.92), rgba(255, 250, 242, 0.68));
+}
+
+.dashboard-chart-card {
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.56), rgba(255, 255, 255, 0.28));
+}
+
+.dashboard-chart-card--wide {
+ grid-column: 1 / -1;
+}
+
+.dashboard-chart-card__head {
+ display: grid;
+ gap: 0.45rem;
+}
+
+.dashboard-chart-card h4 {
+ margin: 0;
+ font-size: 0.95rem;
+}
+
+.chart-note {
+ margin: 0;
+ color: var(--muted);
+ font-size: 0.9rem;
+ line-height: 1.5;
+}
+
+.chart-shell,
+.chart-empty {
+ height: 14rem;
+}
+
+.chart-shell--wide {
+ height: 17rem;
+}
+
+.chart-empty {
+ display: grid;
+ place-items: center;
+ padding: 0.75rem;
+ border-radius: 12px;
+ border: 1px dashed var(--border-strong);
+ background: rgba(255, 255, 255, 0.34);
+ color: var(--muted);
+ font-size: 0.9rem;
+ text-align: center;
+}
+
+.node-line-toggles {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 0.35rem;
+}
+
+.node-line-toggle {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.35rem;
+ min-height: 1.7rem;
+ padding: 0.22rem 0.45rem;
+ border: 1px solid var(--border);
+ border-radius: 999px;
+ background: rgba(255, 255, 255, 0.42);
+ color: var(--foreground);
+ font-size: 0.78rem;
+}
+
+.node-line-toggle small {
+ color: var(--muted);
+ font-size: 0.72rem;
+}
+
+.node-line-toggle.is-hidden {
+ opacity: 0.45;
+ background: transparent;
+}
+
+.node-line-toggle__swatch {
+ width: 0.55rem;
+ height: 0.55rem;
+ border-radius: 999px;
+ flex: none;
+}
+
.error-banner,
.error-text {
color: var(--danger);
@@ -1269,7 +1838,6 @@ button:disabled {
.login-layout,
.me-grid,
.me-hero,
- .me-tip-grid,
.me-metrics-grid,
.hero-panel,
.hero-panel--compact,
@@ -1318,6 +1886,15 @@ button:disabled {
.proxy-group-toolbar {
align-items: stretch;
}
+
+ .dashboard-toolbar {
+ align-items: stretch;
+ }
+
+ .dashboard-glance-grid,
+ .dashboard-chart-grid {
+ grid-template-columns: 1fr;
+ }
}
@media (max-width: 640px) {
diff --git a/app/login/page.tsx b/app/login/page.tsx
index 4692c09..7ef44bd 100644
--- a/app/login/page.tsx
+++ b/app/login/page.tsx
@@ -29,23 +29,15 @@ export default async function LoginPage({
return (
-
-
-
-
-
- FEIEPROXY ACCESS
+ 安全登录
鹅梯 FeieProxy
家宽节点
-
- 统一管理多节点鉴权、流量同步与 Clash 订阅。管理员进入控制台,普通用户进入自助页查看剩余流量并生成订阅链接。
-
- 管理员与普通用户共用入口,系统会自动跳转到对应面板。
diff --git a/app/me/page.tsx b/app/me/page.tsx
index 9a9301f..c186197 100644
--- a/app/me/page.tsx
+++ b/app/me/page.tsx
@@ -1,6 +1,7 @@
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";
@@ -31,20 +32,11 @@ export default async function MePage() {
return (
-
-
-
-
-
-
FEIEPROXY SELF SERVICE
+
账户中心
{user.username}
-
- 鹅梯 FeieProxy 用户面板。这里可以查看配额状态、获取订阅链接,并通过
- Clash URL Scheme 一键导入当前配置。
-
- {user.expires_at ?? "长期有效"}
+ {formatLocalDateTime(user.expires_at, "长期有效")}
{describeRemainingTraffic(user)}
@@ -62,7 +54,7 @@ export default async function MePage() {
-
ACCOUNT SNAPSHOT
+
用户 ID
{user.auth_id}
订阅名称:{user.subscription_label || user.username}
@@ -74,18 +66,15 @@ export default async function MePage() {
- SUBSCRIPTION
-
一键导入配置
+ 订阅
+ 订阅
Clash / Mihomo
-
- 推荐直接通过按钮唤起支持 `clash://` 的客户端导入。若浏览器拦截了自定义协议,允许一次即可。
-
{clashImportUrl ? (
- 一键导入 Clash
+ 一键导入
) : (
订阅暂不可用
@@ -102,21 +91,9 @@ export default async function MePage() {
) : null}
-
SUBSCRIPTION URL
+
订阅链接
{subscriptionUrl ?? "暂不可用"}
-
-
-
TIP 01
-
客户端未唤起时
-
确认已安装支持 `clash://` 的客户端,并允许浏览器打开外部应用。
-
-
-
TIP 02
-
手动导入也可用
-
复制上方订阅链接,在 Clash / Mihomo 客户端中粘贴导入即可。
-
-
@@ -145,13 +122,8 @@ export default async function MePage() {
- ACCESS
- 使用说明
-
- - 域名入口适合网页登录与获取订阅。
- - 现有节点鉴权仍可继续使用原来的 `IP:3000` 直连地址。
- - 如果订阅内容更新,重新点击导入按钮即可刷新客户端配置。
-
+ 账户
+ 账户