commit 74f15c282e833b19b0b1e6590b20a502a61f9b09
Author: 树萌芽 <3205788256@qq.com>
Date: Mon Feb 16 00:13:37 2026 +0800
first commit
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..259b4db
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,41 @@
+# Node/React
+node_modules/
+build/
+coverage/
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+# Go
+*.exe
+*.test
+*.out
+*.dll
+*.so
+*.dylib
+/tmp/
+
+# 数据文件
+data/data.json
+
+# 日志
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# 操作系统
+.DS_Store
+Thumbs.db
+
+# IDE
+.vscode/
+.idea/
+*.swp
+*.swo
+*~
+
+# 其他
+.env
+dist/
diff --git a/mengyaping-backend/.dockerignore b/mengyaping-backend/.dockerignore
new file mode 100644
index 0000000..058f085
--- /dev/null
+++ b/mengyaping-backend/.dockerignore
@@ -0,0 +1,47 @@
+# Git 相关
+.git
+.gitignore
+.gitattributes
+
+# 编辑器和 IDE
+.vscode
+.idea
+*.swp
+*.swo
+*~
+
+# 操作系统文件
+.DS_Store
+Thumbs.db
+
+# 数据文件(运行时生成)
+data/*.json
+
+# 日志文件
+*.log
+
+# 临时文件
+tmp/
+temp/
+
+# 文档
+README.md
+LICENSE
+*.md
+
+# Docker 相关
+Dockerfile
+.dockerignore
+docker-compose.yml
+
+# 测试文件
+*_test.go
+test/
+tests/
+
+# 构建产物
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
diff --git a/mengyaping-backend/Dockerfile b/mengyaping-backend/Dockerfile
new file mode 100644
index 0000000..45530e2
--- /dev/null
+++ b/mengyaping-backend/Dockerfile
@@ -0,0 +1,56 @@
+# 多阶段构建 - 使用官方 Golang 镜像作为构建环境
+FROM golang:1.25-alpine AS builder
+
+# 设置工作目录
+WORKDIR /app
+
+# 安装必要的构建工具
+RUN apk add --no-cache git ca-certificates tzdata
+
+# 复制 go.mod 和 go.sum 文件
+COPY go.mod go.sum ./
+
+# 下载依赖
+RUN go mod download
+
+# 复制源代码
+COPY . .
+
+# 构建应用程序
+RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -o mengyaping-backend .
+
+# 使用轻量级的 alpine 镜像作为运行环境
+FROM alpine:latest
+
+# 安装必要的运行时依赖
+RUN apk --no-cache add ca-certificates tzdata
+
+# 设置时区为上海
+RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
+ echo "Asia/Shanghai" > /etc/timezone
+
+# 创建非 root 用户
+RUN addgroup -g 1000 appuser && \
+ adduser -D -u 1000 -G appuser appuser
+
+# 设置工作目录
+WORKDIR /app
+
+# 从构建阶段复制编译好的二进制文件
+COPY --from=builder /app/mengyaping-backend .
+
+# 创建数据目录
+RUN mkdir -p /app/data && chown -R appuser:appuser /app
+
+# 切换到非 root 用户
+USER appuser
+
+# 暴露端口
+EXPOSE 8080
+
+# 健康检查
+HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
+ CMD wget --no-verbose --tries=1 --spider http://localhost:8080/api/health || exit 1
+
+# 运行应用程序
+CMD ["./mengyaping-backend"]
diff --git a/mengyaping-backend/config/config.go b/mengyaping-backend/config/config.go
new file mode 100644
index 0000000..b1813b8
--- /dev/null
+++ b/mengyaping-backend/config/config.go
@@ -0,0 +1,176 @@
+package config
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "sync"
+ "time"
+)
+
+// Config 应用配置
+type Config struct {
+ Server ServerConfig `json:"server"`
+ Monitor MonitorConfig `json:"monitor"`
+ DataPath string `json:"data_path"`
+}
+
+// ServerConfig 服务器配置
+type ServerConfig struct {
+ Port string `json:"port"`
+ Host string `json:"host"`
+}
+
+// MonitorConfig 监控配置
+type MonitorConfig struct {
+ Interval time.Duration `json:"interval"` // 检测间隔
+ Timeout time.Duration `json:"timeout"` // 请求超时时间
+ RetryCount int `json:"retry_count"` // 重试次数
+ HistoryDays int `json:"history_days"` // 保留历史天数
+}
+
+var (
+ cfg *Config
+ once sync.Once
+)
+
+// GetConfig 获取配置单例
+func GetConfig() *Config {
+ once.Do(func() {
+ cfg = &Config{
+ Server: ServerConfig{
+ Port: getEnv("SERVER_PORT", "8080"),
+ Host: getEnv("SERVER_HOST", "0.0.0.0"),
+ },
+ Monitor: MonitorConfig{
+ Interval: parseDuration(getEnv("MONITOR_INTERVAL", "5m"), 5*time.Minute),
+ Timeout: parseDuration(getEnv("MONITOR_TIMEOUT", "10s"), 10*time.Second),
+ RetryCount: parseInt(getEnv("MONITOR_RETRY_COUNT", "3"), 3),
+ HistoryDays: parseInt(getEnv("MONITOR_HISTORY_DAYS", "7"), 7),
+ },
+ DataPath: getEnv("DATA_PATH", "./data"),
+ }
+
+ // 尝试从配置文件加载(会覆盖环境变量配置)
+ loadConfigFromFile()
+ })
+ return cfg
+}
+
+// getEnv 获取环境变量,如果不存在则返回默认值
+func getEnv(key, defaultValue string) string {
+ if value := os.Getenv(key); value != "" {
+ return value
+ }
+ return defaultValue
+}
+
+// parseInt 解析整数环境变量
+func parseInt(value string, defaultValue int) int {
+ if value == "" {
+ return defaultValue
+ }
+ var result int
+ if _, err := fmt.Sscanf(value, "%d", &result); err != nil {
+ return defaultValue
+ }
+ return result
+}
+
+// parseDuration 解析时间间隔环境变量
+func parseDuration(value string, defaultValue time.Duration) time.Duration {
+ if value == "" {
+ return defaultValue
+ }
+ if duration, err := time.ParseDuration(value); err == nil {
+ return duration
+ }
+ return defaultValue
+}
+
+// loadConfigFromFile 从文件加载配置
+func loadConfigFromFile() {
+ configFile := "./data/config.json"
+ if _, err := os.Stat(configFile); os.IsNotExist(err) {
+ return
+ }
+
+ data, err := os.ReadFile(configFile)
+ if err != nil {
+ return
+ }
+
+ var fileCfg struct {
+ Server ServerConfig `json:"server"`
+ Monitor struct {
+ IntervalMinutes int `json:"interval_minutes"`
+ TimeoutSeconds int `json:"timeout_seconds"`
+ RetryCount int `json:"retry_count"`
+ HistoryDays int `json:"history_days"`
+ } `json:"monitor"`
+ DataPath string `json:"data_path"`
+ }
+
+ if err := json.Unmarshal(data, &fileCfg); err != nil {
+ return
+ }
+
+ if fileCfg.Server.Port != "" {
+ cfg.Server.Port = fileCfg.Server.Port
+ }
+ if fileCfg.Server.Host != "" {
+ cfg.Server.Host = fileCfg.Server.Host
+ }
+ if fileCfg.Monitor.IntervalMinutes > 0 {
+ cfg.Monitor.Interval = time.Duration(fileCfg.Monitor.IntervalMinutes) * time.Minute
+ }
+ if fileCfg.Monitor.TimeoutSeconds > 0 {
+ cfg.Monitor.Timeout = time.Duration(fileCfg.Monitor.TimeoutSeconds) * time.Second
+ }
+ if fileCfg.Monitor.RetryCount > 0 {
+ cfg.Monitor.RetryCount = fileCfg.Monitor.RetryCount
+ }
+ if fileCfg.Monitor.HistoryDays > 0 {
+ cfg.Monitor.HistoryDays = fileCfg.Monitor.HistoryDays
+ }
+ if fileCfg.DataPath != "" {
+ cfg.DataPath = fileCfg.DataPath
+ }
+}
+
+// SaveConfig 保存配置到文件
+func SaveConfig() error {
+ configFile := cfg.DataPath + "/config.json"
+
+ fileCfg := struct {
+ Server ServerConfig `json:"server"`
+ Monitor struct {
+ IntervalMinutes int `json:"interval_minutes"`
+ TimeoutSeconds int `json:"timeout_seconds"`
+ RetryCount int `json:"retry_count"`
+ HistoryDays int `json:"history_days"`
+ } `json:"monitor"`
+ DataPath string `json:"data_path"`
+ }{
+ Server: cfg.Server,
+ Monitor: struct {
+ IntervalMinutes int `json:"interval_minutes"`
+ TimeoutSeconds int `json:"timeout_seconds"`
+ RetryCount int `json:"retry_count"`
+ HistoryDays int `json:"history_days"`
+ }{
+ IntervalMinutes: int(cfg.Monitor.Interval.Minutes()),
+ TimeoutSeconds: int(cfg.Monitor.Timeout.Seconds()),
+ RetryCount: cfg.Monitor.RetryCount,
+ HistoryDays: cfg.Monitor.HistoryDays,
+ },
+ DataPath: cfg.DataPath,
+ }
+
+ data, err := json.MarshalIndent(fileCfg, "", " ")
+ if err != nil {
+ return err
+ }
+
+ return os.WriteFile(configFile, data, 0644)
+}
diff --git a/mengyaping-backend/data/config.json b/mengyaping-backend/data/config.json
new file mode 100644
index 0000000..b8c9124
--- /dev/null
+++ b/mengyaping-backend/data/config.json
@@ -0,0 +1,13 @@
+{
+ "server": {
+ "port": "8080",
+ "host": "0.0.0.0"
+ },
+ "monitor": {
+ "interval_minutes": 5,
+ "timeout_seconds": 10,
+ "retry_count": 3,
+ "history_days": 7
+ },
+ "data_path": "./data"
+}
diff --git a/mengyaping-backend/data/groups.json b/mengyaping-backend/data/groups.json
new file mode 100644
index 0000000..43108ab
--- /dev/null
+++ b/mengyaping-backend/data/groups.json
@@ -0,0 +1,18 @@
+[
+ {
+ "id": "self-made",
+ "name": "自制网站"
+ },
+ {
+ "id": "self-deploy",
+ "name": "自部署网站"
+ },
+ {
+ "id": "admin",
+ "name": "管理员网站"
+ },
+ {
+ "id": "api",
+ "name": "API网站"
+ }
+]
\ No newline at end of file
diff --git a/mengyaping-backend/data/records.json b/mengyaping-backend/data/records.json
new file mode 100644
index 0000000..dffb764
--- /dev/null
+++ b/mengyaping-backend/data/records.json
@@ -0,0 +1,1283 @@
+{
+ "188cb48c619bd5d896bff546_d19b0a475334": [
+ {
+ "website_id": "188cb48c619bd5d896bff546",
+ "url_id": "d19b0a475334",
+ "url": "https://shumengya.top",
+ "status_code": 200,
+ "latency": 196,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:12.7989645+08:00"
+ },
+ {
+ "website_id": "188cb48c619bd5d896bff546",
+ "url_id": "d19b0a475334",
+ "url": "https://shumengya.top",
+ "status_code": 200,
+ "latency": 106,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:02.9437926+08:00"
+ },
+ {
+ "website_id": "188cb48c619bd5d896bff546",
+ "url_id": "d19b0a475334",
+ "url": "https://shumengya.top",
+ "status_code": 200,
+ "latency": 251,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.0890369+08:00"
+ }
+ ],
+ "188cb49298d72b78a37301a1_9d25ae024f36": [
+ {
+ "website_id": "188cb49298d72b78a37301a1",
+ "url_id": "9d25ae024f36",
+ "url": "https://memos.shumengya.top",
+ "status_code": 200,
+ "latency": 77,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:13.0897872+08:00"
+ },
+ {
+ "website_id": "188cb49298d72b78a37301a1",
+ "url_id": "9d25ae024f36",
+ "url": "https://memos.shumengya.top",
+ "status_code": 200,
+ "latency": 178,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:03.016264+08:00"
+ },
+ {
+ "website_id": "188cb49298d72b78a37301a1",
+ "url_id": "9d25ae024f36",
+ "url": "https://memos.shumengya.top",
+ "status_code": 200,
+ "latency": 151,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.1985861+08:00"
+ }
+ ],
+ "188cb52c04cf639cfc2c8ba4_cfe6229fb86b": [
+ {
+ "website_id": "188cb52c04cf639cfc2c8ba4",
+ "url_id": "cfe6229fb86b",
+ "url": "https://pan.shumengya.top",
+ "status_code": 200,
+ "latency": 143,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:12.6543372+08:00"
+ },
+ {
+ "website_id": "188cb52c04cf639cfc2c8ba4",
+ "url_id": "cfe6229fb86b",
+ "url": "https://pan.shumengya.top",
+ "status_code": 200,
+ "latency": 163,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:03.0007388+08:00"
+ },
+ {
+ "website_id": "188cb52c04cf639cfc2c8ba4",
+ "url_id": "cfe6229fb86b",
+ "url": "https://pan.shumengya.top",
+ "status_code": 200,
+ "latency": 140,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.1985861+08:00"
+ }
+ ],
+ "188cb53623f46b7872bf17b6_b53594e7285d": [
+ {
+ "website_id": "188cb53623f46b7872bf17b6",
+ "url_id": "b53594e7285d",
+ "url": "https://git.shumengya.top",
+ "status_code": 200,
+ "latency": 144,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:12.6543372+08:00"
+ },
+ {
+ "website_id": "188cb53623f46b7872bf17b6",
+ "url_id": "b53594e7285d",
+ "url": "https://git.shumengya.top",
+ "status_code": 200,
+ "latency": 128,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:03.0969391+08:00"
+ },
+ {
+ "website_id": "188cb53623f46b7872bf17b6",
+ "url_id": "b53594e7285d",
+ "url": "https://git.shumengya.top",
+ "status_code": 200,
+ "latency": 164,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.224755+08:00"
+ }
+ ],
+ "188cb54b93ddd1e487bf347e_46d822694242": [
+ {
+ "website_id": "188cb54b93ddd1e487bf347e",
+ "url_id": "46d822694242",
+ "url": "https://image.shumengya.top",
+ "status_code": 200,
+ "latency": 573,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:13.0840707+08:00"
+ },
+ {
+ "website_id": "188cb54b93ddd1e487bf347e",
+ "url_id": "46d822694242",
+ "url": "https://image.shumengya.top",
+ "status_code": 200,
+ "latency": 327,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:03.3249271+08:00"
+ },
+ {
+ "website_id": "188cb54b93ddd1e487bf347e",
+ "url_id": "46d822694242",
+ "url": "https://image.shumengya.top",
+ "status_code": 200,
+ "latency": 338,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.4087069+08:00"
+ }
+ ],
+ "188cb5507dcd2da0559bd6dd_6d5125db9d81": [
+ {
+ "website_id": "188cb5507dcd2da0559bd6dd",
+ "url_id": "6d5125db9d81",
+ "url": "https://note.shumengya.top",
+ "status_code": 200,
+ "latency": 111,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:12.6209949+08:00"
+ },
+ {
+ "website_id": "188cb5507dcd2da0559bd6dd",
+ "url_id": "6d5125db9d81",
+ "url": "https://note.shumengya.top",
+ "status_code": 200,
+ "latency": 132,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:03.1366741+08:00"
+ },
+ {
+ "website_id": "188cb5507dcd2da0559bd6dd",
+ "url_id": "6d5125db9d81",
+ "url": "https://note.shumengya.top",
+ "status_code": 200,
+ "latency": 139,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.224755+08:00"
+ }
+ ],
+ "188cb55693193b80d2383cc3_74a3b6e1f50e": [
+ {
+ "website_id": "188cb55693193b80d2383cc3",
+ "url_id": "74a3b6e1f50e",
+ "url": "https://work.shumengya.top",
+ "status_code": 200,
+ "latency": 122,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:12.6335434+08:00"
+ },
+ {
+ "website_id": "188cb55693193b80d2383cc3",
+ "url_id": "74a3b6e1f50e",
+ "url": "https://work.shumengya.top",
+ "status_code": 200,
+ "latency": 121,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:03.134924+08:00"
+ },
+ {
+ "website_id": "188cb55693193b80d2383cc3",
+ "url_id": "74a3b6e1f50e",
+ "url": "https://work.shumengya.top",
+ "status_code": 200,
+ "latency": 219,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.0569807+08:00"
+ }
+ ],
+ "188cb56d8df57dc8c66d42ee_e8f8fd4cf9b9": [
+ {
+ "website_id": "188cb56d8df57dc8c66d42ee",
+ "url_id": "e8f8fd4cf9b9",
+ "url": "https://infogenie.shumengya.top",
+ "status_code": 200,
+ "latency": 110,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:12.6204639+08:00"
+ },
+ {
+ "website_id": "188cb56d8df57dc8c66d42ee",
+ "url_id": "e8f8fd4cf9b9",
+ "url": "https://infogenie.shumengya.top",
+ "status_code": 200,
+ "latency": 120,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:03.1371847+08:00"
+ },
+ {
+ "website_id": "188cb56d8df57dc8c66d42ee",
+ "url_id": "e8f8fd4cf9b9",
+ "url": "https://infogenie.shumengya.top",
+ "status_code": 200,
+ "latency": 173,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.0111242+08:00"
+ }
+ ],
+ "188cb574b122f71087e53ae1_2f9ffdedd860": [
+ {
+ "website_id": "188cb574b122f71087e53ae1",
+ "url_id": "2f9ffdedd860",
+ "url": "https://survey.shumengya.top",
+ "status_code": 502,
+ "latency": 91,
+ "is_up": false,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:12.6023209+08:00"
+ },
+ {
+ "website_id": "188cb574b122f71087e53ae1",
+ "url_id": "2f9ffdedd860",
+ "url": "https://survey.shumengya.top",
+ "status_code": 502,
+ "latency": 166,
+ "is_up": false,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:03.0039014+08:00"
+ },
+ {
+ "website_id": "188cb574b122f71087e53ae1",
+ "url_id": "2f9ffdedd860",
+ "url": "https://survey.shumengya.top",
+ "status_code": 502,
+ "latency": 113,
+ "is_up": false,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:02.9509477+08:00"
+ }
+ ],
+ "188cb5a19b836e80d93df86a_8a4ebcb7eb03": [
+ {
+ "website_id": "188cb5a19b836e80d93df86a",
+ "url_id": "8a4ebcb7eb03",
+ "url": "https://short.shumengya.top",
+ "status_code": 200,
+ "latency": 104,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:12.6146105+08:00"
+ },
+ {
+ "website_id": "188cb5a19b836e80d93df86a",
+ "url_id": "8a4ebcb7eb03",
+ "url": "https://short.shumengya.top",
+ "status_code": 200,
+ "latency": 123,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:03.1240818+08:00"
+ },
+ {
+ "website_id": "188cb5a19b836e80d93df86a",
+ "url_id": "8a4ebcb7eb03",
+ "url": "https://short.shumengya.top",
+ "status_code": 200,
+ "latency": 129,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.3726594+08:00"
+ }
+ ],
+ "188cb6da20586d34df88b086_e7e250eb8985": [
+ {
+ "website_id": "188cb6da20586d34df88b086",
+ "url_id": "e7e250eb8985",
+ "url": "https://bottle.shumengya.top",
+ "status_code": 200,
+ "latency": 106,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:12.6161729+08:00"
+ },
+ {
+ "website_id": "188cb6da20586d34df88b086",
+ "url_id": "e7e250eb8985",
+ "url": "https://bottle.shumengya.top",
+ "status_code": 200,
+ "latency": 92,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:03.2167462+08:00"
+ },
+ {
+ "website_id": "188cb6da20586d34df88b086",
+ "url_id": "e7e250eb8985",
+ "url": "https://bottle.shumengya.top",
+ "status_code": 200,
+ "latency": 137,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.3944633+08:00"
+ }
+ ],
+ "188cb6ff716e2590408704ee_7bf1e5ddd890": null,
+ "188cb6ff716e2590408704ee_85620784f1a3": [
+ {
+ "website_id": "188cb6ff716e2590408704ee",
+ "url_id": "85620784f1a3",
+ "url": "https://reference.smyhub.com",
+ "status_code": 200,
+ "latency": 1019,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:13.8927765+08:00"
+ },
+ {
+ "website_id": "188cb6ff716e2590408704ee",
+ "url_id": "85620784f1a3",
+ "url": "https://reference.smyhub.com",
+ "status_code": 200,
+ "latency": 724,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:03.8312477+08:00"
+ },
+ {
+ "website_id": "188cb6ff716e2590408704ee",
+ "url_id": "85620784f1a3",
+ "url": "https://reference.smyhub.com",
+ "status_code": 200,
+ "latency": 864,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:04.3622007+08:00"
+ }
+ ],
+ "188cb708e8cf52a49a436570_4d9ec8c9ed04": null,
+ "188cb708e8cf52a49a436570_dcc07e30a25a": [
+ {
+ "website_id": "188cb708e8cf52a49a436570",
+ "url_id": "dcc07e30a25a",
+ "url": "https://mind-map.smyhub.com",
+ "status_code": 200,
+ "latency": 997,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:13.6176535+08:00"
+ },
+ {
+ "website_id": "188cb708e8cf52a49a436570",
+ "url_id": "dcc07e30a25a",
+ "url": "https://mind-map.smyhub.com",
+ "status_code": 200,
+ "latency": 700,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:03.8368113+08:00"
+ },
+ {
+ "website_id": "188cb708e8cf52a49a436570",
+ "url_id": "dcc07e30a25a",
+ "url": "https://mind-map.smyhub.com",
+ "status_code": 200,
+ "latency": 246,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.5327063+08:00"
+ }
+ ],
+ "188cb71029ad43105a3dc6c0_3e0bf25fb3ae": null,
+ "188cb71029ad43105a3dc6c0_9148d3eb9835": null,
+ "188cb71029ad43105a3dc6c0_f278e65dd498": [
+ {
+ "website_id": "188cb71029ad43105a3dc6c0",
+ "url_id": "f278e65dd498",
+ "url": "https://it-tools.smyhub.com",
+ "status_code": 200,
+ "latency": 999,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:13.6211344+08:00"
+ },
+ {
+ "website_id": "188cb71029ad43105a3dc6c0",
+ "url_id": "f278e65dd498",
+ "url": "https://it-tools.smyhub.com",
+ "status_code": 200,
+ "latency": 809,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:03.6479761+08:00"
+ },
+ {
+ "website_id": "188cb71029ad43105a3dc6c0",
+ "url_id": "f278e65dd498",
+ "url": "https://it-tools.smyhub.com",
+ "status_code": 200,
+ "latency": 268,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.6172387+08:00"
+ }
+ ],
+ "188cb71029ad43105a3dc6c0_f5e2bfc4a2e5": null,
+ "188cb71b527c48bc3c344acc_13cdc2afab17": [
+ {
+ "website_id": "188cb71b527c48bc3c344acc",
+ "url_id": "13cdc2afab17",
+ "url": "https://xtools.smyhub.com",
+ "status_code": 200,
+ "latency": 1099,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:14.0180484+08:00"
+ },
+ {
+ "website_id": "188cb71b527c48bc3c344acc",
+ "url_id": "13cdc2afab17",
+ "url": "https://xtools.smyhub.com",
+ "status_code": 200,
+ "latency": 719,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:03.5614426+08:00"
+ },
+ {
+ "website_id": "188cb71b527c48bc3c344acc",
+ "url_id": "13cdc2afab17",
+ "url": "https://xtools.smyhub.com",
+ "status_code": 200,
+ "latency": 846,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:04.2212112+08:00"
+ }
+ ],
+ "188cb71b527c48bc3c344acc_377c1524ef52": null,
+ "188cb72555e8d5c4f0d7aa98_421fe08ece5e": [
+ {
+ "website_id": "188cb72555e8d5c4f0d7aa98",
+ "url_id": "421fe08ece5e",
+ "url": "http://monitor.shumengya.top",
+ "status_code": 200,
+ "latency": 62,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:12.7169478+08:00"
+ },
+ {
+ "website_id": "188cb72555e8d5c4f0d7aa98",
+ "url_id": "421fe08ece5e",
+ "url": "http://monitor.shumengya.top",
+ "status_code": 200,
+ "latency": 129,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:02.9668792+08:00"
+ },
+ {
+ "website_id": "188cb72555e8d5c4f0d7aa98",
+ "url_id": "421fe08ece5e",
+ "url": "http://monitor.shumengya.top",
+ "status_code": 200,
+ "latency": 132,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:02.9700179+08:00"
+ }
+ ],
+ "188cb73272be1c9c5a5d6812_cba98c3c329b": [
+ {
+ "website_id": "188cb73272be1c9c5a5d6812",
+ "url_id": "cba98c3c329b",
+ "url": "https://office.shumengya.top",
+ "status_code": 200,
+ "latency": 111,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:12.7674442+08:00"
+ },
+ {
+ "website_id": "188cb73272be1c9c5a5d6812",
+ "url_id": "cba98c3c329b",
+ "url": "https://office.shumengya.top",
+ "status_code": 200,
+ "latency": 159,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:02.9970677+08:00"
+ },
+ {
+ "website_id": "188cb73272be1c9c5a5d6812",
+ "url_id": "cba98c3c329b",
+ "url": "https://office.shumengya.top",
+ "status_code": 200,
+ "latency": 122,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.346952+08:00"
+ }
+ ],
+ "188cb7372655f3841e82a210_299e19afc16d": null,
+ "188cb7372655f3841e82a210_ed4c7dcd0565": [
+ {
+ "website_id": "188cb7372655f3841e82a210",
+ "url_id": "ed4c7dcd0565",
+ "url": "https://cube.smyhub.com",
+ "status_code": 200,
+ "latency": 951,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:13.6575436+08:00"
+ },
+ {
+ "website_id": "188cb7372655f3841e82a210",
+ "url_id": "ed4c7dcd0565",
+ "url": "https://cube.smyhub.com",
+ "status_code": 200,
+ "latency": 817,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:03.7617882+08:00"
+ },
+ {
+ "website_id": "188cb7372655f3841e82a210",
+ "url_id": "ed4c7dcd0565",
+ "url": "https://cube.smyhub.com",
+ "status_code": 200,
+ "latency": 273,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.473403+08:00"
+ }
+ ],
+ "188cb764a98dbb2453c14b8f_2f0596587997": [
+ {
+ "website_id": "188cb764a98dbb2453c14b8f",
+ "url_id": "2f0596587997",
+ "url": "https://60s.api.shumengya.top",
+ "status_code": 200,
+ "latency": 79,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:12.7997429+08:00"
+ },
+ {
+ "website_id": "188cb764a98dbb2453c14b8f",
+ "url_id": "2f0596587997",
+ "url": "https://60s.api.shumengya.top",
+ "status_code": 200,
+ "latency": 175,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:03.0130356+08:00"
+ },
+ {
+ "website_id": "188cb764a98dbb2453c14b8f",
+ "url_id": "2f0596587997",
+ "url": "https://60s.api.shumengya.top",
+ "status_code": 200,
+ "latency": 189,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.0274559+08:00"
+ }
+ ],
+ "188cb77f231fe68c73e24b29_c182709178d9": [
+ {
+ "website_id": "188cb77f231fe68c73e24b29",
+ "url_id": "c182709178d9",
+ "url": "https://nav.api.shumengya.top",
+ "status_code": 200,
+ "latency": 94,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:12.8635813+08:00"
+ },
+ {
+ "website_id": "188cb77f231fe68c73e24b29",
+ "url_id": "c182709178d9",
+ "url": "https://nav.api.shumengya.top",
+ "status_code": 200,
+ "latency": 133,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:03.2708032+08:00"
+ },
+ {
+ "website_id": "188cb77f231fe68c73e24b29",
+ "url_id": "c182709178d9",
+ "url": "https://nav.api.shumengya.top",
+ "status_code": 200,
+ "latency": 159,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.2568063+08:00"
+ }
+ ],
+ "188cb789a76896788f139a96_13bbbc879aa6": null,
+ "188cb789a76896788f139a96_2b42ed612dd3": [
+ {
+ "website_id": "188cb789a76896788f139a96",
+ "url_id": "2b42ed612dd3",
+ "url": "https://note.api.shumengya.top/api/tree",
+ "status_code": 200,
+ "latency": 135,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:12.937508+08:00"
+ },
+ {
+ "website_id": "188cb789a76896788f139a96",
+ "url_id": "2b42ed612dd3",
+ "url": "https://note.api.shumengya.top/api/tree",
+ "status_code": 200,
+ "latency": 144,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:03.3127824+08:00"
+ },
+ {
+ "website_id": "188cb789a76896788f139a96",
+ "url_id": "2b42ed612dd3",
+ "url": "https://note.api.shumengya.top/api/tree",
+ "status_code": 200,
+ "latency": 195,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.2860143+08:00"
+ }
+ ],
+ "188cb7934d5d413890dea61f_bebd3848b644": null,
+ "188cb7934d5d413890dea61f_d841646c7b41": [
+ {
+ "website_id": "188cb7934d5d413890dea61f",
+ "url_id": "d841646c7b41",
+ "url": "https://work.api.shumengya.top",
+ "status_code": 200,
+ "latency": 81,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:12.8822235+08:00"
+ },
+ {
+ "website_id": "188cb7934d5d413890dea61f",
+ "url_id": "d841646c7b41",
+ "url": "https://work.api.shumengya.top",
+ "status_code": 200,
+ "latency": 104,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:03.321074+08:00"
+ },
+ {
+ "website_id": "188cb7934d5d413890dea61f",
+ "url_id": "d841646c7b41",
+ "url": "https://work.api.shumengya.top",
+ "status_code": 200,
+ "latency": 112,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.3980929+08:00"
+ }
+ ],
+ "188cb79ed2aeb85cabcf6e66_7c8ff0e48be4": [
+ {
+ "website_id": "188cb79ed2aeb85cabcf6e66",
+ "url_id": "7c8ff0e48be4",
+ "url": "https://infogenie.api.shumengya.top",
+ "status_code": 200,
+ "latency": 79,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:12.943246+08:00"
+ },
+ {
+ "website_id": "188cb79ed2aeb85cabcf6e66",
+ "url_id": "7c8ff0e48be4",
+ "url": "https://infogenie.api.shumengya.top",
+ "status_code": 200,
+ "latency": 109,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:03.3802576+08:00"
+ },
+ {
+ "website_id": "188cb79ed2aeb85cabcf6e66",
+ "url_id": "7c8ff0e48be4",
+ "url": "https://infogenie.api.shumengya.top",
+ "status_code": 200,
+ "latency": 208,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.0453886+08:00"
+ }
+ ],
+ "188cb7b7b154ddbc1dc4b14d_ef0443476dd8": [
+ {
+ "website_id": "188cb7b7b154ddbc1dc4b14d",
+ "url_id": "ef0443476dd8",
+ "url": "https://short.api.shumengya.top",
+ "status_code": 200,
+ "latency": 77,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:12.9669041+08:00"
+ },
+ {
+ "website_id": "188cb7b7b154ddbc1dc4b14d",
+ "url_id": "ef0443476dd8",
+ "url": "https://short.api.shumengya.top",
+ "status_code": 200,
+ "latency": 121,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:03.4355819+08:00"
+ },
+ {
+ "website_id": "188cb7b7b154ddbc1dc4b14d",
+ "url_id": "ef0443476dd8",
+ "url": "https://short.api.shumengya.top",
+ "status_code": 200,
+ "latency": 209,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.0850094+08:00"
+ }
+ ],
+ "188cb7c8586dd2c4dcf4bd1e_432740a25595": [
+ {
+ "website_id": "188cb7c8586dd2c4dcf4bd1e",
+ "url_id": "432740a25595",
+ "url": "https://bottle.api.shumengya.top",
+ "status_code": 200,
+ "latency": 90,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:12.705286+08:00"
+ },
+ {
+ "website_id": "188cb7c8586dd2c4dcf4bd1e",
+ "url_id": "432740a25595",
+ "url": "https://bottle.api.shumengya.top",
+ "status_code": 200,
+ "latency": 120,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:03.4418066+08:00"
+ },
+ {
+ "website_id": "188cb7c8586dd2c4dcf4bd1e",
+ "url_id": "432740a25595",
+ "url": "https://bottle.api.shumengya.top",
+ "status_code": 200,
+ "latency": 153,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.1058424+08:00"
+ }
+ ],
+ "188cf6ed769b75e497cd2a4a_23ba1b4ea7db": [
+ {
+ "website_id": "188cf6ed769b75e497cd2a4a",
+ "url_id": "23ba1b4ea7db",
+ "url": "https://1pl.big.shumengya.top",
+ "status_code": 0,
+ "latency": 21,
+ "is_up": false,
+ "error": "Get \"https://1pl.big.shumengya.top\": dial tcp: lookup 1pl.big.shumengya.top: no such host",
+ "checked_at": "2026-02-10T01:28:12.9687404+08:00"
+ },
+ {
+ "website_id": "188cf6ed769b75e497cd2a4a",
+ "url_id": "23ba1b4ea7db",
+ "url": "https://1pl.big.shumengya.top",
+ "status_code": 0,
+ "latency": 36,
+ "is_up": false,
+ "error": "Get \"https://1pl.big.shumengya.top\": dial tcp: lookup 1pl.big.shumengya.top: no such host",
+ "checked_at": "2026-02-10T01:31:03.3613566+08:00"
+ }
+ ],
+ "188cf6ed769b75e497cd2a4a_a3e3ff7839f1": [
+ {
+ "website_id": "188cf6ed769b75e497cd2a4a",
+ "url_id": "a3e3ff7839f1",
+ "url": "http://10.0.0.233:19132/smy",
+ "status_code": 200,
+ "latency": 55,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:33:12.8434302+08:00"
+ },
+ {
+ "website_id": "188cf6ed769b75e497cd2a4a",
+ "url_id": "a3e3ff7839f1",
+ "url": "http://10.0.0.233:19132/smy",
+ "status_code": 200,
+ "latency": 33,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.0037109+08:00"
+ }
+ ],
+ "188cf6f322f76b90c35c3484_397f5ededede": [
+ {
+ "website_id": "188cf6f322f76b90c35c3484",
+ "url_id": "397f5ededede",
+ "url": "https://1pl.small.shumengya.top",
+ "status_code": 0,
+ "latency": 35,
+ "is_up": false,
+ "error": "Get \"https://1pl.small.shumengya.top\": dial tcp: lookup 1pl.small.shumengya.top: no such host",
+ "checked_at": "2026-02-10T01:28:13.0022756+08:00"
+ },
+ {
+ "website_id": "188cf6f322f76b90c35c3484",
+ "url_id": "397f5ededede",
+ "url": "https://1pl.small.shumengya.top",
+ "status_code": 0,
+ "latency": 37,
+ "is_up": false,
+ "error": "Get \"https://1pl.small.shumengya.top\": dial tcp: lookup 1pl.small.shumengya.top: no such host",
+ "checked_at": "2026-02-10T01:31:03.3992891+08:00"
+ }
+ ],
+ "188cf6f322f76b90c35c3484_76b6f9fe4f04": [
+ {
+ "website_id": "188cf6f322f76b90c35c3484",
+ "url_id": "76b6f9fe4f04",
+ "url": "http://10.0.0.100:19132/smy",
+ "status_code": 200,
+ "latency": 61,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:33:17.4968298+08:00"
+ },
+ {
+ "website_id": "188cf6f322f76b90c35c3484",
+ "url_id": "76b6f9fe4f04",
+ "url": "http://10.0.0.100:19132/smy",
+ "status_code": 200,
+ "latency": 31,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.0124331+08:00"
+ }
+ ],
+ "188cf7019e56e618827f4927_56250450a6bf": [
+ {
+ "website_id": "188cf7019e56e618827f4927",
+ "url_id": "56250450a6bf",
+ "url": "https://portaintor.big.shumengya.top",
+ "status_code": 0,
+ "latency": 33,
+ "is_up": false,
+ "error": "Get \"https://portaintor.big.shumengya.top\": dial tcp: lookup portaintor.big.shumengya.top: no such host",
+ "checked_at": "2026-02-10T01:28:13.0014618+08:00"
+ },
+ {
+ "website_id": "188cf7019e56e618827f4927",
+ "url_id": "56250450a6bf",
+ "url": "https://portaintor.big.shumengya.top",
+ "status_code": 0,
+ "latency": 21,
+ "is_up": false,
+ "error": "Get \"https://portaintor.big.shumengya.top\": dial tcp: lookup portaintor.big.shumengya.top: no such host",
+ "checked_at": "2026-02-10T01:31:03.4026988+08:00"
+ },
+ {
+ "website_id": "188cf7019e56e618827f4927",
+ "url_id": "56250450a6bf",
+ "url": "https://portaintor.big.shumengya.top",
+ "status_code": 0,
+ "latency": 55,
+ "is_up": false,
+ "error": "Get \"https://portaintor.big.shumengya.top\": dial tcp: lookup portaintor.big.shumengya.top: no such host",
+ "checked_at": "2026-02-10T01:36:03.0595103+08:00"
+ }
+ ],
+ "188cf70700922fa4041f6470_791643dbecab": [
+ {
+ "website_id": "188cf70700922fa4041f6470",
+ "url_id": "791643dbecab",
+ "url": "https://portaintor.small.shumengya.top",
+ "status_code": 0,
+ "latency": 40,
+ "is_up": false,
+ "error": "Get \"https://portaintor.small.shumengya.top\": dial tcp: lookup portaintor.small.shumengya.top: no such host",
+ "checked_at": "2026-02-10T01:28:13.0093292+08:00"
+ },
+ {
+ "website_id": "188cf70700922fa4041f6470",
+ "url_id": "791643dbecab",
+ "url": "https://portaintor.small.shumengya.top",
+ "status_code": 0,
+ "latency": 30,
+ "is_up": false,
+ "error": "Get \"https://portaintor.small.shumengya.top\": dial tcp: lookup portaintor.small.shumengya.top: no such host",
+ "checked_at": "2026-02-10T01:31:03.1674975+08:00"
+ },
+ {
+ "website_id": "188cf70700922fa4041f6470",
+ "url_id": "791643dbecab",
+ "url": "https://portaintor.small.shumengya.top",
+ "status_code": 0,
+ "latency": 58,
+ "is_up": false,
+ "error": "Get \"https://portaintor.small.shumengya.top\": dial tcp: lookup portaintor.small.shumengya.top: no such host",
+ "checked_at": "2026-02-10T01:36:03.0706921+08:00"
+ }
+ ],
+ "188cf728ed2992f06a8f9e52_0a0a59069228": [
+ {
+ "website_id": "188cf728ed2992f06a8f9e52",
+ "url_id": "0a0a59069228",
+ "url": "http://easytier.shumengya.top",
+ "status_code": 200,
+ "latency": 87,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:13.0903479+08:00"
+ },
+ {
+ "website_id": "188cf728ed2992f06a8f9e52",
+ "url_id": "0a0a59069228",
+ "url": "http://easytier.shumengya.top",
+ "status_code": 200,
+ "latency": 73,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:03.5093433+08:00"
+ },
+ {
+ "website_id": "188cf728ed2992f06a8f9e52",
+ "url_id": "0a0a59069228",
+ "url": "http://easytier.shumengya.top",
+ "status_code": 200,
+ "latency": 97,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.1100153+08:00"
+ }
+ ],
+ "188cf72edea71c4cb2284c93_170fab2239ad": [
+ {
+ "website_id": "188cf72edea71c4cb2284c93",
+ "url_id": "170fab2239ad",
+ "url": "https://wireguard.shumengya.top",
+ "status_code": 200,
+ "latency": 86,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:13.089219+08:00"
+ },
+ {
+ "website_id": "188cf72edea71c4cb2284c93",
+ "url_id": "170fab2239ad",
+ "url": "https://wireguard.shumengya.top",
+ "status_code": 200,
+ "latency": 116,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:03.5156692+08:00"
+ },
+ {
+ "website_id": "188cf72edea71c4cb2284c93",
+ "url_id": "170fab2239ad",
+ "url": "https://wireguard.shumengya.top",
+ "status_code": 200,
+ "latency": 138,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.1667204+08:00"
+ }
+ ],
+ "188cf746b16a83dc4d5e3d13_822329e621c2": [
+ {
+ "website_id": "188cf746b16a83dc4d5e3d13",
+ "url_id": "822329e621c2",
+ "url": "https://repo.docker.shumengya.top",
+ "status_code": 0,
+ "latency": 28,
+ "is_up": false,
+ "error": "Get \"https://repo.docker.shumengya.top\": dial tcp: lookup repo.docker.shumengya.top: no such host",
+ "checked_at": "2026-02-10T01:28:12.9669041+08:00"
+ },
+ {
+ "website_id": "188cf746b16a83dc4d5e3d13",
+ "url_id": "822329e621c2",
+ "url": "https://repo.docker.shumengya.top",
+ "status_code": 0,
+ "latency": 30,
+ "is_up": false,
+ "error": "Get \"https://repo.docker.shumengya.top\": dial tcp: lookup repo.docker.shumengya.top: no such host",
+ "checked_at": "2026-02-10T01:31:03.4727583+08:00"
+ },
+ {
+ "website_id": "188cf746b16a83dc4d5e3d13",
+ "url_id": "822329e621c2",
+ "url": "https://repo.docker.shumengya.top",
+ "status_code": 0,
+ "latency": 38,
+ "is_up": false,
+ "error": "Get \"https://repo.docker.shumengya.top\": dial tcp: lookup repo.docker.shumengya.top: no such host",
+ "checked_at": "2026-02-10T01:36:02.8751863+08:00"
+ }
+ ],
+ "188cf74b2fa5da7c8e2b588e_2f580f90f979": [
+ {
+ "website_id": "188cf74b2fa5da7c8e2b588e",
+ "url_id": "2f580f90f979",
+ "url": "https://notice.shumengya.top",
+ "status_code": 200,
+ "latency": 81,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:13.1703076+08:00"
+ },
+ {
+ "website_id": "188cf74b2fa5da7c8e2b588e",
+ "url_id": "2f580f90f979",
+ "url": "https://notice.shumengya.top",
+ "status_code": 200,
+ "latency": 113,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:03.5858702+08:00"
+ },
+ {
+ "website_id": "188cf74b2fa5da7c8e2b588e",
+ "url_id": "2f580f90f979",
+ "url": "https://notice.shumengya.top",
+ "status_code": 200,
+ "latency": 142,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:02.9791497+08:00"
+ }
+ ],
+ "188d00db054cf7860dff3440_ac85b166ccc4": [
+ {
+ "website_id": "188d00db054cf7860dff3440",
+ "url_id": "ac85b166ccc4",
+ "url": "https://ping.shumengya.top",
+ "status_code": 200,
+ "latency": 75,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:13.1669356+08:00"
+ },
+ {
+ "website_id": "188d00db054cf7860dff3440",
+ "url_id": "ac85b166ccc4",
+ "url": "https://ping.shumengya.top",
+ "status_code": 200,
+ "latency": 110,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:03.51355+08:00"
+ },
+ {
+ "website_id": "188d00db054cf7860dff3440",
+ "url_id": "ac85b166ccc4",
+ "url": "https://ping.shumengya.top",
+ "status_code": 200,
+ "latency": 130,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.241444+08:00"
+ }
+ ],
+ "188d00edf571226f42ec8b20_0bf0fe1bb9cc": [
+ {
+ "website_id": "188d00edf571226f42ec8b20",
+ "url_id": "0bf0fe1bb9cc",
+ "url": "https://ping.api.shumengya.top",
+ "status_code": 404,
+ "latency": 75,
+ "is_up": false,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:13.1669356+08:00"
+ },
+ {
+ "website_id": "188d00edf571226f42ec8b20",
+ "url_id": "0bf0fe1bb9cc",
+ "url": "https://ping.api.shumengya.top",
+ "status_code": 404,
+ "latency": 92,
+ "is_up": false,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:03.6069277+08:00"
+ },
+ {
+ "website_id": "188d00edf571226f42ec8b20",
+ "url_id": "0bf0fe1bb9cc",
+ "url": "https://ping.api.shumengya.top",
+ "status_code": 404,
+ "latency": 117,
+ "is_up": false,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.2845602+08:00"
+ }
+ ],
+ "188e7f1a40cdc7bc3bba1817_9af024b3d387": [
+ {
+ "website_id": "188e7f1a40cdc7bc3bba1817",
+ "url_id": "9af024b3d387",
+ "url": "https://cf-dns.smyhub.com/",
+ "status_code": 200,
+ "latency": 2164,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:15.2494284+08:00"
+ },
+ {
+ "website_id": "188e7f1a40cdc7bc3bba1817",
+ "url_id": "9af024b3d387",
+ "url": "https://cf-dns.smyhub.com/",
+ "status_code": 200,
+ "latency": 733,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:04.2499308+08:00"
+ },
+ {
+ "website_id": "188e7f1a40cdc7bc3bba1817",
+ "url_id": "9af024b3d387",
+ "url": "https://cf-dns.smyhub.com/",
+ "status_code": 200,
+ "latency": 245,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.4477372+08:00"
+ }
+ ],
+ "188e7f27a909fe247a2fcd15_b897ef0def67": [
+ {
+ "website_id": "188e7f27a909fe247a2fcd15",
+ "url_id": "b897ef0def67",
+ "url": "https://gh-proxy.smyhub.com/",
+ "status_code": 200,
+ "latency": 1961,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:15.1313421+08:00"
+ },
+ {
+ "website_id": "188e7f27a909fe247a2fcd15",
+ "url_id": "b897ef0def67",
+ "url": "https://gh-proxy.smyhub.com/",
+ "status_code": 200,
+ "latency": 851,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:04.4132184+08:00"
+ },
+ {
+ "website_id": "188e7f27a909fe247a2fcd15",
+ "url_id": "b897ef0def67",
+ "url": "https://gh-proxy.smyhub.com/",
+ "status_code": 200,
+ "latency": 310,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.4170214+08:00"
+ }
+ ],
+ "188e7f33f087667c29da05f2_75a0cb223c2d": [
+ {
+ "website_id": "188e7f33f087667c29da05f2",
+ "url_id": "75a0cb223c2d",
+ "url": "https://floppy-bird.smyhub.com/",
+ "status_code": 200,
+ "latency": 719,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:13.8866679+08:00"
+ },
+ {
+ "website_id": "188e7f33f087667c29da05f2",
+ "url_id": "75a0cb223c2d",
+ "url": "https://floppy-bird.smyhub.com/",
+ "status_code": 200,
+ "latency": 657,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:04.4717261+08:00"
+ },
+ {
+ "website_id": "188e7f33f087667c29da05f2",
+ "url_id": "75a0cb223c2d",
+ "url": "https://floppy-bird.smyhub.com/",
+ "status_code": 200,
+ "latency": 252,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.6474294+08:00"
+ }
+ ],
+ "188e7f4cdd13a3bc80c68184_9f11daaedba4": [
+ {
+ "website_id": "188e7f4cdd13a3bc80c68184",
+ "url_id": "9f11daaedba4",
+ "url": "https://floppy-bird.smyhub.com/",
+ "status_code": 200,
+ "latency": 1347,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:28:13.8578324+08:00"
+ },
+ {
+ "website_id": "188e7f4cdd13a3bc80c68184",
+ "url_id": "9f11daaedba4",
+ "url": "https://floppy-bird.smyhub.com/",
+ "status_code": 200,
+ "latency": 1079,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:31:03.9167731+08:00"
+ },
+ {
+ "website_id": "188e7f4cdd13a3bc80c68184",
+ "url_id": "9f11daaedba4",
+ "url": "https://floppy-bird.smyhub.com/",
+ "status_code": 200,
+ "latency": 259,
+ "is_up": true,
+ "error": "",
+ "checked_at": "2026-02-10T01:36:03.0972084+08:00"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/mengyaping-backend/data/websites.json b/mengyaping-backend/data/websites.json
new file mode 100644
index 0000000..3ab7fc6
--- /dev/null
+++ b/mengyaping-backend/data/websites.json
@@ -0,0 +1,626 @@
+[
+ {
+ "id": "188cb48c619bd5d896bff546",
+ "name": "萌芽主页",
+ "group": "self-made",
+ "urls": [
+ {
+ "id": "d19b0a475334",
+ "url": "https://shumengya.top",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://shumengya.top/favicon.ico",
+ "title": "萌芽主页",
+ "created_at": "2026-01-21T17:21:08.6757862+08:00",
+ "updated_at": "2026-01-27T13:02:31.4021667+08:00"
+ },
+ {
+ "id": "188cb49298d72b78a37301a1",
+ "name": "萌芽备忘录",
+ "group": "self-deploy",
+ "urls": [
+ {
+ "id": "9d25ae024f36",
+ "url": "https://memos.shumengya.top",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://memos.shumengya.top/logo.webp",
+ "title": "Memos",
+ "created_at": "2026-01-21T17:21:35.3722254+08:00",
+ "updated_at": "2026-01-27T13:02:37.9135994+08:00"
+ },
+ {
+ "id": "188cb52c04cf639cfc2c8ba4",
+ "name": "萌芽盘",
+ "group": "self-deploy",
+ "urls": [
+ {
+ "id": "cfe6229fb86b",
+ "url": "https://pan.shumengya.top",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://img.shumengya.top/i/2026/01/04/695a660870959.png",
+ "title": "萌芽盘",
+ "created_at": "2026-01-21T17:32:34.3136511+08:00",
+ "updated_at": "2026-01-27T13:02:42.907207+08:00"
+ },
+ {
+ "id": "188cb53623f46b7872bf17b6",
+ "name": "萌芽Git仓库",
+ "group": "self-deploy",
+ "urls": [
+ {
+ "id": "b53594e7285d",
+ "url": "https://git.shumengya.top",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://git.shumengya.top/assets/img/favicon.svg",
+ "title": "萌芽Git仓库",
+ "created_at": "2026-01-21T17:33:17.7858446+08:00",
+ "updated_at": "2026-01-27T13:03:13.0909987+08:00"
+ },
+ {
+ "id": "188cb54b93ddd1e487bf347e",
+ "name": "萌芽图床",
+ "group": "self-deploy",
+ "urls": [
+ {
+ "id": "46d822694242",
+ "url": "https://image.shumengya.top",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://image.shumengya.top/favicon.ico",
+ "title": "萌芽图床",
+ "created_at": "2026-01-21T17:34:49.8577249+08:00",
+ "updated_at": "2026-01-27T13:03:19.4761768+08:00"
+ },
+ {
+ "id": "188cb5507dcd2da0559bd6dd",
+ "name": "萌芽笔记",
+ "group": "self-made",
+ "urls": [
+ {
+ "id": "6d5125db9d81",
+ "url": "https://note.shumengya.top",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://note.shumengya.top/logo.png",
+ "title": "萌芽笔记",
+ "created_at": "2026-01-21T17:35:10.962372+08:00",
+ "updated_at": "2026-01-27T13:03:25.8837656+08:00"
+ },
+ {
+ "id": "188cb55693193b80d2383cc3",
+ "name": "萌芽作品集",
+ "group": "self-made",
+ "urls": [
+ {
+ "id": "74a3b6e1f50e",
+ "url": "https://work.shumengya.top",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://work.shumengya.top/favicon.ico",
+ "title": "502 Bad Gateway",
+ "created_at": "2026-01-21T17:35:37.0894816+08:00",
+ "updated_at": "2026-01-27T13:03:30.0659765+08:00"
+ },
+ {
+ "id": "188cb56d8df57dc8c66d42ee",
+ "name": "万象口袋",
+ "group": "self-made",
+ "urls": [
+ {
+ "id": "e8f8fd4cf9b9",
+ "url": "https://infogenie.shumengya.top",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://infogenie.shumengya.top/assets/logo.png",
+ "title": "万象口袋",
+ "created_at": "2026-01-21T17:37:15.787501+08:00",
+ "updated_at": "2026-01-27T13:03:34.8680468+08:00"
+ },
+ {
+ "id": "188cb574b122f71087e53ae1",
+ "name": "萌芽问卷",
+ "group": "self-deploy",
+ "urls": [
+ {
+ "id": "2f9ffdedd860",
+ "url": "https://survey.shumengya.top",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://survey.shumengya.top/favicon.ico",
+ "title": "502 Bad Gateway",
+ "created_at": "2026-01-21T17:37:46.4424548+08:00",
+ "updated_at": "2026-01-27T13:03:38.9707881+08:00"
+ },
+ {
+ "id": "188cb5a19b836e80d93df86a",
+ "name": "萌芽短链",
+ "group": "self-made",
+ "urls": [
+ {
+ "id": "8a4ebcb7eb03",
+ "url": "https://short.shumengya.top",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://short.shumengya.top/logo.png",
+ "title": "萌芽短链",
+ "created_at": "2026-01-21T17:40:59.3532064+08:00",
+ "updated_at": "2026-01-27T13:04:16.9980537+08:00"
+ },
+ {
+ "id": "188cb6da20586d34df88b086",
+ "name": "萌芽漂流瓶",
+ "group": "self-made",
+ "urls": [
+ {
+ "id": "e7e250eb8985",
+ "url": "https://bottle.shumengya.top",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://bottle.shumengya.top/logo.png",
+ "title": "萌芽漂流瓶(´,,•ω•,,)♡",
+ "created_at": "2026-01-21T18:03:21.6115541+08:00",
+ "updated_at": "2026-01-27T13:04:27.0426589+08:00"
+ },
+ {
+ "id": "188cb6ff716e2590408704ee",
+ "name": "编程速查表[CloudFlare]",
+ "group": "self-deploy",
+ "urls": [
+ {
+ "id": "85620784f1a3",
+ "url": "https://reference.smyhub.com",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://reference.smyhub.com/icons/favicon.svg",
+ "title": "Quick Reference\n \u0026#x26; Quick Reference",
+ "created_at": "2026-01-21T18:06:01.885722+08:00",
+ "updated_at": "2026-01-27T13:10:39.3541213+08:00"
+ },
+ {
+ "id": "188cb708e8cf52a49a436570",
+ "name": "思绪思维导图[CloudFlare]",
+ "group": "self-deploy",
+ "urls": [
+ {
+ "id": "dcc07e30a25a",
+ "url": "https://mind-map.smyhub.com",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://mind-map.smyhub.com/dist/logo.ico",
+ "title": "思绪思维导图",
+ "created_at": "2026-01-21T18:06:42.5432849+08:00",
+ "updated_at": "2026-01-27T13:10:46.0879149+08:00"
+ },
+ {
+ "id": "188cb71029ad43105a3dc6c0",
+ "name": "it-tools工具集[CloudFlare]",
+ "group": "self-deploy",
+ "urls": [
+ {
+ "id": "f278e65dd498",
+ "url": "https://it-tools.smyhub.com",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://it-tools.smyhub.com/favicon.ico",
+ "title": "IT Tools - Handy online tools for developers",
+ "created_at": "2026-01-21T18:07:13.6963428+08:00",
+ "updated_at": "2026-01-27T13:11:45.7629645+08:00"
+ },
+ {
+ "id": "188cb71b527c48bc3c344acc",
+ "name": "xtools工具集[CloudFlare]",
+ "group": "self-deploy",
+ "urls": [
+ {
+ "id": "13cdc2afab17",
+ "url": "https://xtools.smyhub.com",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://xtools.smyhub.com/favicon.ico",
+ "title": "百川云常用工具",
+ "created_at": "2026-01-21T18:08:01.6256391+08:00",
+ "updated_at": "2026-01-27T13:12:18.9167742+08:00"
+ },
+ {
+ "id": "188cb72555e8d5c4f0d7aa98",
+ "name": "萌芽监控面板",
+ "group": "admin",
+ "urls": [
+ {
+ "id": "421fe08ece5e",
+ "url": "http://monitor.shumengya.top",
+ "remark": ""
+ }
+ ],
+ "favicon": "http://monitor.shumengya.top/logo.png",
+ "title": "萌芽监控面板",
+ "created_at": "2026-01-21T18:08:44.6327577+08:00",
+ "updated_at": "2026-01-23T22:58:04.325685258+08:00"
+ },
+ {
+ "id": "188cb73272be1c9c5a5d6812",
+ "name": "在线office",
+ "group": "self-deploy",
+ "urls": [
+ {
+ "id": "cba98c3c329b",
+ "url": "https://office.shumengya.top",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://office.shumengya.top/img/64.png",
+ "title": "502 Bad Gateway",
+ "created_at": "2026-01-21T18:09:40.9510719+08:00",
+ "updated_at": "2026-02-10T01:28:12.7674442+08:00"
+ },
+ {
+ "id": "188cb7372655f3841e82a210",
+ "name": "网页魔方[CloudFlare]",
+ "group": "self-deploy",
+ "urls": [
+ {
+ "id": "ed4c7dcd0565",
+ "url": "https://cube.smyhub.com",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://cube.smyhub.com/favicon.ico",
+ "title": "HTML5 3D魔方小游戏",
+ "created_at": "2026-01-21T18:10:01.1440137+08:00",
+ "updated_at": "2026-01-27T13:12:55.4496439+08:00"
+ },
+ {
+ "id": "188cb764a98dbb2453c14b8f",
+ "name": "60sAPI集合",
+ "group": "api",
+ "urls": [
+ {
+ "id": "2f0596587997",
+ "url": "https://60s.api.shumengya.top",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://60s.api.shumengya.top/favicon.ico",
+ "title": "502 Bad Gateway",
+ "created_at": "2026-01-21T18:13:16.6194034+08:00",
+ "updated_at": "2026-01-26T11:56:41.21983174+08:00"
+ },
+ {
+ "id": "188cb77f231fe68c73e24b29",
+ "name": "萌芽主页后端API",
+ "group": "api",
+ "urls": [
+ {
+ "id": "c182709178d9",
+ "url": "https://nav.api.shumengya.top",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://nav.api.shumengya.top/favicon.ico",
+ "title": "502 Bad Gateway",
+ "created_at": "2026-01-21T18:15:10.3277851+08:00",
+ "updated_at": "2026-01-26T11:56:41.237091652+08:00"
+ },
+ {
+ "id": "188cb789a76896788f139a96",
+ "name": "萌芽笔记后端API",
+ "group": "api",
+ "urls": [
+ {
+ "id": "2b42ed612dd3",
+ "url": "https://note.api.shumengya.top/api/tree",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://note.api.shumengya.top/favicon.ico",
+ "title": "502 Bad Gateway",
+ "created_at": "2026-01-21T18:15:55.4968142+08:00",
+ "updated_at": "2026-01-26T11:56:40.772396513+08:00"
+ },
+ {
+ "id": "188cb7934d5d413890dea61f",
+ "name": "萌芽作品集后端API",
+ "group": "api",
+ "urls": [
+ {
+ "id": "d841646c7b41",
+ "url": "https://work.api.shumengya.top",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://work.api.shumengya.top/favicon.ico",
+ "title": "502 Bad Gateway",
+ "created_at": "2026-01-21T18:16:36.935795+08:00",
+ "updated_at": "2026-01-26T11:56:40.714095868+08:00"
+ },
+ {
+ "id": "188cb79ed2aeb85cabcf6e66",
+ "name": "万象口袋后端API",
+ "group": "api",
+ "urls": [
+ {
+ "id": "7c8ff0e48be4",
+ "url": "https://infogenie.api.shumengya.top",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://infogenie.api.shumengya.top/favicon.ico",
+ "title": "502 Bad Gateway",
+ "created_at": "2026-01-21T18:17:26.4171439+08:00",
+ "updated_at": "2026-01-26T11:56:41.067849322+08:00"
+ },
+ {
+ "id": "188cb7b7b154ddbc1dc4b14d",
+ "name": "萌芽短链后端API",
+ "group": "api",
+ "urls": [
+ {
+ "id": "ef0443476dd8",
+ "url": "https://short.api.shumengya.top",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://short.api.shumengya.top/favicon.ico",
+ "title": "502 Bad Gateway",
+ "created_at": "2026-01-21T18:19:13.2317895+08:00",
+ "updated_at": "2026-01-26T11:56:41.107775414+08:00"
+ },
+ {
+ "id": "188cb7c8586dd2c4dcf4bd1e",
+ "name": "萌芽漂流瓶后端API",
+ "group": "api",
+ "urls": [
+ {
+ "id": "432740a25595",
+ "url": "https://bottle.api.shumengya.top",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://bottle.api.shumengya.top/favicon.ico",
+ "title": "502 Bad Gateway",
+ "created_at": "2026-01-21T18:20:24.7546969+08:00",
+ "updated_at": "2026-01-26T11:56:41.019334673+08:00"
+ },
+ {
+ "id": "188cf6ed769b75e497cd2a4a",
+ "name": "大萌芽1Panel面板[WG]",
+ "group": "admin",
+ "urls": [
+ {
+ "id": "a3e3ff7839f1",
+ "url": "http://10.0.0.233:19132/smy",
+ "remark": ""
+ }
+ ],
+ "favicon": "http://10.0.0.233:19132/favicon.ico",
+ "title": "loading...",
+ "created_at": "2026-01-22T13:37:33.4073441+08:00",
+ "updated_at": "2026-02-10T01:33:12.8434302+08:00"
+ },
+ {
+ "id": "188cf6f322f76b90c35c3484",
+ "name": "小萌芽1Panel面板[WG]",
+ "group": "admin",
+ "urls": [
+ {
+ "id": "76b6f9fe4f04",
+ "url": "http://10.0.0.100:19132/smy",
+ "remark": ""
+ }
+ ],
+ "favicon": "http://10.0.0.100:19132/favicon.ico",
+ "title": "loading...",
+ "created_at": "2026-01-22T13:37:57.7738884+08:00",
+ "updated_at": "2026-02-10T01:33:17.4968298+08:00"
+ },
+ {
+ "id": "188cf7019e56e618827f4927",
+ "name": "大萌芽Portaintor面板[WG]",
+ "group": "admin",
+ "urls": [
+ {
+ "id": "093d47789bb5",
+ "url": "http://10.0.0.233:8484/",
+ "remark": ""
+ }
+ ],
+ "favicon": "http://10.0.0.233:8484/dd5d4c0b208895c5a7de.png",
+ "title": "Portainer",
+ "created_at": "2026-01-22T13:38:59.9732854+08:00",
+ "updated_at": "2026-02-10T01:38:15.5238829+08:00"
+ },
+ {
+ "id": "188cf70700922fa4041f6470",
+ "name": "小萌芽Portaintor面板[WG]",
+ "group": "admin",
+ "urls": [
+ {
+ "id": "777c292c7696",
+ "url": "http://10.0.0.100:8484/",
+ "remark": ""
+ }
+ ],
+ "favicon": "http://10.0.0.100:8484/dd5d4c0b208895c5a7de.png",
+ "title": "Portainer",
+ "created_at": "2026-01-22T13:39:23.0961745+08:00",
+ "updated_at": "2026-02-10T01:38:36.0226948+08:00"
+ },
+ {
+ "id": "188cf728ed2992f06a8f9e52",
+ "name": "easytier面板",
+ "group": "admin",
+ "urls": [
+ {
+ "id": "0a0a59069228",
+ "url": "http://easytier.shumengya.top",
+ "remark": ""
+ }
+ ],
+ "favicon": "http://easytier.shumengya.top/easytier.png",
+ "title": "EasyTier Dashboard",
+ "created_at": "2026-01-22T13:41:48.7994396+08:00",
+ "updated_at": "2026-01-23T22:58:03.667100466+08:00"
+ },
+ {
+ "id": "188cf72edea71c4cb2284c93",
+ "name": "wireguard面板",
+ "group": "admin",
+ "urls": [
+ {
+ "id": "170fab2239ad",
+ "url": "https://wireguard.shumengya.top",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://wireguard.shumengya.top/favicon.ico",
+ "title": "502 Bad Gateway",
+ "created_at": "2026-01-22T13:42:14.3258123+08:00",
+ "updated_at": "2026-01-26T11:56:40.996902062+08:00"
+ },
+ {
+ "id": "188cf746b16a83dc4d5e3d13",
+ "name": "萌芽Docker镜像仓库",
+ "group": "admin",
+ "urls": [
+ {
+ "id": "822329e621c2",
+ "url": "https://repo.docker.shumengya.top",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://repo.docker.shumengya.top/favicon.ico",
+ "title": "502 Bad Gateway",
+ "created_at": "2026-01-22T13:43:56.6460815+08:00",
+ "updated_at": "2026-01-26T11:56:40.972109897+08:00"
+ },
+ {
+ "id": "188cf74b2fa5da7c8e2b588e",
+ "name": "萌芽通知",
+ "group": "admin",
+ "urls": [
+ {
+ "id": "2f580f90f979",
+ "url": "https://notice.shumengya.top",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://notice.shumengya.top/favicon.ico",
+ "title": "502 Bad Gateway",
+ "created_at": "2026-01-22T13:44:15.9437687+08:00",
+ "updated_at": "2026-01-26T11:56:41.065412821+08:00"
+ },
+ {
+ "id": "188d00db054cf7860dff3440",
+ "name": "萌芽Ping",
+ "group": "admin",
+ "urls": [
+ {
+ "id": "ac85b166ccc4",
+ "url": "https://ping.shumengya.top",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://ping.shumengya.top/favicon.ico",
+ "title": "萌芽Ping - 网站监控面板",
+ "created_at": "2026-01-22T16:39:29.31324815+08:00",
+ "updated_at": "2026-01-23T13:28:03.633565425+08:00"
+ },
+ {
+ "id": "188d00edf571226f42ec8b20",
+ "name": "萌芽Ping后端API",
+ "group": "api",
+ "urls": [
+ {
+ "id": "0bf0fe1bb9cc",
+ "url": "https://ping.api.shumengya.top",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://ping.api.shumengya.top/favicon.ico",
+ "title": "萌芽Git仓库",
+ "created_at": "2026-01-22T16:40:50.651561641+08:00",
+ "updated_at": "2026-01-23T13:28:03.351764738+08:00"
+ },
+ {
+ "id": "188e7f1a40cdc7bc3bba1817",
+ "name": "DNS查询[CloudFlare]",
+ "group": "self-deploy",
+ "urls": [
+ {
+ "id": "9af024b3d387",
+ "url": "https://cf-dns.smyhub.com/",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://cf-assets.www.cloudflare.com/dzlvafdwdttg/6TaQ8Q7BDmdAFRoHpDCb82/8d9bc52a2ac5af100de3a9adcf99ffaa/security-shield-protection-2.svg",
+ "title": "DNS-over-HTTPS Resolver",
+ "created_at": "2026-01-27T13:24:14.3362887+08:00",
+ "updated_at": "2026-01-27T13:24:15.171393+08:00"
+ },
+ {
+ "id": "188e7f27a909fe247a2fcd15",
+ "name": "Github文件加速[CloudFlare]",
+ "group": "self-deploy",
+ "urls": [
+ {
+ "id": "b897ef0def67",
+ "url": "https://gh-proxy.smyhub.com/",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://gh-proxy.smyhub.com/favicon.ico",
+ "title": "GitHub 文件加速",
+ "created_at": "2026-01-27T13:25:11.9196401+08:00",
+ "updated_at": "2026-01-27T13:25:12.8034353+08:00"
+ },
+ {
+ "id": "188e7f33f087667c29da05f2",
+ "name": "FloppyBird[CloudFlare]",
+ "group": "self-deploy",
+ "urls": [
+ {
+ "id": "75a0cb223c2d",
+ "url": "https://floppy-bird.smyhub.com/",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://floppy-bird.smyhub.com/favicon.ico",
+ "title": "Floppy Bird",
+ "created_at": "2026-01-27T13:26:04.6586487+08:00",
+ "updated_at": "2026-01-27T13:26:05.4749231+08:00"
+ },
+ {
+ "id": "188e7f4cdd13a3bc80c68184",
+ "name": "别踩白方块[CloudFlare]",
+ "group": "self-deploy",
+ "urls": [
+ {
+ "id": "9f11daaedba4",
+ "url": "https://floppy-bird.smyhub.com/",
+ "remark": ""
+ }
+ ],
+ "favicon": "https://floppy-bird.smyhub.com/favicon.ico",
+ "title": "Floppy Bird",
+ "created_at": "2026-01-27T13:27:51.7064775+08:00",
+ "updated_at": "2026-01-27T13:27:51.9331276+08:00"
+ }
+]
\ No newline at end of file
diff --git a/mengyaping-backend/docker-compose.yml b/mengyaping-backend/docker-compose.yml
new file mode 100644
index 0000000..8e15cca
--- /dev/null
+++ b/mengyaping-backend/docker-compose.yml
@@ -0,0 +1,40 @@
+version: '3.8'
+
+services:
+ mengyaping-backend:
+ build:
+ context: .
+ dockerfile: Dockerfile
+ container_name: mengyaping-backend
+ restart: unless-stopped
+ ports:
+ - "6161:8080"
+ volumes:
+ # 持久化数据目录
+ - /shumengya/docker/mengyaping-backend/data/:/app/data
+ environment:
+ # 服务器配置
+ - SERVER_PORT=8080
+ - SERVER_HOST=0.0.0.0
+ # 监控配置
+ - MONITOR_INTERVAL=5m
+ - MONITOR_TIMEOUT=10s
+ - MONITOR_RETRY_COUNT=3
+ - MONITOR_HISTORY_DAYS=7
+ networks:
+ - mengyaping-network
+ healthcheck:
+ test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/api/health"]
+ interval: 30s
+ timeout: 3s
+ retries: 3
+ start_period: 5s
+ logging:
+ driver: "json-file"
+ options:
+ max-size: "10m"
+ max-file: "3"
+
+networks:
+ mengyaping-network:
+ driver: bridge
diff --git a/mengyaping-backend/go.mod b/mengyaping-backend/go.mod
new file mode 100644
index 0000000..5fbb0aa
--- /dev/null
+++ b/mengyaping-backend/go.mod
@@ -0,0 +1,42 @@
+module mengyaping-backend
+
+go 1.25.0
+
+require (
+ github.com/gin-contrib/cors v1.7.6
+ github.com/gin-gonic/gin v1.11.0
+)
+
+require (
+ github.com/bytedance/sonic v1.14.0 // indirect
+ github.com/bytedance/sonic/loader v0.3.0 // indirect
+ github.com/cloudwego/base64x v0.1.6 // indirect
+ github.com/gabriel-vasile/mimetype v1.4.9 // indirect
+ github.com/gin-contrib/sse v1.1.0 // indirect
+ github.com/go-playground/locales v0.14.1 // indirect
+ github.com/go-playground/universal-translator v0.18.1 // indirect
+ github.com/go-playground/validator/v10 v10.27.0 // indirect
+ github.com/goccy/go-json v0.10.5 // indirect
+ github.com/goccy/go-yaml v1.18.0 // indirect
+ github.com/json-iterator/go v1.1.12 // indirect
+ github.com/klauspost/cpuid/v2 v2.3.0 // indirect
+ github.com/leodido/go-urn v1.4.0 // indirect
+ github.com/mattn/go-isatty v0.0.20 // indirect
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v1.0.2 // indirect
+ github.com/pelletier/go-toml/v2 v2.2.4 // indirect
+ github.com/quic-go/qpack v0.5.1 // indirect
+ github.com/quic-go/quic-go v0.54.0 // indirect
+ github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
+ github.com/ugorji/go/codec v1.3.0 // indirect
+ go.uber.org/mock v0.5.0 // indirect
+ golang.org/x/arch v0.20.0 // indirect
+ golang.org/x/crypto v0.40.0 // indirect
+ golang.org/x/mod v0.25.0 // indirect
+ golang.org/x/net v0.42.0 // indirect
+ golang.org/x/sync v0.16.0 // indirect
+ golang.org/x/sys v0.35.0 // indirect
+ golang.org/x/text v0.27.0 // indirect
+ golang.org/x/tools v0.34.0 // indirect
+ google.golang.org/protobuf v1.36.9 // indirect
+)
diff --git a/mengyaping-backend/go.sum b/mengyaping-backend/go.sum
new file mode 100644
index 0000000..7539074
--- /dev/null
+++ b/mengyaping-backend/go.sum
@@ -0,0 +1,91 @@
+github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ=
+github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA=
+github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA=
+github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
+github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
+github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
+github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
+github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQY=
+github.com/gin-contrib/cors v1.7.6/go.mod h1:Ulcl+xN4jel9t1Ry8vqph23a60FwH9xVLd+3ykmTjOk=
+github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
+github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
+github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
+github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
+github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
+github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
+github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
+github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
+github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
+github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
+github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
+github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
+github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
+github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
+github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
+github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
+github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
+github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
+github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
+github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
+github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg=
+github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
+github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
+github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
+github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
+github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
+github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
+go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
+go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
+golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c=
+golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
+golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
+golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
+golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
+golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
+golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
+golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
+golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
+golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
+golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
+golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
+golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
+golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
+google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
+google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/mengyaping-backend/handlers/website.go b/mengyaping-backend/handlers/website.go
new file mode 100644
index 0000000..d69b3e3
--- /dev/null
+++ b/mengyaping-backend/handlers/website.go
@@ -0,0 +1,199 @@
+package handlers
+
+import (
+ "net/http"
+
+ "github.com/gin-gonic/gin"
+
+ "mengyaping-backend/models"
+ "mengyaping-backend/services"
+)
+
+// WebsiteHandler 网站处理器
+type WebsiteHandler struct {
+ websiteService *services.WebsiteService
+ monitorService *services.MonitorService
+}
+
+// NewWebsiteHandler 创建网站处理器
+func NewWebsiteHandler() *WebsiteHandler {
+ return &WebsiteHandler{
+ websiteService: services.NewWebsiteService(),
+ monitorService: services.GetMonitorService(),
+ }
+}
+
+// GetWebsites 获取所有网站状态
+func (h *WebsiteHandler) GetWebsites(c *gin.Context) {
+ statuses := h.monitorService.GetAllWebsiteStatuses()
+
+ c.JSON(http.StatusOK, models.APIResponse{
+ Code: 0,
+ Message: "success",
+ Data: statuses,
+ })
+}
+
+// GetWebsite 获取单个网站状态
+func (h *WebsiteHandler) GetWebsite(c *gin.Context) {
+ id := c.Param("id")
+
+ status := h.monitorService.GetWebsiteStatus(id)
+ if status == nil {
+ c.JSON(http.StatusNotFound, models.APIResponse{
+ Code: 404,
+ Message: "网站不存在",
+ })
+ return
+ }
+
+ c.JSON(http.StatusOK, models.APIResponse{
+ Code: 0,
+ Message: "success",
+ Data: status,
+ })
+}
+
+// CreateWebsite 创建网站
+func (h *WebsiteHandler) CreateWebsite(c *gin.Context) {
+ var req models.CreateWebsiteRequest
+ if err := c.ShouldBindJSON(&req); err != nil {
+ c.JSON(http.StatusBadRequest, models.APIResponse{
+ Code: 400,
+ Message: "参数错误: " + err.Error(),
+ })
+ return
+ }
+
+ website, err := h.websiteService.CreateWebsite(req)
+ if err != nil {
+ c.JSON(http.StatusInternalServerError, models.APIResponse{
+ Code: 500,
+ Message: "创建失败: " + err.Error(),
+ })
+ return
+ }
+
+ c.JSON(http.StatusOK, models.APIResponse{
+ Code: 0,
+ Message: "创建成功",
+ Data: website,
+ })
+}
+
+// UpdateWebsite 更新网站
+func (h *WebsiteHandler) UpdateWebsite(c *gin.Context) {
+ id := c.Param("id")
+
+ var req models.UpdateWebsiteRequest
+ if err := c.ShouldBindJSON(&req); err != nil {
+ c.JSON(http.StatusBadRequest, models.APIResponse{
+ Code: 400,
+ Message: "参数错误: " + err.Error(),
+ })
+ return
+ }
+
+ website, err := h.websiteService.UpdateWebsite(id, req)
+ if err != nil {
+ c.JSON(http.StatusInternalServerError, models.APIResponse{
+ Code: 500,
+ Message: "更新失败: " + err.Error(),
+ })
+ return
+ }
+
+ if website == nil {
+ c.JSON(http.StatusNotFound, models.APIResponse{
+ Code: 404,
+ Message: "网站不存在",
+ })
+ return
+ }
+
+ c.JSON(http.StatusOK, models.APIResponse{
+ Code: 0,
+ Message: "更新成功",
+ Data: website,
+ })
+}
+
+// DeleteWebsite 删除网站
+func (h *WebsiteHandler) DeleteWebsite(c *gin.Context) {
+ id := c.Param("id")
+
+ if err := h.websiteService.DeleteWebsite(id); err != nil {
+ c.JSON(http.StatusInternalServerError, models.APIResponse{
+ Code: 500,
+ Message: "删除失败: " + err.Error(),
+ })
+ return
+ }
+
+ c.JSON(http.StatusOK, models.APIResponse{
+ Code: 0,
+ Message: "删除成功",
+ })
+}
+
+// CheckWebsiteNow 立即检测网站
+func (h *WebsiteHandler) CheckWebsiteNow(c *gin.Context) {
+ id := c.Param("id")
+
+ website := h.websiteService.GetWebsite(id)
+ if website == nil {
+ c.JSON(http.StatusNotFound, models.APIResponse{
+ Code: 404,
+ Message: "网站不存在",
+ })
+ return
+ }
+
+ h.monitorService.CheckWebsiteNow(id)
+
+ c.JSON(http.StatusOK, models.APIResponse{
+ Code: 0,
+ Message: "检测任务已提交",
+ })
+}
+
+// GetGroups 获取所有分组
+func (h *WebsiteHandler) GetGroups(c *gin.Context) {
+ groups := h.websiteService.GetGroups()
+
+ c.JSON(http.StatusOK, models.APIResponse{
+ Code: 0,
+ Message: "success",
+ Data: groups,
+ })
+}
+
+// AddGroup 添加分组
+func (h *WebsiteHandler) AddGroup(c *gin.Context) {
+ var req struct {
+ Name string `json:"name" binding:"required"`
+ }
+
+ if err := c.ShouldBindJSON(&req); err != nil {
+ c.JSON(http.StatusBadRequest, models.APIResponse{
+ Code: 400,
+ Message: "参数错误: " + err.Error(),
+ })
+ return
+ }
+
+ group, err := h.websiteService.AddGroup(req.Name)
+ if err != nil {
+ c.JSON(http.StatusInternalServerError, models.APIResponse{
+ Code: 500,
+ Message: "添加失败: " + err.Error(),
+ })
+ return
+ }
+
+ c.JSON(http.StatusOK, models.APIResponse{
+ Code: 0,
+ Message: "添加成功",
+ Data: group,
+ })
+}
diff --git a/mengyaping-backend/main.go b/mengyaping-backend/main.go
new file mode 100644
index 0000000..8b4fca3
--- /dev/null
+++ b/mengyaping-backend/main.go
@@ -0,0 +1,47 @@
+package main
+
+import (
+ "fmt"
+ "log"
+ "os"
+ "os/signal"
+ "syscall"
+
+ "mengyaping-backend/config"
+ "mengyaping-backend/router"
+ "mengyaping-backend/services"
+)
+
+func main() {
+ // 获取配置
+ cfg := config.GetConfig()
+
+ // 确保数据目录存在
+ os.MkdirAll(cfg.DataPath, 0755)
+
+ // 启动监控服务
+ monitorService := services.GetMonitorService()
+ go monitorService.Start()
+
+ // 设置路由
+ r := router.SetupRouter()
+
+ // 优雅关闭
+ go func() {
+ sigCh := make(chan os.Signal, 1)
+ signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
+ <-sigCh
+
+ log.Println("正在关闭服务...")
+ monitorService.Stop()
+ os.Exit(0)
+ }()
+
+ // 启动服务器
+ addr := fmt.Sprintf("%s:%s", cfg.Server.Host, cfg.Server.Port)
+ log.Printf("🌱 萌芽Ping 监控服务已启动,监听地址: %s\n", addr)
+
+ if err := r.Run(addr); err != nil {
+ log.Fatalf("服务器启动失败: %v", err)
+ }
+}
diff --git a/mengyaping-backend/models/website.go b/mengyaping-backend/models/website.go
new file mode 100644
index 0000000..792700c
--- /dev/null
+++ b/mengyaping-backend/models/website.go
@@ -0,0 +1,98 @@
+package models
+
+import (
+ "time"
+)
+
+// Website 网站信息
+type Website struct {
+ ID string `json:"id"`
+ Name string `json:"name"` // 网站名称
+ Group string `json:"group"` // 所属分组
+ URLs []URLInfo `json:"urls"` // 网站访问地址列表
+ Favicon string `json:"favicon"` // 网站图标URL
+ Title string `json:"title"` // 网站标题
+ CreatedAt time.Time `json:"created_at"` // 创建时间
+ UpdatedAt time.Time `json:"updated_at"` // 更新时间
+}
+
+// URLInfo 单个URL的信息
+type URLInfo struct {
+ ID string `json:"id"`
+ URL string `json:"url"` // 访问地址
+ Remark string `json:"remark"` // 备注说明
+}
+
+// MonitorRecord 监控记录
+type MonitorRecord struct {
+ WebsiteID string `json:"website_id"`
+ URLID string `json:"url_id"`
+ URL string `json:"url"`
+ StatusCode int `json:"status_code"` // HTTP状态码
+ Latency int64 `json:"latency"` // 延迟(毫秒)
+ IsUp bool `json:"is_up"` // 是否可访问
+ Error string `json:"error"` // 错误信息
+ CheckedAt time.Time `json:"checked_at"` // 检测时间
+}
+
+// WebsiteStatus 网站状态(用于前端展示)
+type WebsiteStatus struct {
+ Website Website `json:"website"`
+ URLStatuses []URLStatus `json:"url_statuses"`
+ Uptime24h float64 `json:"uptime_24h"` // 24小时可用率
+ Uptime7d float64 `json:"uptime_7d"` // 7天可用率
+ LastChecked time.Time `json:"last_checked"` // 最后检测时间
+}
+
+// URLStatus 单个URL的状态
+type URLStatus struct {
+ URLInfo URLInfo `json:"url_info"`
+ CurrentState MonitorRecord `json:"current_state"` // 当前状态
+ History24h []MonitorRecord `json:"history_24h"` // 24小时历史
+ History7d []HourlyStats `json:"history_7d"` // 7天按小时统计
+ Uptime24h float64 `json:"uptime_24h"` // 24小时可用率
+ Uptime7d float64 `json:"uptime_7d"` // 7天可用率
+ AvgLatency int64 `json:"avg_latency"` // 平均延迟
+}
+
+// HourlyStats 每小时统计
+type HourlyStats struct {
+ Hour time.Time `json:"hour"`
+ TotalCount int `json:"total_count"`
+ UpCount int `json:"up_count"`
+ AvgLatency int64 `json:"avg_latency"`
+ Uptime float64 `json:"uptime"`
+}
+
+// Group 分组
+type Group struct {
+ ID string `json:"id"`
+ Name string `json:"name"`
+}
+
+// DefaultGroups 默认分组
+var DefaultGroups = []Group{
+ {ID: "normal", Name: "普通网站"},
+ {ID: "admin", Name: "管理员网站"},
+}
+
+// CreateWebsiteRequest 创建网站请求
+type CreateWebsiteRequest struct {
+ Name string `json:"name" binding:"required"`
+ Group string `json:"group" binding:"required"`
+ URLs []string `json:"urls" binding:"required,min=1"`
+}
+
+// UpdateWebsiteRequest 更新网站请求
+type UpdateWebsiteRequest struct {
+ Name string `json:"name"`
+ Group string `json:"group"`
+ URLs []string `json:"urls"`
+}
+
+// APIResponse API响应
+type APIResponse struct {
+ Code int `json:"code"`
+ Message string `json:"message"`
+ Data interface{} `json:"data,omitempty"`
+}
diff --git a/mengyaping-backend/router/router.go b/mengyaping-backend/router/router.go
new file mode 100644
index 0000000..25a559b
--- /dev/null
+++ b/mengyaping-backend/router/router.go
@@ -0,0 +1,51 @@
+package router
+
+import (
+ "github.com/gin-contrib/cors"
+ "github.com/gin-gonic/gin"
+
+ "mengyaping-backend/handlers"
+)
+
+// SetupRouter 设置路由
+func SetupRouter() *gin.Engine {
+ r := gin.Default()
+
+ // CORS配置
+ r.Use(cors.New(cors.Config{
+ AllowOrigins: []string{"*"},
+ AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
+ AllowHeaders: []string{"Origin", "Content-Type", "Accept", "Authorization"},
+ ExposeHeaders: []string{"Content-Length"},
+ AllowCredentials: true,
+ }))
+
+ // 创建处理器
+ websiteHandler := handlers.NewWebsiteHandler()
+
+ // API路由组
+ api := r.Group("/api")
+ {
+ // 健康检查
+ api.GET("/health", func(c *gin.Context) {
+ c.JSON(200, gin.H{
+ "status": "ok",
+ "message": "服务运行正常",
+ })
+ })
+
+ // 网站相关
+ api.GET("/websites", websiteHandler.GetWebsites)
+ api.GET("/websites/:id", websiteHandler.GetWebsite)
+ api.POST("/websites", websiteHandler.CreateWebsite)
+ api.PUT("/websites/:id", websiteHandler.UpdateWebsite)
+ api.DELETE("/websites/:id", websiteHandler.DeleteWebsite)
+ api.POST("/websites/:id/check", websiteHandler.CheckWebsiteNow)
+
+ // 分组相关
+ api.GET("/groups", websiteHandler.GetGroups)
+ api.POST("/groups", websiteHandler.AddGroup)
+ }
+
+ return r
+}
diff --git a/mengyaping-backend/services/monitor.go b/mengyaping-backend/services/monitor.go
new file mode 100644
index 0000000..46f5876
--- /dev/null
+++ b/mengyaping-backend/services/monitor.go
@@ -0,0 +1,302 @@
+package services
+
+import (
+ "log"
+ "sync"
+ "time"
+
+ "mengyaping-backend/config"
+ "mengyaping-backend/models"
+ "mengyaping-backend/storage"
+ "mengyaping-backend/utils"
+)
+
+// MonitorService 监控服务
+type MonitorService struct {
+ httpClient *utils.HTTPClient
+ storage *storage.Storage
+ stopCh chan struct{}
+ running bool
+ mu sync.Mutex
+}
+
+var (
+ monitorService *MonitorService
+ monitorOnce sync.Once
+)
+
+// GetMonitorService 获取监控服务单例
+func GetMonitorService() *MonitorService {
+ monitorOnce.Do(func() {
+ cfg := config.GetConfig()
+ monitorService = &MonitorService{
+ httpClient: utils.NewHTTPClient(cfg.Monitor.Timeout),
+ storage: storage.GetStorage(),
+ stopCh: make(chan struct{}),
+ }
+ })
+ return monitorService
+}
+
+// Start 启动监控服务
+func (s *MonitorService) Start() {
+ s.mu.Lock()
+ if s.running {
+ s.mu.Unlock()
+ return
+ }
+ s.running = true
+ s.mu.Unlock()
+
+ log.Println("监控服务已启动")
+
+ // 立即执行一次检测
+ go s.checkAll()
+
+ // 定时检测
+ cfg := config.GetConfig()
+ ticker := time.NewTicker(cfg.Monitor.Interval)
+ defer ticker.Stop()
+
+ for {
+ select {
+ case <-ticker.C:
+ go s.checkAll()
+ case <-s.stopCh:
+ log.Println("监控服务已停止")
+ return
+ }
+ }
+}
+
+// Stop 停止监控服务
+func (s *MonitorService) Stop() {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ if s.running {
+ close(s.stopCh)
+ s.running = false
+ }
+}
+
+// checkAll 检查所有网站
+func (s *MonitorService) checkAll() {
+ websites := s.storage.GetWebsites()
+
+ var wg sync.WaitGroup
+ semaphore := make(chan struct{}, 10) // 限制并发数
+
+ for _, website := range websites {
+ for _, urlInfo := range website.URLs {
+ wg.Add(1)
+ go func(w models.Website, u models.URLInfo) {
+ defer wg.Done()
+ semaphore <- struct{}{}
+ defer func() { <-semaphore }()
+
+ s.checkURL(w, u)
+ }(website, urlInfo)
+ }
+ }
+
+ wg.Wait()
+
+ // 保存记录
+ s.storage.SaveAll()
+}
+
+// checkURL 检查单个URL
+func (s *MonitorService) checkURL(website models.Website, urlInfo models.URLInfo) {
+ result := s.httpClient.CheckWebsite(urlInfo.URL)
+
+ record := models.MonitorRecord{
+ WebsiteID: website.ID,
+ URLID: urlInfo.ID,
+ URL: urlInfo.URL,
+ StatusCode: result.StatusCode,
+ Latency: result.Latency.Milliseconds(),
+ IsUp: result.Error == nil && utils.IsSuccessStatus(result.StatusCode),
+ CheckedAt: time.Now(),
+ }
+
+ if result.Error != nil {
+ record.Error = result.Error.Error()
+ }
+
+ s.storage.AddRecord(record)
+
+ // 更新网站信息(标题和Favicon)
+ if result.Title != "" || result.Favicon != "" {
+ w := s.storage.GetWebsite(website.ID)
+ if w != nil {
+ needUpdate := false
+ if result.Title != "" && w.Title != result.Title {
+ w.Title = result.Title
+ needUpdate = true
+ }
+ if result.Favicon != "" && w.Favicon != result.Favicon {
+ w.Favicon = result.Favicon
+ needUpdate = true
+ }
+ if needUpdate {
+ w.UpdatedAt = time.Now()
+ s.storage.UpdateWebsite(*w)
+ }
+ }
+ }
+
+ log.Printf("检测 [%s] %s - 状态码: %d, 延迟: %dms, 可用: %v",
+ website.Name, urlInfo.URL, result.StatusCode, result.Latency.Milliseconds(), record.IsUp)
+}
+
+// CheckWebsiteNow 立即检查指定网站
+func (s *MonitorService) CheckWebsiteNow(websiteID string) {
+ website := s.storage.GetWebsite(websiteID)
+ if website == nil {
+ return
+ }
+
+ for _, urlInfo := range website.URLs {
+ go s.checkURL(*website, urlInfo)
+ }
+}
+
+// GetWebsiteStatus 获取网站状态
+func (s *MonitorService) GetWebsiteStatus(websiteID string) *models.WebsiteStatus {
+ website := s.storage.GetWebsite(websiteID)
+ if website == nil {
+ return nil
+ }
+
+ status := &models.WebsiteStatus{
+ Website: *website,
+ URLStatuses: []models.URLStatus{},
+ }
+
+ now := time.Now()
+ since24h := now.Add(-24 * time.Hour)
+ since7d := now.Add(-7 * 24 * time.Hour)
+
+ var totalUptime24h, totalUptime7d float64
+ var urlCount int
+
+ for _, urlInfo := range website.URLs {
+ urlStatus := s.getURLStatus(website.ID, urlInfo, since24h, since7d)
+ status.URLStatuses = append(status.URLStatuses, urlStatus)
+
+ totalUptime24h += urlStatus.Uptime24h
+ totalUptime7d += urlStatus.Uptime7d
+ urlCount++
+ }
+
+ if urlCount > 0 {
+ status.Uptime24h = totalUptime24h / float64(urlCount)
+ status.Uptime7d = totalUptime7d / float64(urlCount)
+ }
+
+ // 获取最后检测时间
+ for _, urlStatus := range status.URLStatuses {
+ if urlStatus.CurrentState.CheckedAt.After(status.LastChecked) {
+ status.LastChecked = urlStatus.CurrentState.CheckedAt
+ }
+ }
+
+ return status
+}
+
+// getURLStatus 获取URL状态
+func (s *MonitorService) getURLStatus(websiteID string, urlInfo models.URLInfo, since24h, since7d time.Time) models.URLStatus {
+ urlStatus := models.URLStatus{
+ URLInfo: urlInfo,
+ }
+
+ // 获取最新记录
+ latest := s.storage.GetLatestRecord(websiteID, urlInfo.ID)
+ if latest != nil {
+ urlStatus.CurrentState = *latest
+ }
+
+ // 获取24小时记录
+ records24h := s.storage.GetRecords(websiteID, urlInfo.ID, since24h)
+ urlStatus.History24h = records24h
+
+ // 计算24小时可用率
+ if len(records24h) > 0 {
+ upCount := 0
+ var totalLatency int64
+ for _, r := range records24h {
+ if r.IsUp {
+ upCount++
+ }
+ totalLatency += r.Latency
+ }
+ urlStatus.Uptime24h = float64(upCount) / float64(len(records24h)) * 100
+ urlStatus.AvgLatency = totalLatency / int64(len(records24h))
+ }
+
+ // 获取7天记录并按小时统计
+ records7d := s.storage.GetRecords(websiteID, urlInfo.ID, since7d)
+ urlStatus.History7d = s.aggregateByHour(records7d)
+
+ // 计算7天可用率
+ if len(records7d) > 0 {
+ upCount := 0
+ for _, r := range records7d {
+ if r.IsUp {
+ upCount++
+ }
+ }
+ urlStatus.Uptime7d = float64(upCount) / float64(len(records7d)) * 100
+ }
+
+ return urlStatus
+}
+
+// aggregateByHour 按小时聚合记录
+func (s *MonitorService) aggregateByHour(records []models.MonitorRecord) []models.HourlyStats {
+ hourlyMap := make(map[string]*models.HourlyStats)
+
+ for _, r := range records {
+ hourKey := r.CheckedAt.Truncate(time.Hour).Format(time.RFC3339)
+
+ if _, exists := hourlyMap[hourKey]; !exists {
+ hourlyMap[hourKey] = &models.HourlyStats{
+ Hour: r.CheckedAt.Truncate(time.Hour),
+ }
+ }
+
+ stats := hourlyMap[hourKey]
+ stats.TotalCount++
+ if r.IsUp {
+ stats.UpCount++
+ }
+ stats.AvgLatency += r.Latency
+ }
+
+ var result []models.HourlyStats
+ for _, stats := range hourlyMap {
+ if stats.TotalCount > 0 {
+ stats.AvgLatency /= int64(stats.TotalCount)
+ stats.Uptime = float64(stats.UpCount) / float64(stats.TotalCount) * 100
+ }
+ result = append(result, *stats)
+ }
+
+ return result
+}
+
+// GetAllWebsiteStatuses 获取所有网站状态
+func (s *MonitorService) GetAllWebsiteStatuses() []models.WebsiteStatus {
+ websites := s.storage.GetWebsites()
+ var statuses []models.WebsiteStatus
+
+ for _, website := range websites {
+ status := s.GetWebsiteStatus(website.ID)
+ if status != nil {
+ statuses = append(statuses, *status)
+ }
+ }
+
+ return statuses
+}
diff --git a/mengyaping-backend/services/website.go b/mengyaping-backend/services/website.go
new file mode 100644
index 0000000..23d85fb
--- /dev/null
+++ b/mengyaping-backend/services/website.go
@@ -0,0 +1,127 @@
+package services
+
+import (
+ "time"
+
+ "mengyaping-backend/models"
+ "mengyaping-backend/storage"
+ "mengyaping-backend/utils"
+)
+
+// WebsiteService 网站服务
+type WebsiteService struct {
+ storage *storage.Storage
+}
+
+// NewWebsiteService 创建网站服务
+func NewWebsiteService() *WebsiteService {
+ return &WebsiteService{
+ storage: storage.GetStorage(),
+ }
+}
+
+// CreateWebsite 创建网站
+func (s *WebsiteService) CreateWebsite(req models.CreateWebsiteRequest) (*models.Website, error) {
+ website := models.Website{
+ ID: utils.GenerateID(),
+ Name: req.Name,
+ Group: req.Group,
+ URLs: make([]models.URLInfo, 0),
+ CreatedAt: time.Now(),
+ UpdatedAt: time.Now(),
+ }
+
+ for _, url := range req.URLs {
+ urlInfo := models.URLInfo{
+ ID: utils.GenerateShortID(),
+ URL: url,
+ }
+ website.URLs = append(website.URLs, urlInfo)
+ }
+
+ if err := s.storage.AddWebsite(website); err != nil {
+ return nil, err
+ }
+
+ // 立即检测该网站
+ go GetMonitorService().CheckWebsiteNow(website.ID)
+
+ return &website, nil
+}
+
+// GetWebsite 获取网站
+func (s *WebsiteService) GetWebsite(id string) *models.Website {
+ return s.storage.GetWebsite(id)
+}
+
+// GetAllWebsites 获取所有网站
+func (s *WebsiteService) GetAllWebsites() []models.Website {
+ return s.storage.GetWebsites()
+}
+
+// UpdateWebsite 更新网站
+func (s *WebsiteService) UpdateWebsite(id string, req models.UpdateWebsiteRequest) (*models.Website, error) {
+ website := s.storage.GetWebsite(id)
+ if website == nil {
+ return nil, nil
+ }
+
+ if req.Name != "" {
+ website.Name = req.Name
+ }
+ if req.Group != "" {
+ website.Group = req.Group
+ }
+ if len(req.URLs) > 0 {
+ // 保留已有URL的ID,添加新URL
+ existingURLs := make(map[string]models.URLInfo)
+ for _, u := range website.URLs {
+ existingURLs[u.URL] = u
+ }
+
+ newURLs := make([]models.URLInfo, 0)
+ for _, url := range req.URLs {
+ if existing, ok := existingURLs[url]; ok {
+ newURLs = append(newURLs, existing)
+ } else {
+ newURLs = append(newURLs, models.URLInfo{
+ ID: utils.GenerateShortID(),
+ URL: url,
+ })
+ }
+ }
+ website.URLs = newURLs
+ }
+
+ website.UpdatedAt = time.Now()
+
+ if err := s.storage.UpdateWebsite(*website); err != nil {
+ return nil, err
+ }
+
+ return website, nil
+}
+
+// DeleteWebsite 删除网站
+func (s *WebsiteService) DeleteWebsite(id string) error {
+ return s.storage.DeleteWebsite(id)
+}
+
+// GetGroups 获取所有分组
+func (s *WebsiteService) GetGroups() []models.Group {
+ return s.storage.GetGroups()
+}
+
+// AddGroup 添加分组
+func (s *WebsiteService) AddGroup(name string) (*models.Group, error) {
+ group := models.Group{
+ ID: utils.GenerateShortID(),
+ Name: name,
+ }
+
+ if err := s.storage.AddGroup(group); err != nil {
+ return nil, err
+ }
+
+ return &group, nil
+}
diff --git a/mengyaping-backend/storage/storage.go b/mengyaping-backend/storage/storage.go
new file mode 100644
index 0000000..a027e36
--- /dev/null
+++ b/mengyaping-backend/storage/storage.go
@@ -0,0 +1,285 @@
+package storage
+
+import (
+ "encoding/json"
+ "os"
+ "path/filepath"
+ "sync"
+ "time"
+
+ "mengyaping-backend/config"
+ "mengyaping-backend/models"
+)
+
+// Storage 数据存储
+type Storage struct {
+ dataPath string
+ mu sync.RWMutex
+ websites []models.Website
+ records map[string][]models.MonitorRecord // key: websiteID_urlID
+ groups []models.Group
+}
+
+var (
+ store *Storage
+ once sync.Once
+)
+
+// GetStorage 获取存储单例
+func GetStorage() *Storage {
+ once.Do(func() {
+ cfg := config.GetConfig()
+ store = &Storage{
+ dataPath: cfg.DataPath,
+ websites: []models.Website{},
+ records: make(map[string][]models.MonitorRecord),
+ groups: models.DefaultGroups,
+ }
+ store.ensureDataDir()
+ store.load()
+ })
+ return store
+}
+
+// ensureDataDir 确保数据目录存在
+func (s *Storage) ensureDataDir() {
+ os.MkdirAll(s.dataPath, 0755)
+}
+
+// load 加载数据
+func (s *Storage) load() {
+ s.loadWebsites()
+ s.loadRecords()
+ s.loadGroups()
+}
+
+// loadWebsites 加载网站数据
+func (s *Storage) loadWebsites() {
+ filePath := filepath.Join(s.dataPath, "websites.json")
+ data, err := os.ReadFile(filePath)
+ if err != nil {
+ return
+ }
+ json.Unmarshal(data, &s.websites)
+}
+
+// loadRecords 加载监控记录
+func (s *Storage) loadRecords() {
+ filePath := filepath.Join(s.dataPath, "records.json")
+ data, err := os.ReadFile(filePath)
+ if err != nil {
+ return
+ }
+ json.Unmarshal(data, &s.records)
+
+ // 清理过期记录
+ s.cleanOldRecords()
+}
+
+// loadGroups 加载分组
+func (s *Storage) loadGroups() {
+ filePath := filepath.Join(s.dataPath, "groups.json")
+ data, err := os.ReadFile(filePath)
+ if err != nil {
+ // 使用默认分组
+ s.groups = models.DefaultGroups
+ s.saveGroups()
+ return
+ }
+ json.Unmarshal(data, &s.groups)
+}
+
+// saveWebsites 保存网站数据
+func (s *Storage) saveWebsites() error {
+ filePath := filepath.Join(s.dataPath, "websites.json")
+ data, err := json.MarshalIndent(s.websites, "", " ")
+ if err != nil {
+ return err
+ }
+ return os.WriteFile(filePath, data, 0644)
+}
+
+// saveRecords 保存监控记录
+func (s *Storage) saveRecords() error {
+ filePath := filepath.Join(s.dataPath, "records.json")
+ data, err := json.MarshalIndent(s.records, "", " ")
+ if err != nil {
+ return err
+ }
+ return os.WriteFile(filePath, data, 0644)
+}
+
+// saveGroups 保存分组
+func (s *Storage) saveGroups() error {
+ filePath := filepath.Join(s.dataPath, "groups.json")
+ data, err := json.MarshalIndent(s.groups, "", " ")
+ if err != nil {
+ return err
+ }
+ return os.WriteFile(filePath, data, 0644)
+}
+
+// cleanOldRecords 清理过期记录
+func (s *Storage) cleanOldRecords() {
+ cfg := config.GetConfig()
+ cutoff := time.Now().AddDate(0, 0, -cfg.Monitor.HistoryDays)
+
+ for key, records := range s.records {
+ var newRecords []models.MonitorRecord
+ for _, r := range records {
+ if r.CheckedAt.After(cutoff) {
+ newRecords = append(newRecords, r)
+ }
+ }
+ s.records[key] = newRecords
+ }
+}
+
+// GetWebsites 获取所有网站
+func (s *Storage) GetWebsites() []models.Website {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+
+ result := make([]models.Website, len(s.websites))
+ copy(result, s.websites)
+ return result
+}
+
+// GetWebsite 获取单个网站
+func (s *Storage) GetWebsite(id string) *models.Website {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+
+ for _, w := range s.websites {
+ if w.ID == id {
+ website := w
+ return &website
+ }
+ }
+ return nil
+}
+
+// AddWebsite 添加网站
+func (s *Storage) AddWebsite(website models.Website) error {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ s.websites = append(s.websites, website)
+ return s.saveWebsites()
+}
+
+// UpdateWebsite 更新网站
+func (s *Storage) UpdateWebsite(website models.Website) error {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ for i, w := range s.websites {
+ if w.ID == website.ID {
+ s.websites[i] = website
+ return s.saveWebsites()
+ }
+ }
+ return nil
+}
+
+// DeleteWebsite 删除网站
+func (s *Storage) DeleteWebsite(id string) error {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ for i, w := range s.websites {
+ if w.ID == id {
+ s.websites = append(s.websites[:i], s.websites[i+1:]...)
+ // 删除相关记录
+ for key := range s.records {
+ if len(key) > len(id) && key[:len(id)] == id {
+ delete(s.records, key)
+ }
+ }
+ s.saveRecords()
+ return s.saveWebsites()
+ }
+ }
+ return nil
+}
+
+// AddRecord 添加监控记录
+func (s *Storage) AddRecord(record models.MonitorRecord) error {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ key := record.WebsiteID + "_" + record.URLID
+ s.records[key] = append(s.records[key], record)
+
+ // 每100条记录保存一次
+ if len(s.records[key])%100 == 0 {
+ return s.saveRecords()
+ }
+ return nil
+}
+
+// GetRecords 获取监控记录
+func (s *Storage) GetRecords(websiteID, urlID string, since time.Time) []models.MonitorRecord {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+
+ key := websiteID + "_" + urlID
+ records := s.records[key]
+
+ var result []models.MonitorRecord
+ for _, r := range records {
+ if r.CheckedAt.After(since) {
+ result = append(result, r)
+ }
+ }
+ return result
+}
+
+// GetLatestRecord 获取最新记录
+func (s *Storage) GetLatestRecord(websiteID, urlID string) *models.MonitorRecord {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+
+ key := websiteID + "_" + urlID
+ records := s.records[key]
+
+ if len(records) == 0 {
+ return nil
+ }
+
+ latest := records[len(records)-1]
+ return &latest
+}
+
+// GetGroups 获取所有分组
+func (s *Storage) GetGroups() []models.Group {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+
+ result := make([]models.Group, len(s.groups))
+ copy(result, s.groups)
+ return result
+}
+
+// AddGroup 添加分组
+func (s *Storage) AddGroup(group models.Group) error {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ s.groups = append(s.groups, group)
+ return s.saveGroups()
+}
+
+// SaveAll 保存所有数据
+func (s *Storage) SaveAll() error {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ if err := s.saveWebsites(); err != nil {
+ return err
+ }
+ if err := s.saveRecords(); err != nil {
+ return err
+ }
+ return s.saveGroups()
+}
diff --git a/mengyaping-backend/utils/http.go b/mengyaping-backend/utils/http.go
new file mode 100644
index 0000000..a398703
--- /dev/null
+++ b/mengyaping-backend/utils/http.go
@@ -0,0 +1,131 @@
+package utils
+
+import (
+ "crypto/tls"
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "regexp"
+ "strings"
+ "time"
+)
+
+// HTTPClient HTTP客户端工具
+type HTTPClient struct {
+ client *http.Client
+}
+
+// NewHTTPClient 创建HTTP客户端
+func NewHTTPClient(timeout time.Duration) *HTTPClient {
+ return &HTTPClient{
+ client: &http.Client{
+ Timeout: timeout,
+ Transport: &http.Transport{
+ TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
+ },
+ CheckRedirect: func(req *http.Request, via []*http.Request) error {
+ if len(via) >= 10 {
+ return fmt.Errorf("too many redirects")
+ }
+ return nil
+ },
+ },
+ }
+}
+
+// CheckResult 检查结果
+type CheckResult struct {
+ StatusCode int
+ Latency time.Duration
+ Title string
+ Favicon string
+ Error error
+}
+
+// CheckWebsite 检查网站
+func (c *HTTPClient) CheckWebsite(targetURL string) CheckResult {
+ result := CheckResult{}
+
+ start := time.Now()
+
+ req, err := http.NewRequest("GET", targetURL, nil)
+ if err != nil {
+ result.Error = err
+ return result
+ }
+
+ req.Header.Set("User-Agent", "MengYaPing/1.0 (Website Monitor)")
+ req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
+
+ resp, err := c.client.Do(req)
+ if err != nil {
+ result.Error = err
+ result.Latency = time.Since(start)
+ return result
+ }
+ defer resp.Body.Close()
+
+ result.Latency = time.Since(start)
+ result.StatusCode = resp.StatusCode
+
+ // 读取响应体获取标题
+ body, err := io.ReadAll(io.LimitReader(resp.Body, 1024*100)) // 限制100KB
+ if err == nil {
+ result.Title = extractTitle(string(body))
+ result.Favicon = extractFavicon(string(body), targetURL)
+ }
+
+ return result
+}
+
+// extractTitle 提取网页标题
+func extractTitle(html string) string {
+ re := regexp.MustCompile(`(?i)
]*>([^<]+)`)
+ matches := re.FindStringSubmatch(html)
+ if len(matches) > 1 {
+ return strings.TrimSpace(matches[1])
+ }
+ return ""
+}
+
+// extractFavicon 提取Favicon
+func extractFavicon(html string, baseURL string) string {
+ parsedURL, err := url.Parse(baseURL)
+ if err != nil {
+ return ""
+ }
+
+ // 尝试从HTML中提取favicon链接
+ patterns := []string{
+ `(?i)]*rel=["'](?:shortcut )?icon["'][^>]*href=["']([^"']+)["']`,
+ `(?i)]*href=["']([^"']+)["'][^>]*rel=["'](?:shortcut )?icon["']`,
+ `(?i)]*rel=["']apple-touch-icon["'][^>]*href=["']([^"']+)["']`,
+ }
+
+ for _, pattern := range patterns {
+ re := regexp.MustCompile(pattern)
+ matches := re.FindStringSubmatch(html)
+ if len(matches) > 1 {
+ faviconURL := matches[1]
+ return resolveURL(parsedURL, faviconURL)
+ }
+ }
+
+ // 默认返回 /favicon.ico
+ return fmt.Sprintf("%s://%s/favicon.ico", parsedURL.Scheme, parsedURL.Host)
+}
+
+// resolveURL 解析相对URL
+func resolveURL(base *url.URL, ref string) string {
+ refURL, err := url.Parse(ref)
+ if err != nil {
+ return ref
+ }
+ return base.ResolveReference(refURL).String()
+}
+
+// IsSuccessStatus 判断是否为成功状态码
+func IsSuccessStatus(statusCode int) bool {
+ return statusCode >= 200 && statusCode < 400
+}
diff --git a/mengyaping-backend/utils/id.go b/mengyaping-backend/utils/id.go
new file mode 100644
index 0000000..09fa6b7
--- /dev/null
+++ b/mengyaping-backend/utils/id.go
@@ -0,0 +1,31 @@
+package utils
+
+import (
+ "crypto/rand"
+ "encoding/hex"
+ "time"
+)
+
+// GenerateID 生成唯一ID
+func GenerateID() string {
+ timestamp := time.Now().UnixNano()
+ randomBytes := make([]byte, 4)
+ rand.Read(randomBytes)
+ return hex.EncodeToString([]byte{
+ byte(timestamp >> 56),
+ byte(timestamp >> 48),
+ byte(timestamp >> 40),
+ byte(timestamp >> 32),
+ byte(timestamp >> 24),
+ byte(timestamp >> 16),
+ byte(timestamp >> 8),
+ byte(timestamp),
+ }) + hex.EncodeToString(randomBytes)
+}
+
+// GenerateShortID 生成短ID
+func GenerateShortID() string {
+ randomBytes := make([]byte, 6)
+ rand.Read(randomBytes)
+ return hex.EncodeToString(randomBytes)
+}
diff --git a/mengyaping-frontend/.gitignore b/mengyaping-frontend/.gitignore
new file mode 100644
index 0000000..a547bf3
--- /dev/null
+++ b/mengyaping-frontend/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/mengyaping-frontend/README.md b/mengyaping-frontend/README.md
new file mode 100644
index 0000000..18bc70e
--- /dev/null
+++ b/mengyaping-frontend/README.md
@@ -0,0 +1,16 @@
+# React + Vite
+
+This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
+
+Currently, two official plugins are available:
+
+- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh
+- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
+
+## React Compiler
+
+The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
+
+## Expanding the ESLint configuration
+
+If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project.
diff --git a/mengyaping-frontend/eslint.config.js b/mengyaping-frontend/eslint.config.js
new file mode 100644
index 0000000..4fa125d
--- /dev/null
+++ b/mengyaping-frontend/eslint.config.js
@@ -0,0 +1,29 @@
+import js from '@eslint/js'
+import globals from 'globals'
+import reactHooks from 'eslint-plugin-react-hooks'
+import reactRefresh from 'eslint-plugin-react-refresh'
+import { defineConfig, globalIgnores } from 'eslint/config'
+
+export default defineConfig([
+ globalIgnores(['dist']),
+ {
+ files: ['**/*.{js,jsx}'],
+ extends: [
+ js.configs.recommended,
+ reactHooks.configs.flat.recommended,
+ reactRefresh.configs.vite,
+ ],
+ languageOptions: {
+ ecmaVersion: 2020,
+ globals: globals.browser,
+ parserOptions: {
+ ecmaVersion: 'latest',
+ ecmaFeatures: { jsx: true },
+ sourceType: 'module',
+ },
+ },
+ rules: {
+ 'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }],
+ },
+ },
+])
diff --git a/mengyaping-frontend/index.html b/mengyaping-frontend/index.html
new file mode 100644
index 0000000..862c185
--- /dev/null
+++ b/mengyaping-frontend/index.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+ 萌芽Ping - 网站监控面板
+
+
+
+
+
+
+
+
+
+
diff --git a/mengyaping-frontend/package-lock.json b/mengyaping-frontend/package-lock.json
new file mode 100644
index 0000000..a3edba7
--- /dev/null
+++ b/mengyaping-frontend/package-lock.json
@@ -0,0 +1,3492 @@
+{
+ "name": "mengyaping-frontend",
+ "version": "0.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "mengyaping-frontend",
+ "version": "0.0.0",
+ "dependencies": {
+ "@tailwindcss/vite": "^4.1.18",
+ "react": "^19.2.0",
+ "react-dom": "^19.2.0"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.39.1",
+ "@types/react": "^19.2.5",
+ "@types/react-dom": "^19.2.3",
+ "@vitejs/plugin-react": "^5.1.1",
+ "autoprefixer": "^10.4.23",
+ "eslint": "^9.39.1",
+ "eslint-plugin-react-hooks": "^7.0.1",
+ "eslint-plugin-react-refresh": "^0.4.24",
+ "globals": "^16.5.0",
+ "postcss": "^8.5.6",
+ "tailwindcss": "^4.1.18",
+ "vite": "^7.2.4"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz",
+ "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.6.tgz",
+ "integrity": "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz",
+ "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.28.6",
+ "@babel/generator": "^7.28.6",
+ "@babel/helper-compilation-targets": "^7.28.6",
+ "@babel/helper-module-transforms": "^7.28.6",
+ "@babel/helpers": "^7.28.6",
+ "@babel/parser": "^7.28.6",
+ "@babel/template": "^7.28.6",
+ "@babel/traverse": "^7.28.6",
+ "@babel/types": "^7.28.6",
+ "@jridgewell/remapping": "^2.3.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz",
+ "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.28.6",
+ "@babel/types": "^7.28.6",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz",
+ "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.28.6",
+ "@babel/helper-validator-option": "^7.27.1",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
+ "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz",
+ "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.28.6",
+ "@babel/types": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz",
+ "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.28.6",
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "@babel/traverse": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz",
+ "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
+ "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz",
+ "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.28.6",
+ "@babel/types": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz",
+ "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.6"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-self": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz",
+ "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-source": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz",
+ "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
+ "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.28.6",
+ "@babel/parser": "^7.28.6",
+ "@babel/types": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.6.tgz",
+ "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.28.6",
+ "@babel/generator": "^7.28.6",
+ "@babel/helper-globals": "^7.28.0",
+ "@babel/parser": "^7.28.6",
+ "@babel/template": "^7.28.6",
+ "@babel/types": "^7.28.6",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz",
+ "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz",
+ "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz",
+ "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz",
+ "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz",
+ "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz",
+ "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz",
+ "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz",
+ "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz",
+ "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz",
+ "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz",
+ "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz",
+ "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz",
+ "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==",
+ "cpu": [
+ "loong64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz",
+ "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz",
+ "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz",
+ "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz",
+ "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz",
+ "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz",
+ "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz",
+ "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz",
+ "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz",
+ "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz",
+ "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz",
+ "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz",
+ "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz",
+ "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz",
+ "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.9.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
+ "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.21.1",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
+ "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.7",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
+ "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
+ "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz",
+ "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.1",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.39.2",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz",
+ "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
+ "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
+ "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@humanfs/core": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.7",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
+ "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.1",
+ "@humanwhocodes/retry": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-beta.53",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz",
+ "integrity": "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.55.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.3.tgz",
+ "integrity": "sha512-qyX8+93kK/7R5BEXPC2PjUt0+fS/VO2BVHjEHyIEWiYn88rcRBHmdLgoJjktBltgAf+NY7RfCGB1SoyKS/p9kg==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.55.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.55.3.tgz",
+ "integrity": "sha512-6sHrL42bjt5dHQzJ12Q4vMKfN+kUnZ0atHHnv4V0Wd9JMTk7FDzSY35+7qbz3ypQYMBPANbpGK7JpnWNnhGt8g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.55.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.55.3.tgz",
+ "integrity": "sha512-1ht2SpGIjEl2igJ9AbNpPIKzb1B5goXOcmtD0RFxnwNuMxqkR6AUaaErZz+4o+FKmzxcSNBOLrzsICZVNYa1Rw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.55.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.55.3.tgz",
+ "integrity": "sha512-FYZ4iVunXxtT+CZqQoPVwPhH7549e/Gy7PIRRtq4t5f/vt54pX6eG9ebttRH6QSH7r/zxAFA4EZGlQ0h0FvXiA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.55.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.55.3.tgz",
+ "integrity": "sha512-M/mwDCJ4wLsIgyxv2Lj7Len+UMHd4zAXu4GQ2UaCdksStglWhP61U3uowkaYBQBhVoNpwx5Hputo8eSqM7K82Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.55.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.55.3.tgz",
+ "integrity": "sha512-5jZT2c7jBCrMegKYTYTpni8mg8y3uY8gzeq2ndFOANwNuC/xJbVAoGKR9LhMDA0H3nIhvaqUoBEuJoICBudFrA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.55.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.55.3.tgz",
+ "integrity": "sha512-YeGUhkN1oA+iSPzzhEjVPS29YbViOr8s4lSsFaZKLHswgqP911xx25fPOyE9+khmN6W4VeM0aevbDp4kkEoHiA==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.55.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.55.3.tgz",
+ "integrity": "sha512-eo0iOIOvcAlWB3Z3eh8pVM8hZ0oVkK3AjEM9nSrkSug2l15qHzF3TOwT0747omI6+CJJvl7drwZepT+re6Fy/w==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.55.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.55.3.tgz",
+ "integrity": "sha512-DJay3ep76bKUDImmn//W5SvpjRN5LmK/ntWyeJs/dcnwiiHESd3N4uteK9FDLf0S0W8E6Y0sVRXpOCoQclQqNg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.55.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.55.3.tgz",
+ "integrity": "sha512-BKKWQkY2WgJ5MC/ayvIJTHjy0JUGb5efaHCUiG/39sSUvAYRBaO3+/EK0AZT1RF3pSj86O24GLLik9mAYu0IJg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.55.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.55.3.tgz",
+ "integrity": "sha512-Q9nVlWtKAG7ISW80OiZGxTr6rYtyDSkauHUtvkQI6TNOJjFvpj4gcH+KaJihqYInnAzEEUetPQubRwHef4exVg==",
+ "cpu": [
+ "loong64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-musl": {
+ "version": "4.55.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.55.3.tgz",
+ "integrity": "sha512-2H5LmhzrpC4fFRNwknzmmTvvyJPHwESoJgyReXeFoYYuIDfBhP29TEXOkCJE/KxHi27mj7wDUClNq78ue3QEBQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.55.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.55.3.tgz",
+ "integrity": "sha512-9S542V0ie9LCTznPYlvaeySwBeIEa7rDBgLHKZ5S9DBgcqdJYburabm8TqiqG6mrdTzfV5uttQRHcbKff9lWtA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-musl": {
+ "version": "4.55.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.55.3.tgz",
+ "integrity": "sha512-ukxw+YH3XXpcezLgbJeasgxyTbdpnNAkrIlFGDl7t+pgCxZ89/6n1a+MxlY7CegU+nDgrgdqDelPRNQ/47zs0g==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.55.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.55.3.tgz",
+ "integrity": "sha512-Iauw9UsTTvlF++FhghFJjqYxyXdggXsOqGpFBylaRopVpcbfyIIsNvkf9oGwfgIcf57z3m8+/oSYTo6HutBFNw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.55.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.55.3.tgz",
+ "integrity": "sha512-3OqKAHSEQXKdq9mQ4eajqUgNIK27VZPW3I26EP8miIzuKzCJ3aW3oEn2pzF+4/Hj/Moc0YDsOtBgT5bZ56/vcA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.55.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.55.3.tgz",
+ "integrity": "sha512-0CM8dSVzVIaqMcXIFej8zZrSFLnGrAE8qlNbbHfTw1EEPnFTg1U1ekI0JdzjPyzSfUsHWtodilQQG/RA55berA==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.55.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.55.3.tgz",
+ "integrity": "sha512-+fgJE12FZMIgBaKIAGd45rxf+5ftcycANJRWk8Vz0NnMTM5rADPGuRFTYar+Mqs560xuART7XsX2lSACa1iOmQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.55.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.55.3.tgz",
+ "integrity": "sha512-tMD7NnbAolWPzQlJQJjVFh/fNH3K/KnA7K8gv2dJWCwwnaK6DFCYST1QXYWfu5V0cDwarWC8Sf/cfMHniNq21A==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openbsd-x64": {
+ "version": "4.55.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.55.3.tgz",
+ "integrity": "sha512-u5KsqxOxjEeIbn7bUK1MPM34jrnPwjeqgyin4/N6e/KzXKfpE9Mi0nCxcQjaM9lLmPcHmn/xx1yOjgTMtu1jWQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.55.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.55.3.tgz",
+ "integrity": "sha512-vo54aXwjpTtsAnb3ca7Yxs9t2INZg7QdXN/7yaoG7nPGbOBXYXQY41Km+S1Ov26vzOAzLcAjmMdjyEqS1JkVhw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.55.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.55.3.tgz",
+ "integrity": "sha512-HI+PIVZ+m+9AgpnY3pt6rinUdRYrGHvmVdsNQ4odNqQ/eRF78DVpMR7mOq7nW06QxpczibwBmeQzB68wJ+4W4A==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.55.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.55.3.tgz",
+ "integrity": "sha512-vRByotbdMo3Wdi+8oC2nVxtc3RkkFKrGaok+a62AT8lz/YBuQjaVYAS5Zcs3tPzW43Vsf9J0wehJbUY5xRSekA==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.55.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.55.3.tgz",
+ "integrity": "sha512-POZHq7UeuzMJljC5NjKi8vKMFN6/5EOqcX1yGntNLp7rUTpBAXQ1hW8kWPFxYLv07QMcNM75xqVLGPWQq6TKFA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.55.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.55.3.tgz",
+ "integrity": "sha512-aPFONczE4fUFKNXszdvnd2GqKEYQdV5oEsIbKPujJmWlCI9zEsv1Otig8RKK+X9bed9gFUN6LAeN4ZcNuu4zjg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@tailwindcss/node": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz",
+ "integrity": "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/remapping": "^2.3.4",
+ "enhanced-resolve": "^5.18.3",
+ "jiti": "^2.6.1",
+ "lightningcss": "1.30.2",
+ "magic-string": "^0.30.21",
+ "source-map-js": "^1.2.1",
+ "tailwindcss": "4.1.18"
+ }
+ },
+ "node_modules/@tailwindcss/oxide": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.18.tgz",
+ "integrity": "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ },
+ "optionalDependencies": {
+ "@tailwindcss/oxide-android-arm64": "4.1.18",
+ "@tailwindcss/oxide-darwin-arm64": "4.1.18",
+ "@tailwindcss/oxide-darwin-x64": "4.1.18",
+ "@tailwindcss/oxide-freebsd-x64": "4.1.18",
+ "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18",
+ "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18",
+ "@tailwindcss/oxide-linux-arm64-musl": "4.1.18",
+ "@tailwindcss/oxide-linux-x64-gnu": "4.1.18",
+ "@tailwindcss/oxide-linux-x64-musl": "4.1.18",
+ "@tailwindcss/oxide-wasm32-wasi": "4.1.18",
+ "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18",
+ "@tailwindcss/oxide-win32-x64-msvc": "4.1.18"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-android-arm64": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.18.tgz",
+ "integrity": "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-arm64": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.18.tgz",
+ "integrity": "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-x64": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.18.tgz",
+ "integrity": "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-freebsd-x64": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.18.tgz",
+ "integrity": "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.18.tgz",
+ "integrity": "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.18.tgz",
+ "integrity": "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-musl": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.18.tgz",
+ "integrity": "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-gnu": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.18.tgz",
+ "integrity": "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-musl": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.18.tgz",
+ "integrity": "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-wasm32-wasi": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.18.tgz",
+ "integrity": "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==",
+ "bundleDependencies": [
+ "@napi-rs/wasm-runtime",
+ "@emnapi/core",
+ "@emnapi/runtime",
+ "@tybys/wasm-util",
+ "@emnapi/wasi-threads",
+ "tslib"
+ ],
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.7.1",
+ "@emnapi/runtime": "^1.7.1",
+ "@emnapi/wasi-threads": "^1.1.0",
+ "@napi-rs/wasm-runtime": "^1.1.0",
+ "@tybys/wasm-util": "^0.10.1",
+ "tslib": "^2.4.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.18.tgz",
+ "integrity": "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-x64-msvc": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.18.tgz",
+ "integrity": "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/vite": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.18.tgz",
+ "integrity": "sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA==",
+ "license": "MIT",
+ "dependencies": {
+ "@tailwindcss/node": "4.1.18",
+ "@tailwindcss/oxide": "4.1.18",
+ "tailwindcss": "4.1.18"
+ },
+ "peerDependencies": {
+ "vite": "^5.2.0 || ^6 || ^7"
+ }
+ },
+ "node_modules/@types/babel__core": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "node_modules/@types/babel__generator": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
+ "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__template": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__traverse": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
+ "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.2"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "license": "MIT"
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/react": {
+ "version": "19.2.9",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.9.tgz",
+ "integrity": "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "csstype": "^3.2.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.2.0"
+ }
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.2.tgz",
+ "integrity": "sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.28.5",
+ "@babel/plugin-transform-react-jsx-self": "^7.27.1",
+ "@babel/plugin-transform-react-jsx-source": "^7.27.1",
+ "@rolldown/pluginutils": "1.0.0-beta.53",
+ "@types/babel__core": "^7.20.5",
+ "react-refresh": "^0.18.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "peerDependencies": {
+ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/autoprefixer": {
+ "version": "10.4.23",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.23.tgz",
+ "integrity": "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/autoprefixer"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.28.1",
+ "caniuse-lite": "^1.0.30001760",
+ "fraction.js": "^5.3.4",
+ "picocolors": "^1.1.1",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "bin": {
+ "autoprefixer": "bin/autoprefixer"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.9.16",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.16.tgz",
+ "integrity": "sha512-KeUZdBuxngy825i8xvzaK1Ncnkx0tBmb3k8DkEuqjKRkmtvNTjey2ZsNeh8Dw4lfKvbCOu9oeNx2TKm2vHqcRw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.js"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.1",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
+ "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "baseline-browser-mapping": "^2.9.0",
+ "caniuse-lite": "^1.0.30001759",
+ "electron-to-chromium": "^1.5.263",
+ "node-releases": "^2.0.27",
+ "update-browserslist-db": "^1.2.0"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001765",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001765.tgz",
+ "integrity": "sha512-LWcNtSyZrakjECqmpP4qdg0MMGdN368D7X8XvvAqOcqMv0RxnlqVKZl2V6/mBR68oYMxOZPLw/gO7DuisMHUvQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.267",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz",
+ "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/enhanced-resolve": {
+ "version": "5.18.4",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz",
+ "integrity": "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==",
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz",
+ "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.27.2",
+ "@esbuild/android-arm": "0.27.2",
+ "@esbuild/android-arm64": "0.27.2",
+ "@esbuild/android-x64": "0.27.2",
+ "@esbuild/darwin-arm64": "0.27.2",
+ "@esbuild/darwin-x64": "0.27.2",
+ "@esbuild/freebsd-arm64": "0.27.2",
+ "@esbuild/freebsd-x64": "0.27.2",
+ "@esbuild/linux-arm": "0.27.2",
+ "@esbuild/linux-arm64": "0.27.2",
+ "@esbuild/linux-ia32": "0.27.2",
+ "@esbuild/linux-loong64": "0.27.2",
+ "@esbuild/linux-mips64el": "0.27.2",
+ "@esbuild/linux-ppc64": "0.27.2",
+ "@esbuild/linux-riscv64": "0.27.2",
+ "@esbuild/linux-s390x": "0.27.2",
+ "@esbuild/linux-x64": "0.27.2",
+ "@esbuild/netbsd-arm64": "0.27.2",
+ "@esbuild/netbsd-x64": "0.27.2",
+ "@esbuild/openbsd-arm64": "0.27.2",
+ "@esbuild/openbsd-x64": "0.27.2",
+ "@esbuild/openharmony-arm64": "0.27.2",
+ "@esbuild/sunos-x64": "0.27.2",
+ "@esbuild/win32-arm64": "0.27.2",
+ "@esbuild/win32-ia32": "0.27.2",
+ "@esbuild/win32-x64": "0.27.2"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "9.39.2",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz",
+ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.8.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.21.1",
+ "@eslint/config-helpers": "^0.4.2",
+ "@eslint/core": "^0.17.0",
+ "@eslint/eslintrc": "^3.3.1",
+ "@eslint/js": "9.39.2",
+ "@eslint/plugin-kit": "^0.4.1",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.4.0",
+ "eslint-visitor-keys": "^4.2.1",
+ "espree": "^10.4.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-react-hooks": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz",
+ "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.24.4",
+ "@babel/parser": "^7.24.4",
+ "hermes-parser": "^0.25.1",
+ "zod": "^3.25.0 || ^4.0.0",
+ "zod-validation-error": "^3.5.0 || ^4.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-react-refresh": {
+ "version": "0.4.26",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.26.tgz",
+ "integrity": "sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "eslint": ">=8.40"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
+ "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.15.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
+ "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
+ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/fraction.js": {
+ "version": "5.3.4",
+ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
+ "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/rawify"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "16.5.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz",
+ "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "license": "ISC"
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/hermes-estree": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
+ "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/hermes-parser": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz",
+ "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hermes-estree": "0.25.1"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/jiti": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
+ "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
+ "license": "MIT",
+ "bin": {
+ "jiti": "lib/jiti-cli.mjs"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/lightningcss": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
+ "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==",
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-android-arm64": "1.30.2",
+ "lightningcss-darwin-arm64": "1.30.2",
+ "lightningcss-darwin-x64": "1.30.2",
+ "lightningcss-freebsd-x64": "1.30.2",
+ "lightningcss-linux-arm-gnueabihf": "1.30.2",
+ "lightningcss-linux-arm64-gnu": "1.30.2",
+ "lightningcss-linux-arm64-musl": "1.30.2",
+ "lightningcss-linux-x64-gnu": "1.30.2",
+ "lightningcss-linux-x64-musl": "1.30.2",
+ "lightningcss-win32-arm64-msvc": "1.30.2",
+ "lightningcss-win32-x64-msvc": "1.30.2"
+ }
+ },
+ "node_modules/lightningcss-android-arm64": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz",
+ "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-arm64": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz",
+ "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-x64": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz",
+ "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-freebsd-x64": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz",
+ "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz",
+ "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz",
+ "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz",
+ "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz",
+ "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz",
+ "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz",
+ "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz",
+ "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.21",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.5"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.27",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
+ "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.6",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/react": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
+ "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.27.0"
+ },
+ "peerDependencies": {
+ "react": "^19.2.3"
+ }
+ },
+ "node_modules/react-refresh": {
+ "version": "0.18.0",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz",
+ "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.55.3",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.55.3.tgz",
+ "integrity": "sha512-y9yUpfQvetAjiDLtNMf1hL9NXchIJgWt6zIKeoB+tCd3npX08Eqfzg60V9DhIGVMtQ0AlMkFw5xa+AQ37zxnAA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.8"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.55.3",
+ "@rollup/rollup-android-arm64": "4.55.3",
+ "@rollup/rollup-darwin-arm64": "4.55.3",
+ "@rollup/rollup-darwin-x64": "4.55.3",
+ "@rollup/rollup-freebsd-arm64": "4.55.3",
+ "@rollup/rollup-freebsd-x64": "4.55.3",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.55.3",
+ "@rollup/rollup-linux-arm-musleabihf": "4.55.3",
+ "@rollup/rollup-linux-arm64-gnu": "4.55.3",
+ "@rollup/rollup-linux-arm64-musl": "4.55.3",
+ "@rollup/rollup-linux-loong64-gnu": "4.55.3",
+ "@rollup/rollup-linux-loong64-musl": "4.55.3",
+ "@rollup/rollup-linux-ppc64-gnu": "4.55.3",
+ "@rollup/rollup-linux-ppc64-musl": "4.55.3",
+ "@rollup/rollup-linux-riscv64-gnu": "4.55.3",
+ "@rollup/rollup-linux-riscv64-musl": "4.55.3",
+ "@rollup/rollup-linux-s390x-gnu": "4.55.3",
+ "@rollup/rollup-linux-x64-gnu": "4.55.3",
+ "@rollup/rollup-linux-x64-musl": "4.55.3",
+ "@rollup/rollup-openbsd-x64": "4.55.3",
+ "@rollup/rollup-openharmony-arm64": "4.55.3",
+ "@rollup/rollup-win32-arm64-msvc": "4.55.3",
+ "@rollup/rollup-win32-ia32-msvc": "4.55.3",
+ "@rollup/rollup-win32-x64-gnu": "4.55.3",
+ "@rollup/rollup-win32-x64-msvc": "4.55.3",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/tailwindcss": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz",
+ "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==",
+ "license": "MIT"
+ },
+ "node_modules/tapable": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
+ "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.15",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/vite": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
+ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "esbuild": "^0.27.0",
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3",
+ "postcss": "^8.5.6",
+ "rollup": "^4.43.0",
+ "tinyglobby": "^0.2.15"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "lightningcss": "^1.21.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/zod": {
+ "version": "4.3.5",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.5.tgz",
+ "integrity": "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/zod-validation-error": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz",
+ "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.0 || ^4.0.0"
+ }
+ }
+ }
+}
diff --git a/mengyaping-frontend/package.json b/mengyaping-frontend/package.json
new file mode 100644
index 0000000..9f9aea2
--- /dev/null
+++ b/mengyaping-frontend/package.json
@@ -0,0 +1,31 @@
+{
+ "name": "mengyaping-frontend",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "lint": "eslint .",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@tailwindcss/vite": "^4.1.18",
+ "react": "^19.2.0",
+ "react-dom": "^19.2.0"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.39.1",
+ "@types/react": "^19.2.5",
+ "@types/react-dom": "^19.2.3",
+ "@vitejs/plugin-react": "^5.1.1",
+ "autoprefixer": "^10.4.23",
+ "eslint": "^9.39.1",
+ "eslint-plugin-react-hooks": "^7.0.1",
+ "eslint-plugin-react-refresh": "^0.4.24",
+ "globals": "^16.5.0",
+ "postcss": "^8.5.6",
+ "tailwindcss": "^4.1.18",
+ "vite": "^7.2.4"
+ }
+}
diff --git a/mengyaping-frontend/public/favicon.ico b/mengyaping-frontend/public/favicon.ico
new file mode 100644
index 0000000..cad9fd1
Binary files /dev/null and b/mengyaping-frontend/public/favicon.ico differ
diff --git a/mengyaping-frontend/public/logo.png b/mengyaping-frontend/public/logo.png
new file mode 100644
index 0000000..09fc7dc
Binary files /dev/null and b/mengyaping-frontend/public/logo.png differ
diff --git a/mengyaping-frontend/src/App.css b/mengyaping-frontend/src/App.css
new file mode 100644
index 0000000..b9d355d
--- /dev/null
+++ b/mengyaping-frontend/src/App.css
@@ -0,0 +1,42 @@
+#root {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 2rem;
+ text-align: center;
+}
+
+.logo {
+ height: 6em;
+ padding: 1.5em;
+ will-change: filter;
+ transition: filter 300ms;
+}
+.logo:hover {
+ filter: drop-shadow(0 0 2em #646cffaa);
+}
+.logo.react:hover {
+ filter: drop-shadow(0 0 2em #61dafbaa);
+}
+
+@keyframes logo-spin {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+@media (prefers-reduced-motion: no-preference) {
+ a:nth-of-type(2) .logo {
+ animation: logo-spin infinite 20s linear;
+ }
+}
+
+.card {
+ padding: 2em;
+}
+
+.read-the-docs {
+ color: #888;
+}
diff --git a/mengyaping-frontend/src/App.jsx b/mengyaping-frontend/src/App.jsx
new file mode 100644
index 0000000..d7994b8
--- /dev/null
+++ b/mengyaping-frontend/src/App.jsx
@@ -0,0 +1,7 @@
+import Dashboard from './pages/Dashboard'
+
+function App() {
+ return
+}
+
+export default App
diff --git a/mengyaping-frontend/src/components/StatsCard.jsx b/mengyaping-frontend/src/components/StatsCard.jsx
new file mode 100644
index 0000000..d9adc0f
--- /dev/null
+++ b/mengyaping-frontend/src/components/StatsCard.jsx
@@ -0,0 +1,145 @@
+import { formatUptime, getUptimeColor } from '../hooks/useMonitor';
+
+// 统计概览卡片
+export default function StatsCard({ websites }) {
+ // 计算统计数据
+ const stats = {
+ total: websites?.length || 0,
+ online: 0,
+ offline: 0,
+ avgUptime24h: 0,
+ avgUptime7d: 0,
+ avgLatency: 0,
+ };
+
+ if (websites && websites.length > 0) {
+ let totalUptime24h = 0;
+ let totalUptime7d = 0;
+ let totalLatency = 0;
+ let latencyCount = 0;
+
+ websites.forEach(site => {
+ // 检查所有URL的状态
+ const hasOnlineUrl = site.url_statuses?.some(us => us.current_state?.is_up);
+ if (hasOnlineUrl) {
+ stats.online++;
+ } else {
+ stats.offline++;
+ }
+
+ totalUptime24h += site.uptime_24h || 0;
+ totalUptime7d += site.uptime_7d || 0;
+
+ site.url_statuses?.forEach(us => {
+ if (us.current_state?.latency) {
+ totalLatency += us.current_state.latency;
+ latencyCount++;
+ }
+ });
+ });
+
+ stats.avgUptime24h = totalUptime24h / stats.total;
+ stats.avgUptime7d = totalUptime7d / stats.total;
+ stats.avgLatency = latencyCount > 0 ? Math.round(totalLatency / latencyCount) : 0;
+ }
+
+ return (
+
+ {/* 监控网站数 */}
+
+
+
+
+
+ {stats.online} 在线
+
+ |
+
+
+ {stats.offline} 离线
+
+
+
+
+ {/* 24小时可用率 */}
+
+
+
+
24h 可用率
+
+ {formatUptime(stats.avgUptime24h)}
+
+
+
+
+
+
+
+ {/* 7天可用率 */}
+
+
+
+
7d 可用率
+
+ {formatUptime(stats.avgUptime7d)}
+
+
+
+
+
+
+
+ {/* 平均延迟 */}
+
+
+
+
平均延迟
+
+ {stats.avgLatency}
+ ms
+
+
+
+
+
+
+
+ );
+}
diff --git a/mengyaping-frontend/src/components/UptimeChart.jsx b/mengyaping-frontend/src/components/UptimeChart.jsx
new file mode 100644
index 0000000..e533494
--- /dev/null
+++ b/mengyaping-frontend/src/components/UptimeChart.jsx
@@ -0,0 +1,212 @@
+import { useMemo } from 'react';
+
+// 简易折线图组件
+export default function UptimeChart({ data, height = 120, showLabels = true }) {
+ // 处理数据
+ const chartData = useMemo(() => {
+ if (!data || data.length === 0) {
+ // 生成模拟数据点
+ return Array(24).fill(null).map((_, i) => ({
+ hour: i,
+ uptime: null,
+ avgLatency: 0,
+ }));
+ }
+
+ // 按时间排序
+ const sorted = [...data].sort((a, b) =>
+ new Date(a.hour).getTime() - new Date(b.hour).getTime()
+ );
+
+ return sorted.map(item => ({
+ hour: new Date(item.hour).getHours(),
+ uptime: item.uptime,
+ avgLatency: item.avg_latency,
+ }));
+ }, [data]);
+
+ // 计算图表尺寸
+ const padding = { top: 20, right: 20, bottom: showLabels ? 30 : 10, left: showLabels ? 40 : 10 };
+ const width = 400;
+ const chartWidth = width - padding.left - padding.right;
+ const chartHeight = height - padding.top - padding.bottom;
+
+ // 生成路径
+ const pathData = useMemo(() => {
+ const validPoints = chartData.filter(d => d.uptime !== null);
+ if (validPoints.length === 0) return '';
+
+ const xStep = chartWidth / (chartData.length - 1 || 1);
+
+ let path = '';
+ let lastValidIndex = -1;
+
+ chartData.forEach((d, i) => {
+ if (d.uptime !== null) {
+ const x = i * xStep;
+ const y = chartHeight - (d.uptime / 100 * chartHeight);
+
+ if (lastValidIndex === -1) {
+ path += `M ${x} ${y}`;
+ } else {
+ path += ` L ${x} ${y}`;
+ }
+ lastValidIndex = i;
+ }
+ });
+
+ return path;
+ }, [chartData, chartWidth, chartHeight]);
+
+ // 生成填充区域
+ const areaPath = useMemo(() => {
+ if (!pathData) return '';
+
+ const validPoints = chartData.filter(d => d.uptime !== null);
+ if (validPoints.length === 0) return '';
+
+ const xStep = chartWidth / (chartData.length - 1 || 1);
+ const firstValidIndex = chartData.findIndex(d => d.uptime !== null);
+ const lastValidIndex = chartData.length - 1 - [...chartData].reverse().findIndex(d => d.uptime !== null);
+
+ const startX = firstValidIndex * xStep;
+ const endX = lastValidIndex * xStep;
+
+ return `${pathData} L ${endX} ${chartHeight} L ${startX} ${chartHeight} Z`;
+ }, [pathData, chartData, chartWidth, chartHeight]);
+
+ // 获取颜色
+ const getColor = (uptime) => {
+ if (uptime >= 99) return '#10b981'; // emerald-500
+ if (uptime >= 95) return '#34d399'; // emerald-400
+ if (uptime >= 90) return '#fbbf24'; // amber-400
+ return '#ef4444'; // red-500
+ };
+
+ // 计算平均可用率
+ const avgUptime = useMemo(() => {
+ const validPoints = chartData.filter(d => d.uptime !== null);
+ if (validPoints.length === 0) return null;
+ return validPoints.reduce((sum, d) => sum + d.uptime, 0) / validPoints.length;
+ }, [chartData]);
+
+ const color = avgUptime !== null ? getColor(avgUptime) : '#d1d5db';
+
+ return (
+
+
+
+ );
+}
diff --git a/mengyaping-frontend/src/components/WebsiteCard.jsx b/mengyaping-frontend/src/components/WebsiteCard.jsx
new file mode 100644
index 0000000..2a9346c
--- /dev/null
+++ b/mengyaping-frontend/src/components/WebsiteCard.jsx
@@ -0,0 +1,207 @@
+import { useState } from 'react';
+import { formatLatency, formatUptime, formatTime, getStatusColor, getUptimeColor, getLatencyColor } from '../hooks/useMonitor';
+
+// 网站状态卡片组件
+export default function WebsiteCard({ website, onRefresh, onEdit, onDelete }) {
+ const [expanded, setExpanded] = useState(false);
+
+ // 获取第一个URL的状态作为主状态
+ const primaryStatus = website.url_statuses?.[0];
+ const isUp = primaryStatus?.current_state?.is_up ?? false;
+ const statusCode = primaryStatus?.current_state?.status_code ?? 0;
+ const latency = primaryStatus?.current_state?.latency ?? 0;
+ const primaryUrl = website.website?.urls?.[0]?.url || website.url_statuses?.[0]?.url_info?.url;
+
+ return (
+
+ {/* 卡片头部 */}
+
setExpanded(!expanded)}
+ >
+ {/* 第一行:图标、名称、状态 */}
+
+
+ {/* Favicon */}
+
+ {website.website?.favicon ? (
+

{
+ e.target.style.display = 'none';
+ e.target.nextSibling.style.display = 'flex';
+ }}
+ />
+ ) : null}
+
+ {website.website?.name?.[0] || '?'}
+
+
+
+ {/* 网站名称 */}
+
+ {website.website?.name || '未知网站'}
+
+
+
+ {/* 展开箭头 */}
+
+
+
+ {/* 第二行:状态、延迟、访问按钮 */}
+
+
+ {/* 状态徽章 */}
+
+ {isUp ? `${statusCode}` : '离线'}
+
+
+ {/* 延迟 */}
+
+ {formatLatency(latency)}
+
+
+
+ {/* 访问按钮 */}
+
+
+
+ {/* 网站描述 */}
+
+ {website.website?.title || website.website?.urls?.[0]?.url || '-'}
+
+
+ {/* 可用率条 */}
+
+
+ 24h可用率
+
+ {formatUptime(website.uptime_24h)}
+
+
+
+
+
+
+ {/* 展开详情 */}
+ {expanded && (
+
+ {/* URL列表 */}
+
+ {website.url_statuses?.map((urlStatus, index) => (
+
+
+
+ {urlStatus.url_info?.url}
+
+
+ {urlStatus.current_state?.is_up ? urlStatus.current_state?.status_code : '离线'}
+
+
+
+
+
+
延迟
+
+ {formatLatency(urlStatus.current_state?.latency)}
+
+
+
+
24h
+
+ {formatUptime(urlStatus.uptime_24h)}
+
+
+
+
7d
+
+ {formatUptime(urlStatus.uptime_7d)}
+
+
+
+
+ ))}
+
+
+ {/* 操作按钮 */}
+
+
+
+
+
+
+ {/* 最后检测时间 */}
+
+ 最后检测: {formatTime(website.last_checked)}
+
+
+ )}
+
+ );
+}
diff --git a/mengyaping-frontend/src/components/WebsiteModal.jsx b/mengyaping-frontend/src/components/WebsiteModal.jsx
new file mode 100644
index 0000000..905a578
--- /dev/null
+++ b/mengyaping-frontend/src/components/WebsiteModal.jsx
@@ -0,0 +1,223 @@
+import { useState, useEffect } from 'react';
+import { getGroups, createWebsite, updateWebsite } from '../services/api';
+
+export default function WebsiteModal({ isOpen, onClose, onSuccess, editData }) {
+ const [formData, setFormData] = useState({
+ name: '',
+ group: 'normal',
+ urls: [''],
+ });
+ const [groups, setGroups] = useState([]);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState('');
+
+ useEffect(() => {
+ if (isOpen) {
+ loadGroups();
+ if (editData) {
+ setFormData({
+ name: editData.website?.name || '',
+ group: editData.website?.group || 'normal',
+ urls: editData.website?.urls?.map(u => u.url) || [''],
+ });
+ } else {
+ setFormData({ name: '', group: 'normal', urls: [''] });
+ }
+ setError('');
+ }
+ }, [isOpen, editData]);
+
+ const loadGroups = async () => {
+ try {
+ const data = await getGroups();
+ setGroups(data || []);
+ } catch (err) {
+ console.error('加载分组失败:', err);
+ }
+ };
+
+ const handleAddUrl = () => {
+ setFormData({ ...formData, urls: [...formData.urls, ''] });
+ };
+
+ const handleRemoveUrl = (index) => {
+ if (formData.urls.length > 1) {
+ const newUrls = formData.urls.filter((_, i) => i !== index);
+ setFormData({ ...formData, urls: newUrls });
+ }
+ };
+
+ const handleUrlChange = (index, value) => {
+ const newUrls = [...formData.urls];
+ newUrls[index] = value;
+ setFormData({ ...formData, urls: newUrls });
+ };
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+ setError('');
+
+ // 验证
+ if (!formData.name.trim()) {
+ setError('请输入网站名称');
+ return;
+ }
+
+ const validUrls = formData.urls.filter(url => url.trim());
+ if (validUrls.length === 0) {
+ setError('请至少输入一个网站地址');
+ return;
+ }
+
+ // 验证URL格式
+ for (const url of validUrls) {
+ try {
+ new URL(url);
+ } catch {
+ setError(`无效的URL: ${url}`);
+ return;
+ }
+ }
+
+ setLoading(true);
+ try {
+ const data = {
+ name: formData.name.trim(),
+ group: formData.group,
+ urls: validUrls,
+ };
+
+ if (editData) {
+ await updateWebsite(editData.website.id, data);
+ } else {
+ await createWebsite(data);
+ }
+
+ onSuccess?.();
+ onClose();
+ } catch (err) {
+ setError(err.message);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ if (!isOpen) return null;
+
+ return (
+
+
+ {/* 标题 */}
+
+
+ {editData ? '编辑网站' : '添加监控网站'}
+
+
+
+ {/* 表单 */}
+
+
+ {/* 按钮 */}
+
+
+
+
+
+
+ );
+}
diff --git a/mengyaping-frontend/src/hooks/useMonitor.js b/mengyaping-frontend/src/hooks/useMonitor.js
new file mode 100644
index 0000000..bcd0a68
--- /dev/null
+++ b/mengyaping-frontend/src/hooks/useMonitor.js
@@ -0,0 +1,83 @@
+import { useState, useEffect, useCallback } from 'react';
+
+// 自动刷新数据Hook
+export function useAutoRefresh(fetchFn, interval = 30000) {
+ const [data, setData] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ const refresh = useCallback(async () => {
+ try {
+ setLoading(true);
+ const result = await fetchFn();
+ setData(result);
+ setError(null);
+ } catch (err) {
+ setError(err.message);
+ } finally {
+ setLoading(false);
+ }
+ }, [fetchFn]);
+
+ useEffect(() => {
+ refresh();
+
+ const timer = setInterval(refresh, interval);
+
+ return () => clearInterval(timer);
+ }, [refresh, interval]);
+
+ return { data, loading, error, refresh };
+}
+
+// 格式化时间
+export function formatTime(dateString) {
+ if (!dateString) return '-';
+ const date = new Date(dateString);
+ return date.toLocaleString('zh-CN', {
+ month: '2-digit',
+ day: '2-digit',
+ hour: '2-digit',
+ minute: '2-digit',
+ second: '2-digit',
+ });
+}
+
+// 格式化延迟
+export function formatLatency(ms) {
+ if (ms === undefined || ms === null) return '-';
+ if (ms < 1000) return `${ms}ms`;
+ return `${(ms / 1000).toFixed(2)}s`;
+}
+
+// 格式化可用率
+export function formatUptime(uptime) {
+ if (uptime === undefined || uptime === null) return '-';
+ return `${uptime.toFixed(2)}%`;
+}
+
+// 获取状态颜色类名
+export function getStatusColor(isUp, statusCode) {
+ if (!isUp) return 'text-red-500 bg-red-100';
+ if (statusCode >= 200 && statusCode < 300) return 'text-green-500 bg-green-100';
+ if (statusCode >= 300 && statusCode < 400) return 'text-yellow-500 bg-yellow-100';
+ return 'text-red-500 bg-red-100';
+}
+
+// 获取可用率颜色
+export function getUptimeColor(uptime) {
+ if (uptime >= 99) return 'text-green-500';
+ if (uptime >= 95) return 'text-green-400';
+ if (uptime >= 90) return 'text-yellow-500';
+ if (uptime >= 80) return 'text-orange-500';
+ return 'text-red-500';
+}
+
+// 获取延迟颜色
+export function getLatencyColor(ms) {
+ if (ms < 200) return 'text-green-500';
+ if (ms < 500) return 'text-green-400';
+ if (ms < 1000) return 'text-yellow-500';
+ if (ms < 2000) return 'text-orange-500';
+ return 'text-red-500';
+}
diff --git a/mengyaping-frontend/src/index.css b/mengyaping-frontend/src/index.css
new file mode 100644
index 0000000..c3941e4
--- /dev/null
+++ b/mengyaping-frontend/src/index.css
@@ -0,0 +1,68 @@
+@import "tailwindcss";
+
+:root {
+ font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+ line-height: 1.5;
+ font-weight: 400;
+ color-scheme: light;
+ color: #374151;
+ background-color: #f0fdf4;
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+ min-width: 320px;
+ min-height: 100vh;
+}
+
+/* 自定义滚动条 */
+::-webkit-scrollbar {
+ width: 6px;
+ height: 6px;
+}
+
+::-webkit-scrollbar-track {
+ background: #f1f5f9;
+ border-radius: 3px;
+}
+
+::-webkit-scrollbar-thumb {
+ background: #10b981;
+ border-radius: 3px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+ background: #059669;
+}
+
+/* 动画 */
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ transform: translateY(10px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+.animate-fadeIn {
+ animation: fadeIn 0.3s ease-out;
+}
+
+/* 响应式适配 */
+@media (max-width: 640px) {
+ html {
+ font-size: 14px;
+ }
+}
+
diff --git a/mengyaping-frontend/src/main.jsx b/mengyaping-frontend/src/main.jsx
new file mode 100644
index 0000000..b9a1a6d
--- /dev/null
+++ b/mengyaping-frontend/src/main.jsx
@@ -0,0 +1,10 @@
+import { StrictMode } from 'react'
+import { createRoot } from 'react-dom/client'
+import './index.css'
+import App from './App.jsx'
+
+createRoot(document.getElementById('root')).render(
+
+
+ ,
+)
diff --git a/mengyaping-frontend/src/pages/Dashboard.jsx b/mengyaping-frontend/src/pages/Dashboard.jsx
new file mode 100644
index 0000000..a085c77
--- /dev/null
+++ b/mengyaping-frontend/src/pages/Dashboard.jsx
@@ -0,0 +1,274 @@
+import { useState, useCallback, useMemo } from 'react';
+import WebsiteCard from '../components/WebsiteCard';
+import WebsiteModal from '../components/WebsiteModal';
+import StatsCard from '../components/StatsCard';
+import { useAutoRefresh } from '../hooks/useMonitor';
+import { getWebsites, deleteWebsite, checkWebsiteNow, getGroups } from '../services/api';
+
+export default function Dashboard() {
+ const [modalOpen, setModalOpen] = useState(false);
+ const [editData, setEditData] = useState(null);
+ const [selectedGroup, setSelectedGroup] = useState('all');
+ const [searchTerm, setSearchTerm] = useState('');
+
+ // 获取网站数据
+ const fetchData = useCallback(() => getWebsites(), []);
+ const { data: websites, loading, error, refresh } = useAutoRefresh(fetchData, 30000);
+
+ // 获取分组数据
+ const fetchGroups = useCallback(() => getGroups(), []);
+ const { data: groups } = useAutoRefresh(fetchGroups, 60000);
+
+ // 处理添加网站
+ const handleAdd = () => {
+ setEditData(null);
+ setModalOpen(true);
+ };
+
+ // 处理编辑网站
+ const handleEdit = (website) => {
+ setEditData(website);
+ setModalOpen(true);
+ };
+
+
+ // 处理删除网站
+ const handleDelete = async (id) => {
+ try {
+ await deleteWebsite(id);
+ refresh();
+ } catch (err) {
+ alert('删除失败: ' + err.message);
+ }
+ };
+
+ // 处理立即检测
+ const handleRefresh = async (id) => {
+ try {
+ await checkWebsiteNow(id);
+ setTimeout(refresh, 2000); // 2秒后刷新数据
+ } catch (err) {
+ alert('检测失败: ' + err.message);
+ }
+ };
+
+ // 按分组和搜索过滤网站
+ const filteredWebsites = useMemo(() => {
+ if (!websites) return [];
+
+ return websites.filter(site => {
+ // 分组过滤
+ if (selectedGroup !== 'all' && site.website?.group !== selectedGroup) {
+ return false;
+ }
+ // 搜索过滤
+ if (searchTerm) {
+ const term = searchTerm.toLowerCase();
+ return (
+ site.website?.name?.toLowerCase().includes(term) ||
+ site.website?.title?.toLowerCase().includes(term) ||
+ site.website?.urls?.some(u => u.url.toLowerCase().includes(term))
+ );
+ }
+ return true;
+ });
+ }, [websites, selectedGroup, searchTerm]);
+
+ // 按分组分类网站
+ const groupedWebsites = useMemo(() => {
+ const grouped = {};
+
+ filteredWebsites.forEach(site => {
+ const groupId = site.website?.group || 'normal';
+ if (!grouped[groupId]) {
+ grouped[groupId] = [];
+ }
+ grouped[groupId].push(site);
+ });
+
+ return grouped;
+ }, [filteredWebsites]);
+
+ // 获取分组名称
+ const getGroupName = (groupId) => {
+ const group = groups?.find(g => g.id === groupId);
+ return group?.name || groupId;
+ };
+
+ return (
+
+ {/* 顶部导航 */}
+
+
+
+ {/* Logo */}
+
+
+

+
+
+
+
+ {/* 添加按钮 */}
+
+
+
+
+
+ {/* 主内容区 */}
+
+ {/* 统计概览 */}
+
+
+ {/* 过滤和搜索 */}
+
+
+ {/* 分组选择 */}
+
+
+ {groups?.map(group => (
+
+ ))}
+
+
+ {/* 搜索框 */}
+
+
+
+
setSearchTerm(e.target.value)}
+ className="w-full pl-10 pr-4 py-2 border border-gray-200 rounded-lg focus:ring-2 focus:ring-emerald-500 focus:border-transparent outline-none transition-all"
+ />
+
+
+
+
+
+ {/* 网站列表 */}
+
+ {loading && !websites ? (
+
+ ) : error ? (
+
+
+
加载失败: {error}
+
+
+ ) : filteredWebsites.length === 0 ? (
+
+
+
+ {searchTerm ? '没有找到匹配的网站' : '还没有添加任何监控网站'}
+
+ {!searchTerm && (
+
+ )}
+
+ ) : (
+
+ {Object.keys(groupedWebsites).map(groupId => (
+
+ {selectedGroup === 'all' && (
+
+
+ {getGroupName(groupId)}
+ ({groupedWebsites[groupId].length})
+
+ )}
+
+ {groupedWebsites[groupId].map(website => (
+
+ ))}
+
+
+ ))}
+
+ )}
+
+
+
+ {/* 底部信息 */}
+
+
+ {/* 添加/编辑弹窗 */}
+
setModalOpen(false)}
+ onSuccess={refresh}
+ editData={editData}
+ />
+
+ );
+}
diff --git a/mengyaping-frontend/src/services/api.js b/mengyaping-frontend/src/services/api.js
new file mode 100644
index 0000000..ba2c8b9
--- /dev/null
+++ b/mengyaping-frontend/src/services/api.js
@@ -0,0 +1,77 @@
+// API服务
+// 根据环境变量判断使用哪个 API 地址
+const API_BASE = import.meta.env.PROD
+ ? 'https://ping.api.shumengya.top/api' // 生产环境
+ : 'http://localhost:8080/api'; // 开发环境
+
+// 通用请求方法
+async function request(url, options = {}) {
+ const response = await fetch(`${API_BASE}${url}`, {
+ headers: {
+ 'Content-Type': 'application/json',
+ ...options.headers,
+ },
+ ...options,
+ });
+
+ const data = await response.json();
+
+ if (data.code !== 0) {
+ throw new Error(data.message || '请求失败');
+ }
+
+ return data.data;
+}
+
+// 获取所有网站状态
+export async function getWebsites() {
+ return request('/websites');
+}
+
+// 获取单个网站状态
+export async function getWebsite(id) {
+ return request(`/websites/${id}`);
+}
+
+// 创建网站
+export async function createWebsite(data) {
+ return request('/websites', {
+ method: 'POST',
+ body: JSON.stringify(data),
+ });
+}
+
+// 更新网站
+export async function updateWebsite(id, data) {
+ return request(`/websites/${id}`, {
+ method: 'PUT',
+ body: JSON.stringify(data),
+ });
+}
+
+// 删除网站
+export async function deleteWebsite(id) {
+ return request(`/websites/${id}`, {
+ method: 'DELETE',
+ });
+}
+
+// 立即检测网站
+export async function checkWebsiteNow(id) {
+ return request(`/websites/${id}/check`, {
+ method: 'POST',
+ });
+}
+
+// 获取所有分组
+export async function getGroups() {
+ return request('/groups');
+}
+
+// 添加分组
+export async function addGroup(name) {
+ return request('/groups', {
+ method: 'POST',
+ body: JSON.stringify({ name }),
+ });
+}
diff --git a/mengyaping-frontend/tailwind.config.js b/mengyaping-frontend/tailwind.config.js
new file mode 100644
index 0000000..38302b3
--- /dev/null
+++ b/mengyaping-frontend/tailwind.config.js
@@ -0,0 +1,8 @@
+/** @type {import('tailwindcss').Config} */
+export default {
+ content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
+ theme: {
+ extend: {},
+ },
+ plugins: [],
+}
diff --git a/mengyaping-frontend/vite.config.js b/mengyaping-frontend/vite.config.js
new file mode 100644
index 0000000..c4069b7
--- /dev/null
+++ b/mengyaping-frontend/vite.config.js
@@ -0,0 +1,8 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+import tailwindcss from '@tailwindcss/vite'
+
+// https://vite.dev/config/
+export default defineConfig({
+ plugins: [react(), tailwindcss()],
+})
diff --git a/开启前端.bat b/开启前端.bat
new file mode 100644
index 0000000..97f4f08
--- /dev/null
+++ b/开启前端.bat
@@ -0,0 +1,9 @@
+@echo off
+chcp 65001 >nul
+echo ====================================
+echo 🌱 萌芽Ping 前端服务 (REACT)
+echo ====================================
+cd mengyaping-frontend
+echo 启动开发服务器...
+npm run dev
+pause
diff --git a/开启后端.bat b/开启后端.bat
new file mode 100644
index 0000000..84dd2a0
--- /dev/null
+++ b/开启后端.bat
@@ -0,0 +1,11 @@
+@echo off
+chcp 65001 >nul
+echo ====================================
+echo 🌱 萌芽Ping 后端服务 (GIN)
+echo ====================================
+cd mengyaping-backend
+echo 正在下载依赖...
+go mod tidy
+echo 启动服务...
+go run main.go
+pause
diff --git a/构建前端.bat b/构建前端.bat
new file mode 100644
index 0000000..c221bdd
--- /dev/null
+++ b/构建前端.bat
@@ -0,0 +1,10 @@
+@echo off
+chcp 65001 >nul
+echo ====================================
+echo 构建前端项目 (REACT)
+echo ====================================
+cd mengyaping-frontend
+npm run build
+echo.
+echo ✅ 构建完成!输出目录: dist
+pause