一款优雅的 NodeGet 前端公开状态监控页。支持全球 2D 地图、服务器在线状态、CPU / 内存 / 磁盘 / 网络监控、流量统计、成本与到期时间展示等功能。纯静态构建,并可一键部署到 Cloudflare Pages、EdgeOne Pages 等静态托管平台。
准备写个部署教程的,结果看见官方文档已经非常详细地说明了安装与部署流程。
[!warning] 在阅读本文前,建议先阅读官方教程
https://nodeget.com/guide/install/status-show.html
演示
前台地址:https://hudsonstatus.caileisir.workers.dev/
仓库地址:hudson-Status
图片点击展开

[!question] 配色不喜欢?
每个人的审美和对颜色的要求的不一样的,请滑动到底部的自定义CSS配置
目前实现
- 全球 2D 地图节点展示
- 多节点在线状态监控
- CPU / 内存 / 磁盘 / 网络监控
- Ping 与曲线图
- 成本和到期展示
- 深色模式
- Cloudflare Pages 一键部署
- 任意静态托管平台部署
配置文件
Important
前端启动后会请求 /config.json
开发环境与生产环境均从站点根路径读取配置。
构建时:
public/config.json
↓
dist/config.json
配置字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
site_name |
字符串 | 浏览器标题与页头展示名称 |
site_logo |
字符串 | 页头 Logo URL(可为相对路径如 /favicon.svg,也可为空) |
footer |
字符串 | 页脚文案 |
site_tokens |
数组 | 多条后端连接;每项见下表 |
site_tokens 每一项:
| 字段 | 说明 |
|---|---|
name |
展示名称(如「master-1」) |
backend_url |
NodeGet WebSocket 地址,一般为 wss://你的域名/... |
token |
Dashboard创建的 Visitor 权限 token |
配置示例
{
"site_name": "Hudson Status",
"site_logo": "/logo.svg",
"footer": "Powered by NodeGet",
"site_tokens": [
{
"name": "master server node 1",
"backend_url": "wss://your-backend.example.com",
"token": "YOUR_TOKEN_HERE"
}
]
}
Warning
修改配置后需重新构建,或同步更新 dist/config.json
构建与部署
部署方式
Note
本项目为纯静态页面,可部署到任意静态托管平台。
支持:
- Cloudflare Pages
- EdgeOne Pages
- ..............
一键部署
推荐流程
Fork 仓库
↓
修改 public/config.json
↓
Cloudflare 一键部署
↓
完成
详细教程
ZIP 版本
自定义 CSS
全局主题入口:
src/index.css
默认配色(赛博小清新)
index.css:
@import "tailwindcss";
@plugin "tailwindcss-animate";
@custom-variant dark (&:is(.dark *));
/**
* 全局样式:Tailwind v4、语义色 token、@theme 映射、基础排版与动画工具类。
*
* 自定义属性(--app-*,OKLCH:L 明度 / C 色度 / hue 色相):
* - --app-background / --app-foreground:页面背景与主文字色
* - --app-card / --app-card-foreground:卡片表面与文字
* - --app-popover / --app-popover-foreground:浮层(下拉、气泡)表面与文字
* - --app-primary / --app-primary-foreground:主色(品牌/主要按钮)与其上前景
* - --app-accent / --app-accent-foreground:强调(悬停、选中)与前景;亮色偏绿、暗色偏蓝灰以区别于 primary
* - --app-secondary / --app-secondary-foreground:次级表面与文字
* - --app-muted / --app-muted-foreground:弱化背景与辅助文字
* - --app-destructive / --app-destructive-foreground:危险操作与前景
* - --app-border / --app-input / --app-ring:边框、输入框边框、焦点环
* - --app-sidebar-*:侧栏背景/文字/主色/强调/边框/环(与 accent 策略同步)
* - --app-chart-1 … --app-chart-5:图表序列色
*
* :root 定义亮色;html.dark 覆盖为暗色,并设置 --shadow-sm。
* @theme inline:把 --app-* 接到 Tailwind --color-*,以及 --radius、字体、accordion keyframes。
*
* 自定义动画:fade-in、pulse-ring、pulse-dot、scanline、data-flicker、progress、shimmer;
* 工具类 .animate-*、.bg-grid-pattern。
*/
:root {
--app-background: oklch(0.98 0.004 85);
--app-foreground: oklch(0.25 0.01 250);
--app-card: oklch(0.995 0.002 85);
--app-card-foreground: oklch(0.25 0.01 250);
--app-popover: oklch(0.995 0.002 85);
--app-popover-foreground: oklch(0.25 0.01 250);
--app-primary: oklch(0.58 0.08 165);
--app-primary-foreground: oklch(0.98 0.005 165);
--app-accent: oklch(0.80 0.08 165);
--app-accent-foreground: oklch(0.16 0.06 165);
--app-secondary: oklch(0.92 0.02 165);
--app-secondary-foreground: oklch(0.35 0.02 165);
--app-muted: oklch(0.92 0.01 85);
--app-muted-foreground: oklch(0.50 0.02 250);
--app-destructive: oklch(0.55 0.15 25);
--app-destructive-foreground: oklch(0.98 0 0);
--app-border: oklch(0.85 0.01 85);
--app-input: oklch(0.85 0.01 85);
--app-ring: oklch(0.58 0.08 165);
--app-sidebar: oklch(0.96 0.01 165);
--app-sidebar-foreground: oklch(0.25 0.01 250);
--app-sidebar-primary: oklch(0.58 0.08 165);
--app-sidebar-primary-foreground: oklch(0.98 0.005 165);
--app-sidebar-accent: oklch(0.80 0.08 165);
--app-sidebar-accent-foreground: oklch(0.16 0.06 165);
--app-sidebar-border: oklch(0.85 0.01 85);
--app-sidebar-ring: oklch(0.58 0.08 165);
--app-chart-1: oklch(0.58 0.08 165);
--app-chart-2: oklch(0.65 0.08 240);
--app-chart-3: oklch(0.70 0.10 50);
--app-chart-4: oklch(0.65 0.09 300);
--app-chart-5: oklch(0.72 0.08 90);
}
html.dark {
--app-background: oklch(0.15 0.01 250);
--app-foreground: oklch(0.92 0.005 250);
--app-card: oklch(0.22 0.01 250);
--app-card-foreground: oklch(0.92 0.005 250);
--app-popover: oklch(0.22 0.01 250);
--app-popover-foreground: oklch(0.92 0.005 250);
--app-primary: oklch(0.75 0.10 165);
--app-primary-foreground: oklch(0.15 0.02 165);
--app-accent: oklch(0.42 0.09 250);
--app-accent-foreground: oklch(0.95 0.006 250);
--app-secondary: oklch(0.28 0.02 250);
--app-secondary-foreground: oklch(0.92 0.005 250);
--app-muted: oklch(0.28 0.01 250);
--app-muted-foreground: oklch(0.70 0.01 250);
--app-destructive: oklch(0.50 0.18 25);
--app-destructive-foreground: oklch(0.98 0 0);
--app-border: oklch(0.32 0.01 250);
--app-input: oklch(0.32 0.01 250);
--app-ring: oklch(0.75 0.10 165);
--app-sidebar: oklch(0.18 0.01 250);
--app-sidebar-foreground: oklch(0.92 0.005 250);
--app-sidebar-primary: oklch(0.75 0.10 165);
--app-sidebar-primary-foreground: oklch(0.15 0.02 165);
--app-sidebar-accent: oklch(0.42 0.09 250);
--app-sidebar-accent-foreground: oklch(0.95 0.006 250);
--app-sidebar-border: oklch(0.32 0.01 250);
--app-sidebar-ring: oklch(0.75 0.10 165);
--app-chart-1: oklch(0.75 0.10 165);
--app-chart-2: oklch(0.78 0.08 240);
--app-chart-3: oklch(0.82 0.10 50);
--app-chart-4: oklch(0.78 0.09 300);
--app-chart-5: oklch(0.85 0.08 90);
--shadow-sm: 0 4px 12px -2px rgba(0, 0, 0, 0.6);
}
@theme inline {
--color-background: var(--app-background);
--color-foreground: var(--app-foreground);
--color-card: var(--app-card);
--color-card-foreground: var(--app-card-foreground);
--color-popover: var(--app-popover);
--color-popover-foreground: var(--app-popover-foreground);
--color-primary: var(--app-primary);
--color-primary-foreground: var(--app-primary-foreground);
--color-secondary: var(--app-secondary);
--color-secondary-foreground: var(--app-secondary-foreground);
--color-muted: var(--app-muted);
--color-muted-foreground: var(--app-muted-foreground);
--color-accent: var(--app-accent);
--color-accent-foreground: var(--app-accent-foreground);
--color-destructive: var(--app-destructive);
--color-destructive-foreground: var(--app-destructive-foreground);
--color-border: var(--app-border);
--color-input: var(--app-input);
--color-ring: var(--app-ring);
--color-sidebar: var(--app-sidebar);
--color-sidebar-foreground: var(--app-sidebar-foreground);
--color-sidebar-primary: var(--app-sidebar-primary);
--color-sidebar-primary-foreground: var(--app-sidebar-primary-foreground);
--color-sidebar-accent: var(--app-sidebar-accent);
--color-sidebar-accent-foreground: var(--app-sidebar-accent-foreground);
--color-sidebar-border: var(--app-sidebar-border);
--color-sidebar-ring: var(--app-sidebar-ring);
--color-chart-1: var(--app-chart-1);
--color-chart-2: var(--app-chart-2);
--color-chart-3: var(--app-chart-3);
--color-chart-4: var(--app-chart-4);
--color-chart-5: var(--app-chart-5);
--radius: 0.5rem;
--radius-lg: var(--radius);
--radius-md: calc(var(--radius) - 2px);
--radius-sm: calc(var(--radius) - 4px);
--spacing: 0.25rem;
--font-sans: DM Sans, sans-serif;
--font-serif: Georgia, serif;
--font-mono: Menlo, monospace;
--animate-accordion-down: accordion-down 0.2s ease-out;
--animate-accordion-up: accordion-up 0.2s ease-out;
@keyframes accordion-down {
from { height: 0; }
to { height: var(--radix-accordion-content-height); }
}
@keyframes accordion-up {
from { height: var(--radix-accordion-content-height); }
to { height: 0; }
}
}
@layer base {
* { @apply border-border; }
html {
font-size: 16px;
-webkit-text-size-adjust: 100%;
text-size-adjust: 100%;
}
@media (min-width: 640px) { html { font-size: 16.5px; } }
@media (min-width: 1024px) { html { font-size: 17.5px; } }
@media (min-width: 1280px) { html { font-size: 18px; } }
body {
@apply bg-background text-foreground antialiased;
font-synthesis: none;
}
}
@keyframes fade-in {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes pulse-ring {
0% { transform: scale(1); opacity: 0.6; }
80%, 100% { transform: scale(2.2); opacity: 0; }
}
@keyframes pulse-dot {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.15); }
}
@keyframes scanline {
0% { transform: translateY(-100vh); }
100% { transform: translateY(100vh); }
}
@keyframes data-flicker {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
}
@keyframes progress {
0% { width: 0%; }
50% { width: 70%; }
100% { width: 100%; }
}
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}
.bg-grid-pattern {
background-image:
linear-gradient(to right, currentColor 1px, transparent 1px),
linear-gradient(to bottom, currentColor 1px, transparent 1px);
background-size: 40px 40px;
}
.animate-fade-in {
animation: fade-in 0.5s ease-out forwards;
}
.animate-pulse-ring {
animation: pulse-ring 1.5s ease-out infinite;
}
.animate-pulse-dot {
animation: pulse-dot 2s ease-in-out infinite;
}
.animate-scanline {
animation: scanline 8s linear infinite;
}
.animate-data-flicker {
animation: data-flicker 3s ease-in-out infinite;
}
.animate-progress {
animation: progress 3s ease-in-out infinite;
}
.animate-shimmer {
animation: shimmer 2s linear infinite;
}
琥珀
index.css:
@import "tailwindcss";
@plugin "tailwindcss-animate";
@custom-variant dark (&:is(.dark *));
/**
* 全局样式:Tailwind v4、语义色 token、@theme 映射、基础排版与动画工具类。
*
* 自定义属性(--app-*,OKLCH:L 明度 / C 色度 / hue 色相):
* - --app-background / --app-foreground:页面背景与主文字色
* - --app-card / --app-card-foreground:卡片表面与文字
* - --app-popover / --app-popover-foreground:浮层(下拉、气泡)表面与文字
* - --app-primary / --app-primary-foreground:主色(品牌/主要按钮)与其上前景
* - --app-accent / --app-accent-foreground:强调(悬停、选中)与前景;亮色偏绿、暗色偏蓝灰以区别于 primary
* - --app-secondary / --app-secondary-foreground:次级表面与文字
* - --app-muted / --app-muted-foreground:弱化背景与辅助文字
* - --app-destructive / --app-destructive-foreground:危险操作与前景
* - --app-border / --app-input / --app-ring:边框、输入框边框、焦点环
* - --app-sidebar-*:侧栏背景/文字/主色/强调/边框/环(与 accent 策略同步)
* - --app-chart-1 … --app-chart-5:图表序列色
*
* :root 定义亮色;html.dark 覆盖为暗色,并设置 --shadow-sm。
* @theme inline:把 --app-* 接到 Tailwind --color-*,以及 --radius、字体、accordion keyframes。
*
* 自定义动画:fade-in、pulse-ring、pulse-dot、scanline、data-flicker、progress、shimmer;
* 工具类 .animate-*、.bg-grid-pattern。
*/
:root {
--app-background: oklch(0.9529 0.0146 102.4597);
--app-foreground: oklch(0.4063 0.0255 40.3627);
--app-card: oklch(0.9529 0.0146 102.4597);
--app-card-foreground: oklch(0.4063 0.0255 40.3627);
--app-popover: oklch(0.9529 0.0146 102.4597);
--app-popover-foreground: oklch(0.4063 0.0255 40.3627);
--app-primary: oklch(0.6083 0.0623 44.3588);
--app-primary-foreground: oklch(1 0 0);
--app-secondary: oklch(0.7473 0.0387 80.5476);
--app-secondary-foreground: oklch(1 0 0);
--app-muted: oklch(0.8502 0.0389 49.0874);
--app-muted-foreground: oklch(0.5416 0.0512 37.2132);
--app-accent: oklch(0.8502 0.0389 49.0874);
--app-accent-foreground: oklch(0.4063 0.0255 40.3627);
--app-destructive: oklch(0.2225 0.0098 52.9636);
--app-destructive-foreground: oklch(1 0 0);
--app-border: oklch(0.7473 0.0387 80.5476);
--app-input: oklch(0.7473 0.0387 80.5476);
--app-ring: oklch(0.6083 0.0623 44.3588);
--app-sidebar: oklch(0.8903 0.0278 49.5658);
--app-sidebar-foreground: oklch(0.4063 0.0255 40.3627);
--app-sidebar-primary: oklch(0.6083 0.0623 44.3588);
--app-sidebar-primary-foreground: oklch(1 0 0);
--app-sidebar-accent: oklch(0.7272 0.0539 52.332);
--app-sidebar-accent-foreground: oklch(1 0 0);
--app-sidebar-border: oklch(0.644 0.0405 52.3917);
--app-sidebar-ring: oklch(0.6083 0.0623 44.3588);
--app-chart-1: oklch(0.6083 0.0623 44.3588);
--app-chart-2: oklch(0.5416 0.0512 37.2132);
--app-chart-3: oklch(0.7272 0.0539 52.332);
--app-chart-4: oklch(0.7473 0.0387 80.5476);
--app-chart-5: oklch(0.644 0.0405 52.3917);
}
html.dark {
/* 柔和「炭灰 + 克制琥珀」:略提亮底、降饱和主色,减轻黑金刺眼感 */
--app-background: oklch(0.22 0.016 82);
--app-foreground: oklch(0.87 0.022 84);
--app-card: oklch(0.26 0.02 80);
--app-card-foreground: oklch(0.87 0.022 84);
--app-popover: oklch(0.28 0.022 79);
--app-popover-foreground: oklch(0.87 0.022 84);
--app-primary: oklch(0.69 0.07 76);
--app-primary-foreground: oklch(0.17 0.02 82);
--app-secondary: oklch(0.34 0.038 77);
--app-secondary-foreground: oklch(0.87 0.022 84);
--app-muted: oklch(0.32 0.028 78);
--app-muted-foreground: oklch(0.61 0.036 76);
--app-accent: oklch(0.37 0.045 75);
--app-accent-foreground: oklch(0.87 0.022 84);
--app-destructive: oklch(0.55 0.14 22);
--app-destructive-foreground: oklch(0.96 0.018 95);
--app-border: oklch(0.37 0.032 77);
--app-input: oklch(0.33 0.03 79);
--app-ring: oklch(0.66 0.085 76);
--app-sidebar: oklch(0.195 0.018 83);
--app-sidebar-foreground: oklch(0.87 0.022 84);
--app-sidebar-primary: oklch(0.69 0.07 76);
--app-sidebar-primary-foreground: oklch(0.17 0.02 82);
--app-sidebar-accent: oklch(0.36 0.046 76);
--app-sidebar-accent-foreground: oklch(0.87 0.022 84);
--app-sidebar-border: oklch(0.36 0.034 77);
--app-sidebar-ring: oklch(0.66 0.085 76);
--app-chart-1: oklch(0.72 0.085 74);
--app-chart-2: oklch(0.6 0.065 78);
--app-chart-3: oklch(0.52 0.055 80);
--app-chart-4: oklch(0.44 0.042 77);
--app-chart-5: oklch(0.62 0.06 72);
/* 暗色下阴影偏冷、偏漫射,减轻「描边」感 */
--shadow-sm: 1px 2px 10px -2px hsl(220 14% 4% / 0.55);
}
@theme inline {
--color-background: var(--app-background);
--color-foreground: var(--app-foreground);
--color-card: var(--app-card);
--color-card-foreground: var(--app-card-foreground);
--color-popover: var(--app-popover);
--color-popover-foreground: var(--app-popover-foreground);
--color-primary: var(--app-primary);
--color-primary-foreground: var(--app-primary-foreground);
--color-secondary: var(--app-secondary);
--color-secondary-foreground: var(--app-secondary-foreground);
--color-muted: var(--app-muted);
--color-muted-foreground: var(--app-muted-foreground);
--color-accent: var(--app-accent);
--color-accent-foreground: var(--app-accent-foreground);
--color-destructive: var(--app-destructive);
--color-destructive-foreground: var(--app-destructive-foreground);
--color-border: var(--app-border);
--color-input: var(--app-input);
--color-ring: var(--app-ring);
--color-sidebar: var(--app-sidebar);
--color-sidebar-foreground: var(--app-sidebar-foreground);
--color-sidebar-primary: var(--app-sidebar-primary);
--color-sidebar-primary-foreground: var(--app-sidebar-primary-foreground);
--color-sidebar-accent: var(--app-sidebar-accent);
--color-sidebar-accent-foreground: var(--app-sidebar-accent-foreground);
--color-sidebar-border: var(--app-sidebar-border);
--color-sidebar-ring: var(--app-sidebar-ring);
--color-chart-1: var(--app-chart-1);
--color-chart-2: var(--app-chart-2);
--color-chart-3: var(--app-chart-3);
--color-chart-4: var(--app-chart-4);
--color-chart-5: var(--app-chart-5);
--radius: 0.5rem;
--radius-lg: var(--radius);
--radius-md: calc(var(--radius) - 2px);
--radius-sm: calc(var(--radius) - 4px);
--spacing: 0.25rem;
--font-sans: DM Sans, sans-serif;
--font-serif: Georgia, serif;
--font-mono: Menlo, monospace;
--shadow-sm: 2px 2px 0px 0px hsl(20 18% 51% / 0.4);
--animate-accordion-down: accordion-down 0.2s ease-out;
--animate-accordion-up: accordion-up 0.2s ease-out;
@keyframes accordion-down {
from { height: 0; }
to { height: var(--radix-accordion-content-height); }
}
@keyframes accordion-up {
from { height: var(--radix-accordion-content-height); }
to { height: 0; }
}
}
@layer base {
* {
@apply border-border;
}
/**
* 桌面端放大根字号:所有 rem 类(text-sm、gap、p 等)同步放大;
* 移动端保持 16px 避免拥挤。
*/
html {
font-size: 16px;
-webkit-text-size-adjust: 100%;
text-size-adjust: 100%;
}
@media (min-width: 640px) {
html {
font-size: 16.5px;
}
}
@media (min-width: 1024px) {
html {
font-size: 17.5px;
}
}
@media (min-width: 1280px) {
html {
font-size: 18px;
}
}
body {
@apply bg-background text-foreground antialiased;
font-synthesis: none;
}
}
/* ─── Custom Keyframe Animations ─── */
@keyframes fade-in {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes pulse-ring {
0% { transform: scale(1); opacity: 0.6; }
80%, 100% { transform: scale(2.2); opacity: 0; }
}
@keyframes pulse-dot {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.15); }
}
@keyframes scanline {
0% { transform: translateY(-100vh); }
100% { transform: translateY(100vh); }
}
@keyframes data-flicker {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
}
@keyframes progress {
0% { width: 0%; }
50% { width: 70%; }
100% { width: 100%; }
}
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}
/* ─── Utility Classes ─── */
.bg-grid-pattern {
background-image:
linear-gradient(to right, currentColor 1px, transparent 1px),
linear-gradient(to bottom, currentColor 1px, transparent 1px);
background-size: 40px 40px;
}
.animate-fade-in {
animation: fade-in 0.5s ease-out forwards;
}
.animate-pulse-ring {
animation: pulse-ring 1.5s ease-out infinite;
}
.animate-pulse-dot {
animation: pulse-dot 2s ease-in-out infinite;
}
.animate-scanline {
animation: scanline 8s linear infinite;
}
.animate-data-flicker {
animation: data-flicker 3s ease-in-out infinite;
}
.animate-progress {
animation: progress 3s ease-in-out infinite;
}
.animate-shimmer {
animation: shimmer 2s linear infinite;
}
配色说明
目前AI非常牛逼,完全可以按照自己喜好来构建配色,将src/index.css文件上传给AI,描述你对配色的需求,等待生成随后替换index.css里面的代码即可.
致谢
Tip
感谢以下项目与社区支持
- NodeGet
- NodeGet-StatusShow
- React
- Vite
- Tailwind CSS
- shadcn/ui
- Recharts
- d3-geo
不错,支持 NodeGet 发展
支持
考虑到很多新手在部署公开探针页面遇到困难,开发组已经设计了新的主题安装方案,大概会在3天内支持
基于静态文件托管的方式仍然是推荐方案,另外会补充一个门槛极低的补充方案
BD
之前看到就感觉挺好看,再次看到感觉还是很好看
支持鸡腿
好长,感谢分享
支持
dd大佬
bd