From 4fcd46e58ca68afc6c952edebf84a0225ba79c06 Mon Sep 17 00:00:00 2001 From: lxien Date: Sun, 16 Aug 2026 17:06:04 +0800 Subject: [PATCH] refactor: Optimize and improve the server-side admin panel. --- desktop/assets/MadeWithSlint-logo-whitebg.png | Bin 12351 -> 0 bytes server-ui/package-lock.json | 4 +- server-ui/package.json | 2 +- server-ui/src/api/client.ts | 32 +-- server-ui/src/api/index.ts | 6 +- server-ui/src/assets/icon/arrow-down.svg | 6 + server-ui/src/assets/icon/arrow-left.svg | 6 + server-ui/src/assets/icon/arrow-right.svg | 6 + server-ui/src/assets/icon/arrow-up.svg | 8 + server-ui/src/assets/icon/computer.svg | 6 + server-ui/src/assets/icon/download.svg | 6 + server-ui/src/assets/icon/kick.svg | 6 + server-ui/src/assets/icon/link.svg | 10 + server-ui/src/assets/icon/moon.svg | 6 + server-ui/src/assets/icon/search.svg | 10 + server-ui/src/assets/icon/share.svg | 6 + server-ui/src/assets/icon/signal.svg | 6 + server-ui/src/assets/icon/sun.svg | 6 + server-ui/src/assets/icon/upload.svg | 6 + server-ui/src/assets/icon/user.svg | 14 ++ server-ui/src/assets/icon/users.svg | 10 + server-ui/src/assets/styles/main.css | 132 ++-------- server-ui/src/components/AppIcon.vue | 128 +++------- server-ui/src/components/DonutChart.vue | 2 +- server-ui/src/components/IconBadge.vue | 17 +- server-ui/src/components/PaginationBar.vue | 34 +-- server-ui/src/components/SectionCard.vue | 20 +- server-ui/src/components/StatCard.vue | 12 +- server-ui/src/components/ThemeToggle.vue | 30 ++- server-ui/src/components/TrafficChart.vue | 12 +- server-ui/src/components/TrafficIO.vue | 27 +- server-ui/src/components/TrafficSummary.vue | 36 ++- server-ui/src/composables/useLocale.ts | 10 +- server-ui/src/composables/usePresence.ts | 31 +++ server-ui/src/composables/useSidebar.ts | 5 - server-ui/src/composables/useTheme.ts | 38 ++- server-ui/src/constants/menus.ts | 8 +- server-ui/src/i18n/messages/en-US/index.ts | 96 ++------ server-ui/src/i18n/messages/zh-CN/index.ts | 96 ++------ server-ui/src/i18n/schema.ts | 88 ++----- server-ui/src/layouts/AppHeader.vue | 7 +- server-ui/src/layouts/AppSidebar.vue | 39 +-- server-ui/src/router/index.ts | 11 +- server-ui/src/stores/dashboard.ts | 23 +- server-ui/src/types/api.ts | 37 ++- server-ui/src/utils/format.ts | 6 +- server-ui/src/utils/index.ts | 15 -- server-ui/src/utils/os.ts | 4 - server-ui/src/utils/theme.ts | 6 - server-ui/src/views/ClientDetail.vue | 232 ++++++++---------- server-ui/src/views/Clients.vue | 84 +++---- server-ui/src/views/Monitor.vue | 86 ++++--- .../{ProxyDetail.vue => TunnelDetail.vue} | 67 +++-- .../src/views/{Proxies.vue => Tunnels.vue} | 105 ++++---- 54 files changed, 722 insertions(+), 984 deletions(-) delete mode 100644 desktop/assets/MadeWithSlint-logo-whitebg.png create mode 100644 server-ui/src/assets/icon/arrow-down.svg create mode 100644 server-ui/src/assets/icon/arrow-left.svg create mode 100644 server-ui/src/assets/icon/arrow-right.svg create mode 100644 server-ui/src/assets/icon/arrow-up.svg create mode 100644 server-ui/src/assets/icon/computer.svg create mode 100644 server-ui/src/assets/icon/download.svg create mode 100644 server-ui/src/assets/icon/kick.svg create mode 100644 server-ui/src/assets/icon/link.svg create mode 100644 server-ui/src/assets/icon/moon.svg create mode 100644 server-ui/src/assets/icon/search.svg create mode 100644 server-ui/src/assets/icon/share.svg create mode 100644 server-ui/src/assets/icon/signal.svg create mode 100644 server-ui/src/assets/icon/sun.svg create mode 100644 server-ui/src/assets/icon/upload.svg create mode 100644 server-ui/src/assets/icon/user.svg create mode 100644 server-ui/src/assets/icon/users.svg create mode 100644 server-ui/src/composables/usePresence.ts delete mode 100644 server-ui/src/utils/index.ts rename server-ui/src/views/{ProxyDetail.vue => TunnelDetail.vue} (83%) rename server-ui/src/views/{Proxies.vue => Tunnels.vue} (76%) diff --git a/desktop/assets/MadeWithSlint-logo-whitebg.png b/desktop/assets/MadeWithSlint-logo-whitebg.png deleted file mode 100644 index f7f39c1170384a4ac35b73e54f807fcc03bff8c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12351 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

$#u-~joKRB^N1QZgdeVGc?}nLOxy?w| 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 diff --git a/server-ui/package-lock.json b/server-ui/package-lock.json index 18a1693..51005f2 100644 --- a/server-ui/package-lock.json +++ b/server-ui/package-lock.json @@ -1,12 +1,12 @@ { "name": "orbien-server-ui", - "version": "2.1.0-SNAPSHOT", + "version": "3.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "orbien-server-ui", - "version": "2.1.0-SNAPSHOT", + "version": "3.0.0", "dependencies": { "vue": "^3.5.22", "vue-i18n": "^11.4.8", diff --git a/server-ui/package.json b/server-ui/package.json index 14d4e54..b60c5a3 100644 --- a/server-ui/package.json +++ b/server-ui/package.json @@ -1,7 +1,7 @@ { "name": "orbien-server-ui", "private": true, - "version": "2.1.0-SNAPSHOT", + "version": "3.0.0", "type": "module", "scripts": { "dev": "vite", diff --git a/server-ui/src/api/client.ts b/server-ui/src/api/client.ts index bccda09..0599697 100644 --- a/server-ui/src/api/client.ts +++ b/server-ui/src/api/client.ts @@ -2,8 +2,8 @@ import type { Page, SystemInfo, ClientInfo, - ProxyInfo, - ProxyTrafficResp, + TunnelInfo, + TunnelTrafficResp, ApiResponse, } from '@/types/api' import {ApiError} from './errors' @@ -31,34 +31,34 @@ export function fetchClients(page = 1, pageSize = 200) { return api>(`/api/v1/clients?page=${page}&pageSize=${pageSize}`) } -export function fetchClient(runId: string) { - return api(`/api/v1/clients/${encodeURIComponent(runId)}`) +export function fetchClient(sessionId: string) { + return api(`/api/v1/clients/${encodeURIComponent(sessionId)}`) } -export function kickClient(runId: string) { - return api(`/api/v1/clients/${encodeURIComponent(runId)}/kick`, { +export function kickClient(sessionId: string) { + return api(`/api/v1/clients/${encodeURIComponent(sessionId)}/kick`, { method: 'POST', }) } -export type ProxyListParams = { +export type TunnelListParams = { page?: number pageSize?: number - clientId?: string + sessionId?: string q?: string } -export function fetchProxies(pageOrParams: number | ProxyListParams = 1, pageSize = 200) { - const params: ProxyListParams = +export function fetchTunnels(pageOrParams: number | TunnelListParams = 1, pageSize = 200) { + const params: TunnelListParams = typeof pageOrParams === 'number' ? {page: pageOrParams, pageSize} : pageOrParams const qs = new URLSearchParams() qs.set('page', String(params.page ?? 1)) qs.set('pageSize', String(params.pageSize ?? 200)) - if (params.clientId) qs.set('clientId', params.clientId) + if (params.sessionId) qs.set('sessionId', params.sessionId) if (params.q) qs.set('q', params.q) - return api>(`/api/v1/proxies?${qs.toString()}`) + return api>(`/api/v1/tunnels?${qs.toString()}`) } export type TrafficRange = '7d' | '24h' @@ -67,12 +67,12 @@ function trafficQuery(range: TrafficRange = '7d') { return range === '24h' ? '?range=24h' : '?range=7d' } -export function fetchProxyTraffic(name: string, range: TrafficRange = '7d') { - return api( - `/api/v1/proxies/${encodeURIComponent(name)}/traffic${trafficQuery(range)}`, +export function fetchTunnelTraffic(name: string, range: TrafficRange = '7d') { + return api( + `/api/v1/tunnels/${encodeURIComponent(name)}/traffic${trafficQuery(range)}`, ) } export function fetchSystemTraffic(range: TrafficRange = '7d') { - return api(`/api/v1/system/traffic${trafficQuery(range)}`) + return api(`/api/v1/system/traffic${trafficQuery(range)}`) } diff --git a/server-ui/src/api/index.ts b/server-ui/src/api/index.ts index 5c7cfa0..cea0fcc 100644 --- a/server-ui/src/api/index.ts +++ b/server-ui/src/api/index.ts @@ -4,8 +4,8 @@ export { fetchClients, fetchClient, kickClient, - fetchProxies, - fetchProxyTraffic, + fetchTunnels, + fetchTunnelTraffic, fetchSystemTraffic, } from './client' -export type {ProxyListParams, TrafficRange} from './client' +export type {TunnelListParams, TrafficRange} from './client' diff --git a/server-ui/src/assets/icon/arrow-down.svg b/server-ui/src/assets/icon/arrow-down.svg new file mode 100644 index 0000000..46d7cdc --- /dev/null +++ b/server-ui/src/assets/icon/arrow-down.svg @@ -0,0 +1,6 @@ + + + diff --git a/server-ui/src/assets/icon/arrow-left.svg b/server-ui/src/assets/icon/arrow-left.svg new file mode 100644 index 0000000..37945df --- /dev/null +++ b/server-ui/src/assets/icon/arrow-left.svg @@ -0,0 +1,6 @@ + + + diff --git a/server-ui/src/assets/icon/arrow-right.svg b/server-ui/src/assets/icon/arrow-right.svg new file mode 100644 index 0000000..652ddfb --- /dev/null +++ b/server-ui/src/assets/icon/arrow-right.svg @@ -0,0 +1,6 @@ + + + diff --git a/server-ui/src/assets/icon/arrow-up.svg b/server-ui/src/assets/icon/arrow-up.svg new file mode 100644 index 0000000..e5e02ad --- /dev/null +++ b/server-ui/src/assets/icon/arrow-up.svg @@ -0,0 +1,8 @@ + + + + + diff --git a/server-ui/src/assets/icon/computer.svg b/server-ui/src/assets/icon/computer.svg new file mode 100644 index 0000000..e26ddee --- /dev/null +++ b/server-ui/src/assets/icon/computer.svg @@ -0,0 +1,6 @@ + + + diff --git a/server-ui/src/assets/icon/download.svg b/server-ui/src/assets/icon/download.svg new file mode 100644 index 0000000..e220ff2 --- /dev/null +++ b/server-ui/src/assets/icon/download.svg @@ -0,0 +1,6 @@ + + + diff --git a/server-ui/src/assets/icon/kick.svg b/server-ui/src/assets/icon/kick.svg new file mode 100644 index 0000000..7c277a2 --- /dev/null +++ b/server-ui/src/assets/icon/kick.svg @@ -0,0 +1,6 @@ + + + diff --git a/server-ui/src/assets/icon/link.svg b/server-ui/src/assets/icon/link.svg new file mode 100644 index 0000000..7333c8c --- /dev/null +++ b/server-ui/src/assets/icon/link.svg @@ -0,0 +1,10 @@ + + + + diff --git a/server-ui/src/assets/icon/moon.svg b/server-ui/src/assets/icon/moon.svg new file mode 100644 index 0000000..ba37e58 --- /dev/null +++ b/server-ui/src/assets/icon/moon.svg @@ -0,0 +1,6 @@ + + + diff --git a/server-ui/src/assets/icon/search.svg b/server-ui/src/assets/icon/search.svg new file mode 100644 index 0000000..dd30c8d --- /dev/null +++ b/server-ui/src/assets/icon/search.svg @@ -0,0 +1,10 @@ + + + + diff --git a/server-ui/src/assets/icon/share.svg b/server-ui/src/assets/icon/share.svg new file mode 100644 index 0000000..28eb068 --- /dev/null +++ b/server-ui/src/assets/icon/share.svg @@ -0,0 +1,6 @@ + + + diff --git a/server-ui/src/assets/icon/signal.svg b/server-ui/src/assets/icon/signal.svg new file mode 100644 index 0000000..270db29 --- /dev/null +++ b/server-ui/src/assets/icon/signal.svg @@ -0,0 +1,6 @@ + + + diff --git a/server-ui/src/assets/icon/sun.svg b/server-ui/src/assets/icon/sun.svg new file mode 100644 index 0000000..fa8d400 --- /dev/null +++ b/server-ui/src/assets/icon/sun.svg @@ -0,0 +1,6 @@ + + + diff --git a/server-ui/src/assets/icon/upload.svg b/server-ui/src/assets/icon/upload.svg new file mode 100644 index 0000000..8781e50 --- /dev/null +++ b/server-ui/src/assets/icon/upload.svg @@ -0,0 +1,6 @@ + + + diff --git a/server-ui/src/assets/icon/user.svg b/server-ui/src/assets/icon/user.svg new file mode 100644 index 0000000..e8a894f --- /dev/null +++ b/server-ui/src/assets/icon/user.svg @@ -0,0 +1,14 @@ + + + + + diff --git a/server-ui/src/assets/icon/users.svg b/server-ui/src/assets/icon/users.svg new file mode 100644 index 0000000..8a0ba4b --- /dev/null +++ b/server-ui/src/assets/icon/users.svg @@ -0,0 +1,10 @@ + + + + diff --git a/server-ui/src/assets/styles/main.css b/server-ui/src/assets/styles/main.css index 56283ab..83350f5 100644 --- a/server-ui/src/assets/styles/main.css +++ b/server-ui/src/assets/styles/main.css @@ -23,7 +23,6 @@ html[data-theme='dark'] { --focus-ring: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent); --status-ok: #52c41a; --status-ok-soft: rgba(82, 196, 26, 0.14); - --table-head-bg: rgba(255, 255, 255, 0.04); --scrollbar: #434343; --header-height: 56px; --sidebar-width: 200px; @@ -55,7 +54,6 @@ html[data-theme='light'] { --focus-ring: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent); --status-ok: #52c41a; --status-ok-soft: rgba(82, 196, 26, 0.1); - --table-head-bg: #fafafa; --scrollbar: #d9d9d9; } @@ -82,11 +80,8 @@ body { .top, .sidebar, .card, -table, -.ghost, .theme-toggle, .icon-btn, -.pill, .err, .side-link, .sidebar-collapse { @@ -147,6 +142,9 @@ table, align-items: center; gap: 0.65rem; min-width: 0; + text-decoration: none; + color: inherit; + cursor: pointer; } .logo-img { @@ -186,7 +184,6 @@ html[data-theme='dark'] .brand-orb { flex-shrink: 0; } -.ghost, .theme-toggle, .icon-btn { background: var(--panel); @@ -217,14 +214,12 @@ html[data-theme='dark'] .brand-orb { stroke-linejoin: round; } -.ghost:hover:not(:disabled), .theme-toggle:hover, .icon-btn:hover { border-color: var(--line-strong); background: var(--panel-hover); } -.ghost:focus-visible, .theme-toggle:focus-visible, .icon-btn:focus-visible, .side-link:focus-visible, @@ -234,11 +229,6 @@ html[data-theme='dark'] .brand-orb { box-shadow: var(--focus-ring); } -.ghost:disabled { - opacity: 0.5; - cursor: not-allowed; -} - .theme-toggle { display: inline-grid; place-items: center; @@ -251,11 +241,8 @@ html[data-theme='dark'] .brand-orb { .theme-toggle svg { width: 1.1rem; height: 1.1rem; - fill: none; - stroke: currentColor; - stroke-width: 1.8; - stroke-linecap: round; - stroke-linejoin: round; + fill: currentColor; + stroke: none; } .locale-switch { @@ -355,11 +342,8 @@ html[data-theme='dark'] .brand-orb { .side-icon svg { width: 1.2rem; height: 1.2rem; - fill: none; - stroke: currentColor; - stroke-width: 1.7; - stroke-linecap: round; - stroke-linejoin: round; + fill: currentColor; + stroke: none; } .side-label { @@ -395,14 +379,26 @@ html[data-theme='dark'] .brand-orb { } .sidebar-collapse svg { - width: 1.1rem; - height: 1.1rem; + width: 1.05rem; + height: 1.05rem; flex-shrink: 0; - fill: none; - stroke: currentColor; - stroke-width: 1.8; - stroke-linecap: round; - stroke-linejoin: round; + fill: currentColor; + stroke: none; +} + +.collapse-icon { + width: 1.05rem; + height: 1.05rem; + display: inline-grid; + place-items: center; + line-height: 0; + color: inherit; +} + +.collapse-icon svg { + width: 100%; + height: 100%; + display: block; } .sidebar-backdrop { @@ -454,10 +450,6 @@ html[data-theme='dark'] .brand-orb { border-color: var(--line-strong); } -.card.wide { - grid-column: 1 / -1; -} - .k { color: var(--muted); font-size: 0.85rem; @@ -477,78 +469,6 @@ html[data-theme='dark'] .brand-orb { color: var(--text-secondary); } -table { - width: 100%; - border-collapse: collapse; - background: var(--panel); - border: 1px solid var(--line); - border-radius: 12px; - overflow: hidden; - box-shadow: var(--shadow); -} - -thead { - background: var(--table-head-bg); -} - -th, -td { - text-align: left; - padding: 0.7rem 0.85rem; - border-bottom: 1px solid var(--line); - color: var(--text); -} - -th { - color: var(--muted); - font-weight: 500; - font-size: 0.85rem; -} - -tbody tr:hover td { - background: var(--panel-hover); -} - -tbody tr:last-child td { - border-bottom: none; -} - -.empty { - color: var(--muted); - text-align: center; -} - -.pill { - display: inline-block; - background: var(--accent-soft); - color: var(--accent-text); - border: 1px solid transparent; - padding: 0.15rem 0.55rem; - border-radius: 999px; - font-size: 0.8rem; - font-weight: 600; -} - -.status { - display: inline-flex; - align-items: center; - gap: 0.35rem; - color: var(--status-ok); - background: var(--status-ok-soft); - padding: 0.15rem 0.55rem; - border-radius: 999px; - font-size: 0.8rem; - font-weight: 600; -} - -.status::before { - content: ''; - width: 0.45rem; - height: 0.45rem; - border-radius: 50%; - background: currentColor; -} - .err { color: var(--danger); background: var(--danger-bg); diff --git a/server-ui/src/components/AppIcon.vue b/server-ui/src/components/AppIcon.vue index a44b7e1..5afe4a4 100644 --- a/server-ui/src/components/AppIcon.vue +++ b/server-ui/src/components/AppIcon.vue @@ -1,112 +1,50 @@ diff --git a/server-ui/src/components/DonutChart.vue b/server-ui/src/components/DonutChart.vue index b2d1fe0..86acbd7 100644 --- a/server-ui/src/components/DonutChart.vue +++ b/server-ui/src/components/DonutChart.vue @@ -66,7 +66,7 @@ function donutArc(x: number, y: number, radius: number, start: number, end: numb class="donut-svg" :viewBox="`0 0 ${size} ${size}`" role="img" - :aria-label="t('monitor.proxyTypes')" + :aria-label="t('monitor.tunnelTypes')" > -export type IconTone = 'blue' | 'green' | 'purple' | 'violet' | 'orange' | 'slate' | 'teal' +export type IconTone = 'blue' | 'green' | 'violet' | 'orange' @@ -61,10 +57,4 @@ withDefaults( .stat-icon { margin-top: 0.05rem; } - -.stat-hint { - margin-top: 0.45rem; - color: var(--muted); - font-size: 0.78rem; -} diff --git a/server-ui/src/components/ThemeToggle.vue b/server-ui/src/components/ThemeToggle.vue index b3634b9..ee4e35d 100644 --- a/server-ui/src/components/ThemeToggle.vue +++ b/server-ui/src/components/ThemeToggle.vue @@ -1,5 +1,7 @@ @@ -12,14 +14,24 @@ const {isDark, label, toggle} = useTheme() :title="label" @click="toggle" > - - +