From aff88ba97d66b510409c24cf6997f7569259a368 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=91=E8=90=8C=E8=8A=BD?= <3205788256@qq.com> Date: Sat, 16 May 2026 19:03:32 +0800 Subject: [PATCH] chore: sync local updates --- AGENTS.md | 4 +- mengyaping-backend/.gitignore | 3 + mengyaping-backend/Dockerfile | 45 +- mengyaping-backend/build-linux-amd64.bat | 35 + mengyaping-backend/cmd/importjson/main.go | 136 + mengyaping-backend/config/config.go | 269 +- mengyaping-backend/config/database.go | 25 + mengyaping-backend/config/monitor_interval.go | 38 + .../config/monitor_interval_test.go | 24 + mengyaping-backend/data/.gitkeep | 0 mengyaping-backend/data/config.json | 13 - mengyaping-backend/data/groups.json | 18 - mengyaping-backend/data/records.json | 9735 ----------------- mengyaping-backend/data/websites.json | 821 -- mengyaping-backend/docker-compose.yml | 8 +- mengyaping-backend/go.mod | 8 + mengyaping-backend/go.sum | 40 + mengyaping-backend/handlers/settings.go | 87 + mengyaping-backend/handlers/website.go | 466 +- mengyaping-backend/main.go | 97 +- mengyaping-backend/middleware/admin.go | 43 + mengyaping-backend/models/website.go | 230 +- mengyaping-backend/router/router.go | 117 +- mengyaping-backend/schema/mysql.sql | 19 + mengyaping-backend/services/monitor.go | 906 +- mengyaping-backend/services/website.go | 319 +- mengyaping-backend/storage/app_config.go | 161 + mengyaping-backend/storage/db_kv.go | 14 + mengyaping-backend/storage/db_models.go | 93 + mengyaping-backend/storage/kv.go | 62 + mengyaping-backend/storage/probe.go | 295 + mengyaping-backend/storage/storage.go | 667 +- mengyaping-backend/utils/intranet.go | 37 + mengyaping-backend/utils/intranet_test.go | 24 + mengyaping-frontend/package-lock.json | 70 +- mengyaping-frontend/package.json | 3 +- mengyaping-frontend/src/App.jsx | 15 +- .../src/components/AdminGroupsPanel.jsx | 179 + .../src/components/RandomBackdrop.jsx | 57 + .../src/components/StatsCard.jsx | 6 - .../src/components/WebsiteCard.jsx | 91 +- mengyaping-frontend/src/constants/events.js | 2 + mengyaping-frontend/src/main.jsx | 5 +- mengyaping-frontend/src/pages/AdminPanel.jsx | 206 + mengyaping-frontend/src/pages/Dashboard.jsx | 221 +- .../src/pages/PublicDashboard.jsx | 5 + mengyaping-frontend/src/services/api.js | 107 +- 47 files changed, 3651 insertions(+), 12175 deletions(-) create mode 100644 mengyaping-backend/.gitignore create mode 100644 mengyaping-backend/build-linux-amd64.bat create mode 100644 mengyaping-backend/cmd/importjson/main.go create mode 100644 mengyaping-backend/config/database.go create mode 100644 mengyaping-backend/config/monitor_interval.go create mode 100644 mengyaping-backend/config/monitor_interval_test.go create mode 100644 mengyaping-backend/data/.gitkeep delete mode 100644 mengyaping-backend/data/config.json delete mode 100644 mengyaping-backend/data/groups.json delete mode 100644 mengyaping-backend/data/records.json delete mode 100644 mengyaping-backend/data/websites.json create mode 100644 mengyaping-backend/handlers/settings.go create mode 100644 mengyaping-backend/middleware/admin.go create mode 100644 mengyaping-backend/schema/mysql.sql create mode 100644 mengyaping-backend/storage/app_config.go create mode 100644 mengyaping-backend/storage/db_kv.go create mode 100644 mengyaping-backend/storage/db_models.go create mode 100644 mengyaping-backend/storage/kv.go create mode 100644 mengyaping-backend/storage/probe.go create mode 100644 mengyaping-backend/utils/intranet.go create mode 100644 mengyaping-backend/utils/intranet_test.go create mode 100644 mengyaping-frontend/src/components/AdminGroupsPanel.jsx create mode 100644 mengyaping-frontend/src/components/RandomBackdrop.jsx create mode 100644 mengyaping-frontend/src/constants/events.js create mode 100644 mengyaping-frontend/src/pages/AdminPanel.jsx create mode 100644 mengyaping-frontend/src/pages/PublicDashboard.jsx diff --git a/AGENTS.md b/AGENTS.md index f5733d8..e6895df 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,7 +5,7 @@ This repository is split into two apps: - `mengyaping-frontend/`: React + Vite UI. Main code is in `src/` (`components/`, `pages/`, `hooks/`, `services/`), with static assets in `public/`. - `mengyaping-backend/`: Go + Gin API and monitor service. Core folders are `handlers/`, `services/`, `router/`, `models/`, `storage/`, `config/`, and `utils/`. -Runtime data is persisted under `mengyaping-backend/data/` (`websites.json`, `records.json`, `groups.json`, `config.json`). Keep data format changes backward-compatible. +Runtime site/group data is in MySQL. Optional `mengyaping-backend/data/` is only for local tooling (e.g. `cmd/importjson` reading `websites.json` / `groups.json`). App settings and admin token live in table `monitor_kv`. ## Build, Test, and Development Commands Frontend (run inside `mengyaping-frontend/`): @@ -44,4 +44,4 @@ Each PR should include: ## Security & Configuration Tips - Do not commit secrets, tokens, or private endpoints. - Frontend dev API target is `http://localhost:8080/api` in `mengyaping-frontend/src/services/api.js`. -- Commit only sanitized sample data in `mengyaping-backend/data/`. +- Commit only sanitized sample data in `mengyaping-backend/data/`. diff --git a/mengyaping-backend/.gitignore b/mengyaping-backend/.gitignore new file mode 100644 index 0000000..fbe4284 --- /dev/null +++ b/mengyaping-backend/.gitignore @@ -0,0 +1,3 @@ +# 本地交叉编译产物(部署时再生成) +dist/mengyaping-backend +dist/mengyaping-backend.exe diff --git a/mengyaping-backend/Dockerfile b/mengyaping-backend/Dockerfile index f1566ec..a66a9d1 100644 --- a/mengyaping-backend/Dockerfile +++ b/mengyaping-backend/Dockerfile @@ -1,56 +1,31 @@ -# 多阶段构建 - 使用官方 Golang 镜像作为构建环境 -FROM golang:1.25-alpine AS builder +# 生产镜像:使用本机预编译的 Linux amd64 二进制(避免在服务器 docker build 时再跑 go build) +# Windows: 先双击运行 build-linux-amd64.bat,生成 dist/mengyaping-backend 后再执行: +# docker compose build +# 若需在容器内编译,请使用: docker build -f Dockerfile.source . -# 设置工作目录 -WORKDIR /app +FROM alpine:3.21 -# 安装必要的构建工具 -RUN apk add --no-cache git ca-certificates tzdata +RUN apk --no-cache add ca-certificates tzdata wget -# 复制 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 . +COPY dist/mengyaping-backend /app/mengyaping-backend -# 创建数据目录 -RUN mkdir -p /app/data && chown -R appuser:appuser /app +RUN chmod +x /app/mengyaping-backend && \ + 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/build-linux-amd64.bat b/mengyaping-backend/build-linux-amd64.bat new file mode 100644 index 0000000..9b6b8ed --- /dev/null +++ b/mengyaping-backend/build-linux-amd64.bat @@ -0,0 +1,35 @@ +@echo off +chcp 65001 >nul 2>&1 +setlocal +cd /d "%~dp0" + +where go >nul 2>nul +if errorlevel 1 ( + echo [ERROR] Go not found in PATH. Install Go and reopen the terminal. + pause + exit /b 1 +) + +set "DIST=dist" +if not exist "%DIST%" mkdir "%DIST%" + +set "GOOS=linux" +set "GOARCH=amd64" +set "CGO_ENABLED=0" + +echo Cross-compile linux/amd64 -^> "%DIST%\mengyaping-backend" +go version +go build -trimpath -ldflags="-s -w" -o "%DIST%\mengyaping-backend" . +if errorlevel 1 ( + echo. + echo [ERROR] go build failed. See messages above. + echo Tips: go.mod requires Go 1.25+; run from mengyaping-backend folder. + pause + exit /b 1 +) + +echo. +echo [OK] "%CD%\%DIST%\mengyaping-backend" +echo Next: copy this folder to server, then: docker compose build +endlocal +exit /b 0 diff --git a/mengyaping-backend/cmd/importjson/main.go b/mengyaping-backend/cmd/importjson/main.go new file mode 100644 index 0000000..e2dcc32 --- /dev/null +++ b/mengyaping-backend/cmd/importjson/main.go @@ -0,0 +1,136 @@ +// 将 data/*.json(站点与分组)导入 MySQL。建议空库首次使用,或加 -wipe 清空业务表后再导。 +// +// go run ./cmd/importjson -data ./data -wipe +package main + +import ( + "encoding/json" + "flag" + "fmt" + "log" + "os" + "path/filepath" + + "gorm.io/datatypes" + "gorm.io/driver/mysql" + "gorm.io/gorm" + "gorm.io/gorm/logger" + + "mengyaping-backend/config" + "mengyaping-backend/models" + "mengyaping-backend/storage" +) + +func main() { + dataDir := flag.String("data", "./data", "JSON 目录(websites.json、groups.json)") + wipe := flag.Bool("wipe", false, "导入前 TRUNCATE 所有 monitor_* 表(慎用)") + flag.Parse() + + cfg := config.GetConfig() + // 大批量 INSERT 时单条超过 200ms 会触发 GORM 默认「SLOW SQL」提示,易被误认为报错;导入工具改为静默 SQL 日志 + db, err := gorm.Open(mysql.Open(cfg.DatabaseDSN()), &gorm.Config{ + Logger: logger.Default.LogMode(logger.Silent), + }) + if err != nil { + log.Fatal(err) + } + + if err := db.AutoMigrate( + &storage.MonitorGroup{}, + &storage.MonitorWebsite{}, + &storage.MonitorWebsiteURL{}, + &storage.MonitorWebsiteGroup{}, + &storage.MonitorProbeLatest{}, + &storage.MonitorProbeHour{}, + &storage.MonitorProbeDay{}, + &storage.MonitorKV{}, + ); err != nil { + log.Fatal(err) + } + + if *wipe { + _ = db.Exec("SET FOREIGN_KEY_CHECKS = 0").Error + for _, tbl := range []string{ + "monitor_probe_latest", + "monitor_probe_hour", + "monitor_probe_day", + "monitor_kv", + "monitor_checks", + "monitor_website_urls", + "monitor_website_groups", + "monitor_websites", + "monitor_groups", + } { + _ = db.Exec("TRUNCATE TABLE " + tbl).Error + } + _ = db.Exec("SET FOREIGN_KEY_CHECKS = 1").Error + log.Println("已清空 monitor_* 表") + } + + websitesPath := filepath.Join(*dataDir, "websites.json") + raw, err := os.ReadFile(websitesPath) + if err != nil { + log.Fatalf("读取 websites.json: %v", err) + } + var websites []models.Website + if err := json.Unmarshal(raw, &websites); err != nil { + log.Fatal(err) + } + + groupsPath := filepath.Join(*dataDir, "groups.json") + var fileGroups []models.Group + if g, err := os.ReadFile(groupsPath); err == nil { + _ = json.Unmarshal(g, &fileGroups) + } + if len(fileGroups) == 0 { + fileGroups = models.DefaultGroups + } + for i, g := range fileGroups { + if err := db.Create(&storage.MonitorGroup{ + ID: g.ID, Name: g.Name, SortOrder: i + 1, + }).Error; err != nil { + log.Printf("导入分组 %s: %v", g.ID, err) + } + } + + for _, w := range websites { + if len(w.Groups) == 0 && w.Group != "" { + w.Groups = []string{w.Group} + } + row := &storage.MonitorWebsite{ + ID: w.ID, + Name: w.Name, + LegacyGroup: "", + Favicon: w.Favicon, + Title: w.Title, + IPAddresses: datatypes.JSON(mustJSON(w.IPAddresses)), + CreatedAt: w.CreatedAt, + UpdatedAt: w.UpdatedAt, + } + if err := db.Create(row).Error; err != nil { + log.Printf("网站 %s: %v", w.ID, err) + continue + } + for i, u := range w.URLs { + _ = db.Create(&storage.MonitorWebsiteURL{ + WebsiteID: w.ID, URLID: u.ID, URL: u.URL, Remark: u.Remark, SortOrder: i, + }) + } + for _, gid := range w.Groups { + _ = db.Create(&storage.MonitorWebsiteGroup{WebsiteID: w.ID, GroupID: gid}) + } + } + fmt.Printf("已导入 %d 个网站\n", len(websites)) + log.Println("探测历史由运行时写入 monitor_probe_* 汇总表,不再从 records.json 导入") +} + +func mustJSON(v []string) []byte { + if v == nil { + return []byte("[]") + } + b, err := json.Marshal(v) + if err != nil { + return []byte("[]") + } + return b +} diff --git a/mengyaping-backend/config/config.go b/mengyaping-backend/config/config.go index 1f66719..a6a2ffd 100644 --- a/mengyaping-backend/config/config.go +++ b/mengyaping-backend/config/config.go @@ -1,176 +1,93 @@ -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", "1h"), 1*time.Hour), - Timeout: parseDuration(getEnv("MONITOR_TIMEOUT", "10s"), 10*time.Second), - RetryCount: parseInt(getEnv("MONITOR_RETRY_COUNT", "3"), 3), - HistoryDays: parseInt(getEnv("MONITOR_HISTORY_DAYS", "90"), 90), - }, - 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) -} +package config + +import ( + "fmt" + "os" + "sync" + "time" +) + +// Config 应用配置(持久化在 MySQL monitor_kv,首次启动前由环境变量提供默认值;MySQL 连接信息首次连接仍依赖环境变量/DB_DSN) +type Config struct { + Server ServerConfig `json:"server"` + Monitor MonitorConfig `json:"monitor"` + DataPath string `json:"data_path"` + Database DatabaseConfig `json:"database"` +} + +// 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 获取配置单例(连接 MySQL 后由 storage 根据 monitor_kv 覆盖) +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", "30m"), 30*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", "90"), 90), + }, + DataPath: getEnv("DATA_PATH", "./data"), + Database: DatabaseConfig{ + Host: getEnv("DB_HOST", "192.168.1.100"), + Port: getEnv("DB_PORT", "3306"), + User: getEnv("DB_USER", "mengyaping"), + Password: getEnv("DB_PASSWORD", "mengyaping"), + Database: getEnv("DB_NAME", "mengyaping"), + }, + } + }) + 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 +} diff --git a/mengyaping-backend/config/database.go b/mengyaping-backend/config/database.go new file mode 100644 index 0000000..ff0dfbd --- /dev/null +++ b/mengyaping-backend/config/database.go @@ -0,0 +1,25 @@ +package config + +import ( + "fmt" + "os" +) + +// DatabaseConfig MySQL 连接(环境变量 DB_* 可覆盖;亦支持 DB_DSN 整串) +type DatabaseConfig struct { + Host string `json:"host"` + Port string `json:"port"` + User string `json:"user"` + Password string `json:"password"` + Database string `json:"database"` +} + +// DatabaseDSN 返回 GORM/MySQL driver 用连接串 +func (c *Config) DatabaseDSN() string { + if dsn := os.Getenv("DB_DSN"); dsn != "" { + return dsn + } + d := c.Database + return fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", + d.User, d.Password, d.Host, d.Port, d.Database) +} diff --git a/mengyaping-backend/config/monitor_interval.go b/mengyaping-backend/config/monitor_interval.go new file mode 100644 index 0000000..165271f --- /dev/null +++ b/mengyaping-backend/config/monitor_interval.go @@ -0,0 +1,38 @@ +package config + +import "slices" + +// AllowedMonitorIntervalMinutes 后台预设的全局检测周期(分钟) +var AllowedMonitorIntervalMinutes = []int{10, 20, 30, 60, 180, 360} + +// IsAllowedMonitorInterval 是否为允许的检测间隔(分钟) +func IsAllowedMonitorInterval(minutes int) bool { + return minutes > 0 && slices.Contains(AllowedMonitorIntervalMinutes, minutes) +} + +// SnapMonitorIntervalMinutes 将分钟数裁剪到最近的预设值(用于读取旧配置) +func SnapMonitorIntervalMinutes(minutes int) int { + if minutes <= 0 { + return 30 + } + if IsAllowedMonitorInterval(minutes) { + return minutes + } + best := AllowedMonitorIntervalMinutes[0] + bestDist := absInt(minutes - best) + for _, v := range AllowedMonitorIntervalMinutes[1:] { + d := absInt(minutes - v) + if d < bestDist { + bestDist = d + best = v + } + } + return best +} + +func absInt(x int) int { + if x < 0 { + return -x + } + return x +} diff --git a/mengyaping-backend/config/monitor_interval_test.go b/mengyaping-backend/config/monitor_interval_test.go new file mode 100644 index 0000000..ba3a0ff --- /dev/null +++ b/mengyaping-backend/config/monitor_interval_test.go @@ -0,0 +1,24 @@ +package config + +import "testing" + +func TestIsAllowedMonitorInterval(t *testing.T) { + if !IsAllowedMonitorInterval(10) || !IsAllowedMonitorInterval(360) { + t.Fatal("expected 10 and 360 allowed") + } + if IsAllowedMonitorInterval(5) || IsAllowedMonitorInterval(45) { + t.Fatal("expected 5 and 45 disallowed") + } +} + +func TestSnapMonitorIntervalMinutes(t *testing.T) { + if SnapMonitorIntervalMinutes(5) != 10 { + t.Fatalf("5 -> 10, got %d", SnapMonitorIntervalMinutes(5)) + } + if SnapMonitorIntervalMinutes(7) != 10 { + t.Fatalf("7 -> 10, got %d", SnapMonitorIntervalMinutes(7)) + } + if SnapMonitorIntervalMinutes(25) != 20 { + t.Fatalf("25 -> 20, got %d", SnapMonitorIntervalMinutes(25)) + } +} diff --git a/mengyaping-backend/data/.gitkeep b/mengyaping-backend/data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/mengyaping-backend/data/config.json b/mengyaping-backend/data/config.json deleted file mode 100644 index 85ccd04..0000000 --- a/mengyaping-backend/data/config.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "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 deleted file mode 100644 index 43108ab..0000000 --- a/mengyaping-backend/data/groups.json +++ /dev/null @@ -1,18 +0,0 @@ -[ - { - "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 deleted file mode 100644 index 089d94d..0000000 --- a/mengyaping-backend/data/records.json +++ /dev/null @@ -1,9735 +0,0 @@ -{ - "188cb48c619bd5d896bff546_d19b0a475334": [ - { - "website_id": "188cb48c619bd5d896bff546", - "url_id": "d19b0a475334", - "url": "https://shumengya.top", - "status_code": 200, - "latency": 213, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:01:30.3518219+08:00" - }, - { - "website_id": "188cb48c619bd5d896bff546", - "url_id": "d19b0a475334", - "url": "https://shumengya.top", - "status_code": 200, - "latency": 60, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:06:30.4190076+08:00" - }, - { - "website_id": "188cb48c619bd5d896bff546", - "url_id": "d19b0a475334", - "url": "https://shumengya.top", - "status_code": 200, - "latency": 107, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:10:58.4668528+08:00" - }, - { - "website_id": "188cb48c619bd5d896bff546", - "url_id": "d19b0a475334", - "url": "https://shumengya.top", - "status_code": 200, - "latency": 53, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:15:58.0861888+08:00" - }, - { - "website_id": "188cb48c619bd5d896bff546", - "url_id": "d19b0a475334", - "url": "https://shumengya.top", - "status_code": 200, - "latency": 59, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:20:58.0121422+08:00" - }, - { - "website_id": "188cb48c619bd5d896bff546", - "url_id": "d19b0a475334", - "url": "https://shumengya.top", - "status_code": 200, - "latency": 54, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:25:58.0762431+08:00" - }, - { - "website_id": "188cb48c619bd5d896bff546", - "url_id": "d19b0a475334", - "url": "https://shumengya.top", - "status_code": 200, - "latency": 68, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:30:58.0535498+08:00" - }, - { - "website_id": "188cb48c619bd5d896bff546", - "url_id": "d19b0a475334", - "url": "https://shumengya.top", - "status_code": 200, - "latency": 69, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:35:58.1878862+08:00" - }, - { - "website_id": "188cb48c619bd5d896bff546", - "url_id": "d19b0a475334", - "url": "https://shumengya.top", - "status_code": 200, - "latency": 76, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:40:58.0282947+08:00" - }, - { - "website_id": "188cb48c619bd5d896bff546", - "url_id": "d19b0a475334", - "url": "https://shumengya.top", - "status_code": 200, - "latency": 91, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:45:58.0427496+08:00" - }, - { - "website_id": "188cb48c619bd5d896bff546", - "url_id": "d19b0a475334", - "url": "https://shumengya.top", - "status_code": 200, - "latency": 85, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:13:25.3437809+08:00" - }, - { - "website_id": "188cb48c619bd5d896bff546", - "url_id": "d19b0a475334", - "url": "https://shumengya.top", - "status_code": 200, - "latency": 197, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:21:36.1247216+08:00" - }, - { - "website_id": "188cb48c619bd5d896bff546", - "url_id": "d19b0a475334", - "url": "https://shumengya.top", - "status_code": 200, - "latency": 67, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:26:36.0051672+08:00" - }, - { - "website_id": "188cb48c619bd5d896bff546", - "url_id": "d19b0a475334", - "url": "https://shumengya.top", - "status_code": 200, - "latency": 82, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:31:36.013936+08:00" - }, - { - "website_id": "188cb48c619bd5d896bff546", - "url_id": "d19b0a475334", - "url": "https://shumengya.top", - "status_code": 200, - "latency": 64, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:36:35.9928867+08:00" - }, - { - "website_id": "188cb48c619bd5d896bff546", - "url_id": "d19b0a475334", - "url": "https://shumengya.top", - "status_code": 200, - "latency": 68, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:41:48.2096529+08:00" - }, - { - "website_id": "188cb48c619bd5d896bff546", - "url_id": "d19b0a475334", - "url": "https://shumengya.top", - "status_code": 200, - "latency": 75, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:46:37.5730538+08:00" - }, - { - "website_id": "188cb48c619bd5d896bff546", - "url_id": "d19b0a475334", - "url": "https://shumengya.top", - "status_code": 200, - "latency": 70, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:51:35.9972463+08:00" - }, - { - "website_id": "188cb48c619bd5d896bff546", - "url_id": "d19b0a475334", - "url": "https://shumengya.top", - "status_code": 200, - "latency": 64, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:56:37.6175615+08:00" - }, - { - "website_id": "188cb48c619bd5d896bff546", - "url_id": "d19b0a475334", - "url": "https://shumengya.top", - "status_code": 200, - "latency": 577, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:49:24.7306178+08:00" - }, - { - "website_id": "188cb48c619bd5d896bff546", - "url_id": "d19b0a475334", - "url": "https://shumengya.top", - "status_code": 200, - "latency": 76, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:51:41.7880262+08:00" - }, - { - "website_id": "188cb48c619bd5d896bff546", - "url_id": "d19b0a475334", - "url": "https://shumengya.top", - "status_code": 200, - "latency": 87, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:56:29.785254+08:00" - }, - { - "website_id": "188cb48c619bd5d896bff546", - "url_id": "d19b0a475334", - "url": "https://shumengya.top", - "status_code": 200, - "latency": 73, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:01:29.7749925+08:00" - }, - { - "website_id": "188cb48c619bd5d896bff546", - "url_id": "d19b0a475334", - "url": "https://shumengya.top", - "status_code": 200, - "latency": 64, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:06:29.7613995+08:00" - }, - { - "website_id": "188cb48c619bd5d896bff546", - "url_id": "d19b0a475334", - "url": "https://shumengya.top", - "status_code": 200, - "latency": 81, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:11:29.7778361+08:00" - } - ], - "188cb49298d72b78a37301a1_9d25ae024f36": [ - { - "website_id": "188cb49298d72b78a37301a1", - "url_id": "9d25ae024f36", - "url": "https://memos.shumengya.top", - "status_code": 200, - "latency": 176, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:01:30.3145844+08:00" - }, - { - "website_id": "188cb49298d72b78a37301a1", - "url_id": "9d25ae024f36", - "url": "https://memos.shumengya.top", - "status_code": 200, - "latency": 71, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:06:30.4236596+08:00" - }, - { - "website_id": "188cb49298d72b78a37301a1", - "url_id": "9d25ae024f36", - "url": "https://memos.shumengya.top", - "status_code": 200, - "latency": 191, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:10:58.5529677+08:00" - }, - { - "website_id": "188cb49298d72b78a37301a1", - "url_id": "9d25ae024f36", - "url": "https://memos.shumengya.top", - "status_code": 200, - "latency": 55, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:15:58.0969356+08:00" - }, - { - "website_id": "188cb49298d72b78a37301a1", - "url_id": "9d25ae024f36", - "url": "https://memos.shumengya.top", - "status_code": 200, - "latency": 164, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:20:58.1167747+08:00" - }, - { - "website_id": "188cb49298d72b78a37301a1", - "url_id": "9d25ae024f36", - "url": "https://memos.shumengya.top", - "status_code": 200, - "latency": 59, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:25:58.0865817+08:00" - }, - { - "website_id": "188cb49298d72b78a37301a1", - "url_id": "9d25ae024f36", - "url": "https://memos.shumengya.top", - "status_code": 200, - "latency": 61, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:30:58.012316+08:00" - }, - { - "website_id": "188cb49298d72b78a37301a1", - "url_id": "9d25ae024f36", - "url": "https://memos.shumengya.top", - "status_code": 200, - "latency": 62, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:35:58.0136053+08:00" - }, - { - "website_id": "188cb49298d72b78a37301a1", - "url_id": "9d25ae024f36", - "url": "https://memos.shumengya.top", - "status_code": 200, - "latency": 67, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:40:58.1667614+08:00" - }, - { - "website_id": "188cb49298d72b78a37301a1", - "url_id": "9d25ae024f36", - "url": "https://memos.shumengya.top", - "status_code": 200, - "latency": 61, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:45:58.117091+08:00" - }, - { - "website_id": "188cb49298d72b78a37301a1", - "url_id": "9d25ae024f36", - "url": "https://memos.shumengya.top", - "status_code": 200, - "latency": 167, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:13:25.4258532+08:00" - }, - { - "website_id": "188cb49298d72b78a37301a1", - "url_id": "9d25ae024f36", - "url": "https://memos.shumengya.top", - "status_code": 200, - "latency": 58, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:21:37.3768431+08:00" - }, - { - "website_id": "188cb49298d72b78a37301a1", - "url_id": "9d25ae024f36", - "url": "https://memos.shumengya.top", - "status_code": 200, - "latency": 89, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:26:36.0273499+08:00" - }, - { - "website_id": "188cb49298d72b78a37301a1", - "url_id": "9d25ae024f36", - "url": "https://memos.shumengya.top", - "status_code": 200, - "latency": 88, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:31:36.0192552+08:00" - }, - { - "website_id": "188cb49298d72b78a37301a1", - "url_id": "9d25ae024f36", - "url": "https://memos.shumengya.top", - "status_code": 200, - "latency": 49, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:36:36.8294284+08:00" - }, - { - "website_id": "188cb49298d72b78a37301a1", - "url_id": "9d25ae024f36", - "url": "https://memos.shumengya.top", - "status_code": 200, - "latency": 69, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:41:36.0062434+08:00" - }, - { - "website_id": "188cb49298d72b78a37301a1", - "url_id": "9d25ae024f36", - "url": "https://memos.shumengya.top", - "status_code": 200, - "latency": 67, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:46:37.4963488+08:00" - }, - { - "website_id": "188cb49298d72b78a37301a1", - "url_id": "9d25ae024f36", - "url": "https://memos.shumengya.top", - "status_code": 200, - "latency": 59, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:51:36.235684+08:00" - }, - { - "website_id": "188cb49298d72b78a37301a1", - "url_id": "9d25ae024f36", - "url": "https://memos.shumengya.top", - "status_code": 200, - "latency": 59, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:56:35.9878827+08:00" - }, - { - "website_id": "188cb49298d72b78a37301a1", - "url_id": "9d25ae024f36", - "url": "https://memos.shumengya.top", - "status_code": 200, - "latency": 601, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:49:24.1534192+08:00" - }, - { - "website_id": "188cb49298d72b78a37301a1", - "url_id": "9d25ae024f36", - "url": "https://memos.shumengya.top", - "status_code": 200, - "latency": 80, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:51:42.8726981+08:00" - }, - { - "website_id": "188cb49298d72b78a37301a1", - "url_id": "9d25ae024f36", - "url": "https://memos.shumengya.top", - "status_code": 200, - "latency": 62, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:56:30.8531004+08:00" - }, - { - "website_id": "188cb49298d72b78a37301a1", - "url_id": "9d25ae024f36", - "url": "https://memos.shumengya.top", - "status_code": 200, - "latency": 80, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:01:30.8566235+08:00" - }, - { - "website_id": "188cb49298d72b78a37301a1", - "url_id": "9d25ae024f36", - "url": "https://memos.shumengya.top", - "status_code": 200, - "latency": 91, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:06:30.8545324+08:00" - }, - { - "website_id": "188cb49298d72b78a37301a1", - "url_id": "9d25ae024f36", - "url": "https://memos.shumengya.top", - "status_code": 200, - "latency": 63, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:11:30.8471418+08:00" - } - ], - "188cb52c04cf639cfc2c8ba4_cfe6229fb86b": [ - { - "website_id": "188cb52c04cf639cfc2c8ba4", - "url_id": "cfe6229fb86b", - "url": "https://pan.shumengya.top", - "status_code": 200, - "latency": 94, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:01:30.2343529+08:00" - }, - { - "website_id": "188cb52c04cf639cfc2c8ba4", - "url_id": "cfe6229fb86b", - "url": "https://pan.shumengya.top", - "status_code": 200, - "latency": 64, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:06:30.4693623+08:00" - }, - { - "website_id": "188cb52c04cf639cfc2c8ba4", - "url_id": "cfe6229fb86b", - "url": "https://pan.shumengya.top", - "status_code": 200, - "latency": 62, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:10:58.5369688+08:00" - }, - { - "website_id": "188cb52c04cf639cfc2c8ba4", - "url_id": "cfe6229fb86b", - "url": "https://pan.shumengya.top", - "status_code": 200, - "latency": 185, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:15:58.1360089+08:00" - }, - { - "website_id": "188cb52c04cf639cfc2c8ba4", - "url_id": "cfe6229fb86b", - "url": "https://pan.shumengya.top", - "status_code": 200, - "latency": 61, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:20:58.1242615+08:00" - }, - { - "website_id": "188cb52c04cf639cfc2c8ba4", - "url_id": "cfe6229fb86b", - "url": "https://pan.shumengya.top", - "status_code": 200, - "latency": 60, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:25:58.122983+08:00" - }, - { - "website_id": "188cb52c04cf639cfc2c8ba4", - "url_id": "cfe6229fb86b", - "url": "https://pan.shumengya.top", - "status_code": 200, - "latency": 64, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:30:58.0664754+08:00" - }, - { - "website_id": "188cb52c04cf639cfc2c8ba4", - "url_id": "cfe6229fb86b", - "url": "https://pan.shumengya.top", - "status_code": 200, - "latency": 63, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:35:58.1478068+08:00" - }, - { - "website_id": "188cb52c04cf639cfc2c8ba4", - "url_id": "cfe6229fb86b", - "url": "https://pan.shumengya.top", - "status_code": 200, - "latency": 70, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:40:58.1924443+08:00" - }, - { - "website_id": "188cb52c04cf639cfc2c8ba4", - "url_id": "cfe6229fb86b", - "url": "https://pan.shumengya.top", - "status_code": 200, - "latency": 65, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:45:58.1508737+08:00" - }, - { - "website_id": "188cb52c04cf639cfc2c8ba4", - "url_id": "cfe6229fb86b", - "url": "https://pan.shumengya.top", - "status_code": 200, - "latency": 93, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:13:25.3524472+08:00" - }, - { - "website_id": "188cb52c04cf639cfc2c8ba4", - "url_id": "cfe6229fb86b", - "url": "https://pan.shumengya.top", - "status_code": 200, - "latency": 56, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:21:37.4336716+08:00" - }, - { - "website_id": "188cb52c04cf639cfc2c8ba4", - "url_id": "cfe6229fb86b", - "url": "https://pan.shumengya.top", - "status_code": 200, - "latency": 76, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:26:36.0144262+08:00" - }, - { - "website_id": "188cb52c04cf639cfc2c8ba4", - "url_id": "cfe6229fb86b", - "url": "https://pan.shumengya.top", - "status_code": 200, - "latency": 88, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:31:36.0198598+08:00" - }, - { - "website_id": "188cb52c04cf639cfc2c8ba4", - "url_id": "cfe6229fb86b", - "url": "https://pan.shumengya.top", - "status_code": 200, - "latency": 62, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:36:36.7800465+08:00" - }, - { - "website_id": "188cb52c04cf639cfc2c8ba4", - "url_id": "cfe6229fb86b", - "url": "https://pan.shumengya.top", - "status_code": 200, - "latency": 66, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:41:36.3228889+08:00" - }, - { - "website_id": "188cb52c04cf639cfc2c8ba4", - "url_id": "cfe6229fb86b", - "url": "https://pan.shumengya.top", - "status_code": 200, - "latency": 72, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:46:37.8844494+08:00" - }, - { - "website_id": "188cb52c04cf639cfc2c8ba4", - "url_id": "cfe6229fb86b", - "url": "https://pan.shumengya.top", - "status_code": 200, - "latency": 63, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:51:35.9911733+08:00" - }, - { - "website_id": "188cb52c04cf639cfc2c8ba4", - "url_id": "cfe6229fb86b", - "url": "https://pan.shumengya.top", - "status_code": 200, - "latency": 69, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:56:35.9988544+08:00" - }, - { - "website_id": "188cb52c04cf639cfc2c8ba4", - "url_id": "cfe6229fb86b", - "url": "https://pan.shumengya.top", - "status_code": 200, - "latency": 65, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:49:25.6839804+08:00" - }, - { - "website_id": "188cb52c04cf639cfc2c8ba4", - "url_id": "cfe6229fb86b", - "url": "https://pan.shumengya.top", - "status_code": 200, - "latency": 60, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:51:43.942245+08:00" - }, - { - "website_id": "188cb52c04cf639cfc2c8ba4", - "url_id": "cfe6229fb86b", - "url": "https://pan.shumengya.top", - "status_code": 200, - "latency": 75, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:56:31.9290912+08:00" - }, - { - "website_id": "188cb52c04cf639cfc2c8ba4", - "url_id": "cfe6229fb86b", - "url": "https://pan.shumengya.top", - "status_code": 200, - "latency": 65, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:01:31.9227419+08:00" - }, - { - "website_id": "188cb52c04cf639cfc2c8ba4", - "url_id": "cfe6229fb86b", - "url": "https://pan.shumengya.top", - "status_code": 200, - "latency": 61, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:06:31.916946+08:00" - }, - { - "website_id": "188cb52c04cf639cfc2c8ba4", - "url_id": "cfe6229fb86b", - "url": "https://pan.shumengya.top", - "status_code": 200, - "latency": 81, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:11:31.9293141+08:00" - } - ], - "188cb53623f46b7872bf17b6_b53594e7285d": [ - { - "website_id": "188cb53623f46b7872bf17b6", - "url_id": "b53594e7285d", - "url": "https://git.shumengya.top", - "status_code": 200, - "latency": 218, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:01:30.35668+08:00" - }, - { - "website_id": "188cb53623f46b7872bf17b6", - "url_id": "b53594e7285d", - "url": "https://git.shumengya.top", - "status_code": 200, - "latency": 70, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:06:30.2094343+08:00" - }, - { - "website_id": "188cb53623f46b7872bf17b6", - "url_id": "b53594e7285d", - "url": "https://git.shumengya.top", - "status_code": 200, - "latency": 81, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:10:58.6188928+08:00" - }, - { - "website_id": "188cb53623f46b7872bf17b6", - "url_id": "b53594e7285d", - "url": "https://git.shumengya.top", - "status_code": 200, - "latency": 76, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:15:58.026826+08:00" - }, - { - "website_id": "188cb53623f46b7872bf17b6", - "url_id": "b53594e7285d", - "url": "https://git.shumengya.top", - "status_code": 200, - "latency": 80, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:20:58.1576909+08:00" - }, - { - "website_id": "188cb53623f46b7872bf17b6", - "url_id": "b53594e7285d", - "url": "https://git.shumengya.top", - "status_code": 200, - "latency": 69, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:25:58.1340808+08:00" - }, - { - "website_id": "188cb53623f46b7872bf17b6", - "url_id": "b53594e7285d", - "url": "https://git.shumengya.top", - "status_code": 200, - "latency": 81, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:30:58.0311078+08:00" - }, - { - "website_id": "188cb53623f46b7872bf17b6", - "url_id": "b53594e7285d", - "url": "https://git.shumengya.top", - "status_code": 200, - "latency": 168, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:35:58.1179367+08:00" - }, - { - "website_id": "188cb53623f46b7872bf17b6", - "url_id": "b53594e7285d", - "url": "https://git.shumengya.top", - "status_code": 200, - "latency": 71, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:40:58.1947148+08:00" - }, - { - "website_id": "188cb53623f46b7872bf17b6", - "url_id": "b53594e7285d", - "url": "https://git.shumengya.top", - "status_code": 200, - "latency": 87, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:45:58.185041+08:00" - }, - { - "website_id": "188cb53623f46b7872bf17b6", - "url_id": "b53594e7285d", - "url": "https://git.shumengya.top", - "status_code": 200, - "latency": 103, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:13:25.3623345+08:00" - }, - { - "website_id": "188cb53623f46b7872bf17b6", - "url_id": "b53594e7285d", - "url": "https://git.shumengya.top", - "status_code": 200, - "latency": 64, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:21:37.4991479+08:00" - }, - { - "website_id": "188cb53623f46b7872bf17b6", - "url_id": "b53594e7285d", - "url": "https://git.shumengya.top", - "status_code": 200, - "latency": 87, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:26:36.0251315+08:00" - }, - { - "website_id": "188cb53623f46b7872bf17b6", - "url_id": "b53594e7285d", - "url": "https://git.shumengya.top", - "status_code": 200, - "latency": 104, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:31:36.0351928+08:00" - }, - { - "website_id": "188cb53623f46b7872bf17b6", - "url_id": "b53594e7285d", - "url": "https://git.shumengya.top", - "status_code": 200, - "latency": 73, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:36:37.8692712+08:00" - }, - { - "website_id": "188cb53623f46b7872bf17b6", - "url_id": "b53594e7285d", - "url": "https://git.shumengya.top", - "status_code": 200, - "latency": 73, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:41:36.0108645+08:00" - }, - { - "website_id": "188cb53623f46b7872bf17b6", - "url_id": "b53594e7285d", - "url": "https://git.shumengya.top", - "status_code": 200, - "latency": 80, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:46:37.9647051+08:00" - }, - { - "website_id": "188cb53623f46b7872bf17b6", - "url_id": "b53594e7285d", - "url": "https://git.shumengya.top", - "status_code": 200, - "latency": 69, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:51:35.9978678+08:00" - }, - { - "website_id": "188cb53623f46b7872bf17b6", - "url_id": "b53594e7285d", - "url": "https://git.shumengya.top", - "status_code": 200, - "latency": 70, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:56:35.9987935+08:00" - }, - { - "website_id": "188cb53623f46b7872bf17b6", - "url_id": "b53594e7285d", - "url": "https://git.shumengya.top", - "status_code": 200, - "latency": 796, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:49:22.6029862+08:00" - }, - { - "website_id": "188cb53623f46b7872bf17b6", - "url_id": "b53594e7285d", - "url": "https://git.shumengya.top", - "status_code": 200, - "latency": 83, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:51:45.0298267+08:00" - }, - { - "website_id": "188cb53623f46b7872bf17b6", - "url_id": "b53594e7285d", - "url": "https://git.shumengya.top", - "status_code": 200, - "latency": 83, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:56:33.0129026+08:00" - }, - { - "website_id": "188cb53623f46b7872bf17b6", - "url_id": "b53594e7285d", - "url": "https://git.shumengya.top", - "status_code": 200, - "latency": 181, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:01:33.1054077+08:00" - }, - { - "website_id": "188cb53623f46b7872bf17b6", - "url_id": "b53594e7285d", - "url": "https://git.shumengya.top", - "status_code": 200, - "latency": 75, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:06:32.9932873+08:00" - }, - { - "website_id": "188cb53623f46b7872bf17b6", - "url_id": "b53594e7285d", - "url": "https://git.shumengya.top", - "status_code": 200, - "latency": 81, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:11:33.0152668+08:00" - } - ], - "188cb54b93ddd1e487bf347e_46d822694242": [ - { - "website_id": "188cb54b93ddd1e487bf347e", - "url_id": "46d822694242", - "url": "https://image.shumengya.top", - "status_code": 200, - "latency": 401, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:01:30.5401869+08:00" - }, - { - "website_id": "188cb54b93ddd1e487bf347e", - "url_id": "46d822694242", - "url": "https://image.shumengya.top", - "status_code": 200, - "latency": 263, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:06:30.4039563+08:00" - }, - { - "website_id": "188cb54b93ddd1e487bf347e", - "url_id": "46d822694242", - "url": "https://image.shumengya.top", - "status_code": 200, - "latency": 387, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:10:58.7478334+08:00" - }, - { - "website_id": "188cb54b93ddd1e487bf347e", - "url_id": "46d822694242", - "url": "https://image.shumengya.top", - "status_code": 200, - "latency": 261, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:15:58.2894545+08:00" - }, - { - "website_id": "188cb54b93ddd1e487bf347e", - "url_id": "46d822694242", - "url": "https://image.shumengya.top", - "status_code": 200, - "latency": 268, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:20:58.3522388+08:00" - }, - { - "website_id": "188cb54b93ddd1e487bf347e", - "url_id": "46d822694242", - "url": "https://image.shumengya.top", - "status_code": 200, - "latency": 262, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:25:58.3288223+08:00" - }, - { - "website_id": "188cb54b93ddd1e487bf347e", - "url_id": "46d822694242", - "url": "https://image.shumengya.top", - "status_code": 200, - "latency": 267, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:30:58.3434381+08:00" - }, - { - "website_id": "188cb54b93ddd1e487bf347e", - "url_id": "46d822694242", - "url": "https://image.shumengya.top", - "status_code": 200, - "latency": 265, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:35:58.3563323+08:00" - }, - { - "website_id": "188cb54b93ddd1e487bf347e", - "url_id": "46d822694242", - "url": "https://image.shumengya.top", - "status_code": 200, - "latency": 254, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:40:58.4016966+08:00" - }, - { - "website_id": "188cb54b93ddd1e487bf347e", - "url_id": "46d822694242", - "url": "https://image.shumengya.top", - "status_code": 200, - "latency": 265, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:45:58.3176474+08:00" - }, - { - "website_id": "188cb54b93ddd1e487bf347e", - "url_id": "46d822694242", - "url": "https://image.shumengya.top", - "status_code": 200, - "latency": 425, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:13:25.6848829+08:00" - }, - { - "website_id": "188cb54b93ddd1e487bf347e", - "url_id": "46d822694242", - "url": "https://image.shumengya.top", - "status_code": 200, - "latency": 275, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:21:37.3173115+08:00" - }, - { - "website_id": "188cb54b93ddd1e487bf347e", - "url_id": "46d822694242", - "url": "https://image.shumengya.top", - "status_code": 200, - "latency": 273, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:26:36.2118334+08:00" - }, - { - "website_id": "188cb54b93ddd1e487bf347e", - "url_id": "46d822694242", - "url": "https://image.shumengya.top", - "status_code": 200, - "latency": 292, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:31:36.2243852+08:00" - }, - { - "website_id": "188cb54b93ddd1e487bf347e", - "url_id": "46d822694242", - "url": "https://image.shumengya.top", - "status_code": 200, - "latency": 278, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:36:36.2076744+08:00" - }, - { - "website_id": "188cb54b93ddd1e487bf347e", - "url_id": "46d822694242", - "url": "https://image.shumengya.top", - "status_code": 200, - "latency": 268, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:41:36.5957907+08:00" - }, - { - "website_id": "188cb54b93ddd1e487bf347e", - "url_id": "46d822694242", - "url": "https://image.shumengya.top", - "status_code": 200, - "latency": 314, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:46:36.2442079+08:00" - }, - { - "website_id": "188cb54b93ddd1e487bf347e", - "url_id": "46d822694242", - "url": "https://image.shumengya.top", - "status_code": 200, - "latency": 242, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:51:36.1747383+08:00" - }, - { - "website_id": "188cb54b93ddd1e487bf347e", - "url_id": "46d822694242", - "url": "https://image.shumengya.top", - "status_code": 200, - "latency": 274, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:56:36.3862239+08:00" - }, - { - "website_id": "188cb54b93ddd1e487bf347e", - "url_id": "46d822694242", - "url": "https://image.shumengya.top", - "status_code": 200, - "latency": 908, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:49:22.7153944+08:00" - }, - { - "website_id": "188cb54b93ddd1e487bf347e", - "url_id": "46d822694242", - "url": "https://image.shumengya.top", - "status_code": 200, - "latency": 276, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:51:46.3098858+08:00" - }, - { - "website_id": "188cb54b93ddd1e487bf347e", - "url_id": "46d822694242", - "url": "https://image.shumengya.top", - "status_code": 200, - "latency": 290, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:56:34.3043215+08:00" - }, - { - "website_id": "188cb54b93ddd1e487bf347e", - "url_id": "46d822694242", - "url": "https://image.shumengya.top", - "status_code": 200, - "latency": 259, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:01:34.3740852+08:00" - }, - { - "website_id": "188cb54b93ddd1e487bf347e", - "url_id": "46d822694242", - "url": "https://image.shumengya.top", - "status_code": 200, - "latency": 286, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:06:34.2810697+08:00" - }, - { - "website_id": "188cb54b93ddd1e487bf347e", - "url_id": "46d822694242", - "url": "https://image.shumengya.top", - "status_code": 200, - "latency": 279, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:11:34.2972329+08:00" - } - ], - "188cb5507dcd2da0559bd6dd_6d5125db9d81": [ - { - "website_id": "188cb5507dcd2da0559bd6dd", - "url_id": "6d5125db9d81", - "url": "https://note.shumengya.top", - "status_code": 200, - "latency": 220, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:01:30.3582647+08:00" - }, - { - "website_id": "188cb5507dcd2da0559bd6dd", - "url_id": "6d5125db9d81", - "url": "https://note.shumengya.top", - "status_code": 200, - "latency": 63, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:06:30.2022436+08:00" - }, - { - "website_id": "188cb5507dcd2da0559bd6dd", - "url_id": "6d5125db9d81", - "url": "https://note.shumengya.top", - "status_code": 200, - "latency": 148, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:10:59.0707769+08:00" - }, - { - "website_id": "188cb5507dcd2da0559bd6dd", - "url_id": "6d5125db9d81", - "url": "https://note.shumengya.top", - "status_code": 200, - "latency": 51, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:15:58.0074865+08:00" - }, - { - "website_id": "188cb5507dcd2da0559bd6dd", - "url_id": "6d5125db9d81", - "url": "https://note.shumengya.top", - "status_code": 200, - "latency": 157, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:20:58.27495+08:00" - }, - { - "website_id": "188cb5507dcd2da0559bd6dd", - "url_id": "6d5125db9d81", - "url": "https://note.shumengya.top", - "status_code": 200, - "latency": 56, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:25:58.1315798+08:00" - }, - { - "website_id": "188cb5507dcd2da0559bd6dd", - "url_id": "6d5125db9d81", - "url": "https://note.shumengya.top", - "status_code": 200, - "latency": 67, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:30:58.0180465+08:00" - }, - { - "website_id": "188cb5507dcd2da0559bd6dd", - "url_id": "6d5125db9d81", - "url": "https://note.shumengya.top", - "status_code": 200, - "latency": 61, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:35:58.146403+08:00" - }, - { - "website_id": "188cb5507dcd2da0559bd6dd", - "url_id": "6d5125db9d81", - "url": "https://note.shumengya.top", - "status_code": 200, - "latency": 70, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:40:58.237799+08:00" - }, - { - "website_id": "188cb5507dcd2da0559bd6dd", - "url_id": "6d5125db9d81", - "url": "https://note.shumengya.top", - "status_code": 200, - "latency": 60, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:45:58.1664426+08:00" - }, - { - "website_id": "188cb5507dcd2da0559bd6dd", - "url_id": "6d5125db9d81", - "url": "https://note.shumengya.top", - "status_code": 200, - "latency": 169, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:13:25.4280383+08:00" - }, - { - "website_id": "188cb5507dcd2da0559bd6dd", - "url_id": "6d5125db9d81", - "url": "https://note.shumengya.top", - "status_code": 200, - "latency": 68, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:21:48.2246115+08:00" - }, - { - "website_id": "188cb5507dcd2da0559bd6dd", - "url_id": "6d5125db9d81", - "url": "https://note.shumengya.top", - "status_code": 200, - "latency": 75, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:26:36.0138816+08:00" - }, - { - "website_id": "188cb5507dcd2da0559bd6dd", - "url_id": "6d5125db9d81", - "url": "https://note.shumengya.top", - "status_code": 200, - "latency": 65, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:31:36.0850369+08:00" - }, - { - "website_id": "188cb5507dcd2da0559bd6dd", - "url_id": "6d5125db9d81", - "url": "https://note.shumengya.top", - "status_code": 200, - "latency": 70, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:36:36.1319975+08:00" - }, - { - "website_id": "188cb5507dcd2da0559bd6dd", - "url_id": "6d5125db9d81", - "url": "https://note.shumengya.top", - "status_code": 200, - "latency": 61, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:41:35.998855+08:00" - }, - { - "website_id": "188cb5507dcd2da0559bd6dd", - "url_id": "6d5125db9d81", - "url": "https://note.shumengya.top", - "status_code": 200, - "latency": 72, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:46:36.0011437+08:00" - }, - { - "website_id": "188cb5507dcd2da0559bd6dd", - "url_id": "6d5125db9d81", - "url": "https://note.shumengya.top", - "status_code": 200, - "latency": 68, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:51:35.9960971+08:00" - }, - { - "website_id": "188cb5507dcd2da0559bd6dd", - "url_id": "6d5125db9d81", - "url": "https://note.shumengya.top", - "status_code": 200, - "latency": 61, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:56:36.1307335+08:00" - }, - { - "website_id": "188cb5507dcd2da0559bd6dd", - "url_id": "6d5125db9d81", - "url": "https://note.shumengya.top", - "status_code": 200, - "latency": 100, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:49:25.2539084+08:00" - }, - { - "website_id": "188cb5507dcd2da0559bd6dd", - "url_id": "6d5125db9d81", - "url": "https://note.shumengya.top", - "status_code": 200, - "latency": 86, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:51:47.3982067+08:00" - }, - { - "website_id": "188cb5507dcd2da0559bd6dd", - "url_id": "6d5125db9d81", - "url": "https://note.shumengya.top", - "status_code": 200, - "latency": 83, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:56:35.3919529+08:00" - }, - { - "website_id": "188cb5507dcd2da0559bd6dd", - "url_id": "6d5125db9d81", - "url": "https://note.shumengya.top", - "status_code": 200, - "latency": 71, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:01:35.4457372+08:00" - }, - { - "website_id": "188cb5507dcd2da0559bd6dd", - "url_id": "6d5125db9d81", - "url": "https://note.shumengya.top", - "status_code": 200, - "latency": 100, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:06:35.3824803+08:00" - }, - { - "website_id": "188cb5507dcd2da0559bd6dd", - "url_id": "6d5125db9d81", - "url": "https://note.shumengya.top", - "status_code": 200, - "latency": 78, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:11:35.3832617+08:00" - } - ], - "188cb55693193b80d2383cc3_74a3b6e1f50e": [ - { - "website_id": "188cb55693193b80d2383cc3", - "url_id": "74a3b6e1f50e", - "url": "https://work.shumengya.top", - "status_code": 200, - "latency": 202, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:01:30.3410504+08:00" - }, - { - "website_id": "188cb55693193b80d2383cc3", - "url_id": "74a3b6e1f50e", - "url": "https://work.shumengya.top", - "status_code": 200, - "latency": 63, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:06:30.2017319+08:00" - }, - { - "website_id": "188cb55693193b80d2383cc3", - "url_id": "74a3b6e1f50e", - "url": "https://work.shumengya.top", - "status_code": 200, - "latency": 79, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:10:58.9158794+08:00" - }, - { - "website_id": "188cb55693193b80d2383cc3", - "url_id": "74a3b6e1f50e", - "url": "https://work.shumengya.top", - "status_code": 200, - "latency": 68, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:15:58.1553447+08:00" - }, - { - "website_id": "188cb55693193b80d2383cc3", - "url_id": "74a3b6e1f50e", - "url": "https://work.shumengya.top", - "status_code": 200, - "latency": 67, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:20:58.1913031+08:00" - }, - { - "website_id": "188cb55693193b80d2383cc3", - "url_id": "74a3b6e1f50e", - "url": "https://work.shumengya.top", - "status_code": 200, - "latency": 53, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:25:58.1297449+08:00" - }, - { - "website_id": "188cb55693193b80d2383cc3", - "url_id": "74a3b6e1f50e", - "url": "https://work.shumengya.top", - "status_code": 200, - "latency": 63, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:30:58.0955142+08:00" - }, - { - "website_id": "188cb55693193b80d2383cc3", - "url_id": "74a3b6e1f50e", - "url": "https://work.shumengya.top", - "status_code": 200, - "latency": 179, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:35:58.4274524+08:00" - }, - { - "website_id": "188cb55693193b80d2383cc3", - "url_id": "74a3b6e1f50e", - "url": "https://work.shumengya.top", - "status_code": 200, - "latency": 50, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:40:58.2373569+08:00" - }, - { - "website_id": "188cb55693193b80d2383cc3", - "url_id": "74a3b6e1f50e", - "url": "https://work.shumengya.top", - "status_code": 200, - "latency": 188, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:45:58.2930692+08:00" - }, - { - "website_id": "188cb55693193b80d2383cc3", - "url_id": "74a3b6e1f50e", - "url": "https://work.shumengya.top", - "status_code": 200, - "latency": 53, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:13:25.3953208+08:00" - }, - { - "website_id": "188cb55693193b80d2383cc3", - "url_id": "74a3b6e1f50e", - "url": "https://work.shumengya.top", - "status_code": 200, - "latency": 63, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:21:48.1556313+08:00" - }, - { - "website_id": "188cb55693193b80d2383cc3", - "url_id": "74a3b6e1f50e", - "url": "https://work.shumengya.top", - "status_code": 200, - "latency": 65, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:26:36.1441675+08:00" - }, - { - "website_id": "188cb55693193b80d2383cc3", - "url_id": "74a3b6e1f50e", - "url": "https://work.shumengya.top", - "status_code": 200, - "latency": 70, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:31:36.0850369+08:00" - }, - { - "website_id": "188cb55693193b80d2383cc3", - "url_id": "74a3b6e1f50e", - "url": "https://work.shumengya.top", - "status_code": 200, - "latency": 65, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:36:35.9941188+08:00" - }, - { - "website_id": "188cb55693193b80d2383cc3", - "url_id": "74a3b6e1f50e", - "url": "https://work.shumengya.top", - "status_code": 200, - "latency": 58, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:41:35.9966+08:00" - }, - { - "website_id": "188cb55693193b80d2383cc3", - "url_id": "74a3b6e1f50e", - "url": "https://work.shumengya.top", - "status_code": 200, - "latency": 67, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:46:35.9960108+08:00" - }, - { - "website_id": "188cb55693193b80d2383cc3", - "url_id": "74a3b6e1f50e", - "url": "https://work.shumengya.top", - "status_code": 200, - "latency": 192, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:51:37.5052071+08:00" - }, - { - "website_id": "188cb55693193b80d2383cc3", - "url_id": "74a3b6e1f50e", - "url": "https://work.shumengya.top", - "status_code": 200, - "latency": 54, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:56:36.4407981+08:00" - }, - { - "website_id": "188cb55693193b80d2383cc3", - "url_id": "74a3b6e1f50e", - "url": "https://work.shumengya.top", - "status_code": 200, - "latency": 148, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:49:25.1427358+08:00" - }, - { - "website_id": "188cb55693193b80d2383cc3", - "url_id": "74a3b6e1f50e", - "url": "https://work.shumengya.top", - "status_code": 200, - "latency": 106, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:51:48.5087751+08:00" - }, - { - "website_id": "188cb55693193b80d2383cc3", - "url_id": "74a3b6e1f50e", - "url": "https://work.shumengya.top", - "status_code": 200, - "latency": 92, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:56:36.4908149+08:00" - }, - { - "website_id": "188cb55693193b80d2383cc3", - "url_id": "74a3b6e1f50e", - "url": "https://work.shumengya.top", - "status_code": 200, - "latency": 89, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:01:36.5423098+08:00" - }, - { - "website_id": "188cb55693193b80d2383cc3", - "url_id": "74a3b6e1f50e", - "url": "https://work.shumengya.top", - "status_code": 200, - "latency": 194, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:06:36.5772777+08:00" - }, - { - "website_id": "188cb55693193b80d2383cc3", - "url_id": "74a3b6e1f50e", - "url": "https://work.shumengya.top", - "status_code": 200, - "latency": 67, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:11:36.4555473+08:00" - } - ], - "188cb56d8df57dc8c66d42ee_e8f8fd4cf9b9": [ - { - "website_id": "188cb56d8df57dc8c66d42ee", - "url_id": "e8f8fd4cf9b9", - "url": "https://infogenie.shumengya.top", - "status_code": 200, - "latency": 183, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:01:30.3219531+08:00" - }, - { - "website_id": "188cb56d8df57dc8c66d42ee", - "url_id": "e8f8fd4cf9b9", - "url": "https://infogenie.shumengya.top", - "status_code": 200, - "latency": 80, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:06:30.2191819+08:00" - }, - { - "website_id": "188cb56d8df57dc8c66d42ee", - "url_id": "e8f8fd4cf9b9", - "url": "https://infogenie.shumengya.top", - "status_code": 200, - "latency": 141, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:10:59.2126191+08:00" - }, - { - "website_id": "188cb56d8df57dc8c66d42ee", - "url_id": "e8f8fd4cf9b9", - "url": "https://infogenie.shumengya.top", - "status_code": 200, - "latency": 52, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:15:58.1386721+08:00" - }, - { - "website_id": "188cb56d8df57dc8c66d42ee", - "url_id": "e8f8fd4cf9b9", - "url": "https://infogenie.shumengya.top", - "status_code": 200, - "latency": 176, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:20:58.3229221+08:00" - }, - { - "website_id": "188cb56d8df57dc8c66d42ee", - "url_id": "e8f8fd4cf9b9", - "url": "https://infogenie.shumengya.top", - "status_code": 200, - "latency": 57, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:25:58.1357213+08:00" - }, - { - "website_id": "188cb56d8df57dc8c66d42ee", - "url_id": "e8f8fd4cf9b9", - "url": "https://infogenie.shumengya.top", - "status_code": 200, - "latency": 64, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:30:58.3288357+08:00" - }, - { - "website_id": "188cb56d8df57dc8c66d42ee", - "url_id": "e8f8fd4cf9b9", - "url": "https://infogenie.shumengya.top", - "status_code": 200, - "latency": 60, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:35:58.3764123+08:00" - }, - { - "website_id": "188cb56d8df57dc8c66d42ee", - "url_id": "e8f8fd4cf9b9", - "url": "https://infogenie.shumengya.top", - "status_code": 200, - "latency": 67, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:40:58.2604107+08:00" - }, - { - "website_id": "188cb56d8df57dc8c66d42ee", - "url_id": "e8f8fd4cf9b9", - "url": "https://infogenie.shumengya.top", - "status_code": 200, - "latency": 64, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:45:58.3193464+08:00" - }, - { - "website_id": "188cb56d8df57dc8c66d42ee", - "url_id": "e8f8fd4cf9b9", - "url": "https://infogenie.shumengya.top", - "status_code": 200, - "latency": 55, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:13:25.3991381+08:00" - }, - { - "website_id": "188cb56d8df57dc8c66d42ee", - "url_id": "e8f8fd4cf9b9", - "url": "https://infogenie.shumengya.top", - "status_code": 200, - "latency": 197, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:21:48.42258+08:00" - }, - { - "website_id": "188cb56d8df57dc8c66d42ee", - "url_id": "e8f8fd4cf9b9", - "url": "https://infogenie.shumengya.top", - "status_code": 200, - "latency": 52, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:26:36.1351696+08:00" - }, - { - "website_id": "188cb56d8df57dc8c66d42ee", - "url_id": "e8f8fd4cf9b9", - "url": "https://infogenie.shumengya.top", - "status_code": 200, - "latency": 65, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:31:37.0636641+08:00" - }, - { - "website_id": "188cb56d8df57dc8c66d42ee", - "url_id": "e8f8fd4cf9b9", - "url": "https://infogenie.shumengya.top", - "status_code": 200, - "latency": 56, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:36:36.1298393+08:00" - }, - { - "website_id": "188cb56d8df57dc8c66d42ee", - "url_id": "e8f8fd4cf9b9", - "url": "https://infogenie.shumengya.top", - "status_code": 200, - "latency": 64, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:41:37.0242441+08:00" - }, - { - "website_id": "188cb56d8df57dc8c66d42ee", - "url_id": "e8f8fd4cf9b9", - "url": "https://infogenie.shumengya.top", - "status_code": 200, - "latency": 80, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:46:36.0092883+08:00" - }, - { - "website_id": "188cb56d8df57dc8c66d42ee", - "url_id": "e8f8fd4cf9b9", - "url": "https://infogenie.shumengya.top", - "status_code": 200, - "latency": 72, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:51:37.5774464+08:00" - }, - { - "website_id": "188cb56d8df57dc8c66d42ee", - "url_id": "e8f8fd4cf9b9", - "url": "https://infogenie.shumengya.top", - "status_code": 200, - "latency": 59, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:56:37.5533295+08:00" - }, - { - "website_id": "188cb56d8df57dc8c66d42ee", - "url_id": "e8f8fd4cf9b9", - "url": "https://infogenie.shumengya.top", - "status_code": 200, - "latency": 77, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:49:25.3367397+08:00" - }, - { - "website_id": "188cb56d8df57dc8c66d42ee", - "url_id": "e8f8fd4cf9b9", - "url": "https://infogenie.shumengya.top", - "status_code": 200, - "latency": 76, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:51:49.5859382+08:00" - }, - { - "website_id": "188cb56d8df57dc8c66d42ee", - "url_id": "e8f8fd4cf9b9", - "url": "https://infogenie.shumengya.top", - "status_code": 200, - "latency": 163, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:56:37.6617602+08:00" - }, - { - "website_id": "188cb56d8df57dc8c66d42ee", - "url_id": "e8f8fd4cf9b9", - "url": "https://infogenie.shumengya.top", - "status_code": 200, - "latency": 70, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:01:37.6150401+08:00" - }, - { - "website_id": "188cb56d8df57dc8c66d42ee", - "url_id": "e8f8fd4cf9b9", - "url": "https://infogenie.shumengya.top", - "status_code": 200, - "latency": 78, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:06:37.6557552+08:00" - }, - { - "website_id": "188cb56d8df57dc8c66d42ee", - "url_id": "e8f8fd4cf9b9", - "url": "https://infogenie.shumengya.top", - "status_code": 200, - "latency": 71, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:11:37.5274099+08:00" - } - ], - "188cb574b122f71087e53ae1_2f9ffdedd860": [ - { - "website_id": "188cb574b122f71087e53ae1", - "url_id": "2f9ffdedd860", - "url": "https://survey.shumengya.top", - "status_code": 502, - "latency": 151, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:01:30.3868735+08:00" - }, - { - "website_id": "188cb574b122f71087e53ae1", - "url_id": "2f9ffdedd860", - "url": "https://survey.shumengya.top", - "status_code": 502, - "latency": 49, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:06:30.3471695+08:00" - }, - { - "website_id": "188cb574b122f71087e53ae1", - "url_id": "2f9ffdedd860", - "url": "https://survey.shumengya.top", - "status_code": 502, - "latency": 168, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:10:59.2399023+08:00" - }, - { - "website_id": "188cb574b122f71087e53ae1", - "url_id": "2f9ffdedd860", - "url": "https://survey.shumengya.top", - "status_code": 502, - "latency": 50, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:15:58.1473997+08:00" - }, - { - "website_id": "188cb574b122f71087e53ae1", - "url_id": "2f9ffdedd860", - "url": "https://survey.shumengya.top", - "status_code": 502, - "latency": 56, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:20:58.2069431+08:00" - }, - { - "website_id": "188cb574b122f71087e53ae1", - "url_id": "2f9ffdedd860", - "url": "https://survey.shumengya.top", - "status_code": 502, - "latency": 49, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:25:58.0652905+08:00" - }, - { - "website_id": "188cb574b122f71087e53ae1", - "url_id": "2f9ffdedd860", - "url": "https://survey.shumengya.top", - "status_code": 502, - "latency": 50, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:30:58.0007479+08:00" - }, - { - "website_id": "188cb574b122f71087e53ae1", - "url_id": "2f9ffdedd860", - "url": "https://survey.shumengya.top", - "status_code": 502, - "latency": 134, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:35:58.0845016+08:00" - }, - { - "website_id": "188cb574b122f71087e53ae1", - "url_id": "2f9ffdedd860", - "url": "https://survey.shumengya.top", - "status_code": 502, - "latency": 54, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:40:58.249977+08:00" - }, - { - "website_id": "188cb574b122f71087e53ae1", - "url_id": "2f9ffdedd860", - "url": "https://survey.shumengya.top", - "status_code": 502, - "latency": 76, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:45:58.0273731+08:00" - }, - { - "website_id": "188cb574b122f71087e53ae1", - "url_id": "2f9ffdedd860", - "url": "https://survey.shumengya.top", - "status_code": 502, - "latency": 41, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T11:13:37.5653881+08:00" - }, - { - "website_id": "188cb574b122f71087e53ae1", - "url_id": "2f9ffdedd860", - "url": "https://survey.shumengya.top", - "status_code": 502, - "latency": 39, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T11:21:48.2465005+08:00" - }, - { - "website_id": "188cb574b122f71087e53ae1", - "url_id": "2f9ffdedd860", - "url": "https://survey.shumengya.top", - "status_code": 502, - "latency": 42, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T11:26:48.3318822+08:00" - }, - { - "website_id": "188cb574b122f71087e53ae1", - "url_id": "2f9ffdedd860", - "url": "https://survey.shumengya.top", - "status_code": 502, - "latency": 44, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T11:31:48.1706242+08:00" - }, - { - "website_id": "188cb574b122f71087e53ae1", - "url_id": "2f9ffdedd860", - "url": "https://survey.shumengya.top", - "status_code": 502, - "latency": 43, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T11:36:48.3965601+08:00" - }, - { - "website_id": "188cb574b122f71087e53ae1", - "url_id": "2f9ffdedd860", - "url": "https://survey.shumengya.top", - "status_code": 502, - "latency": 40, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T11:41:48.1408271+08:00" - }, - { - "website_id": "188cb574b122f71087e53ae1", - "url_id": "2f9ffdedd860", - "url": "https://survey.shumengya.top", - "status_code": 502, - "latency": 43, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T11:46:48.5671551+08:00" - }, - { - "website_id": "188cb574b122f71087e53ae1", - "url_id": "2f9ffdedd860", - "url": "https://survey.shumengya.top", - "status_code": 502, - "latency": 46, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T11:51:49.7916386+08:00" - }, - { - "website_id": "188cb574b122f71087e53ae1", - "url_id": "2f9ffdedd860", - "url": "https://survey.shumengya.top", - "status_code": 502, - "latency": 39, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T11:56:48.1087922+08:00" - }, - { - "website_id": "188cb574b122f71087e53ae1", - "url_id": "2f9ffdedd860", - "url": "https://survey.shumengya.top", - "status_code": 502, - "latency": 42, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T12:49:35.0070849+08:00" - }, - { - "website_id": "188cb574b122f71087e53ae1", - "url_id": "2f9ffdedd860", - "url": "https://survey.shumengya.top", - "status_code": 502, - "latency": 58, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T12:52:13.9042824+08:00" - }, - { - "website_id": "188cb574b122f71087e53ae1", - "url_id": "2f9ffdedd860", - "url": "https://survey.shumengya.top", - "status_code": 502, - "latency": 44, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T12:56:50.8682468+08:00" - }, - { - "website_id": "188cb574b122f71087e53ae1", - "url_id": "2f9ffdedd860", - "url": "https://survey.shumengya.top", - "status_code": 502, - "latency": 37, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T13:01:50.8240397+08:00" - }, - { - "website_id": "188cb574b122f71087e53ae1", - "url_id": "2f9ffdedd860", - "url": "https://survey.shumengya.top", - "status_code": 502, - "latency": 78, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T13:06:50.8919213+08:00" - }, - { - "website_id": "188cb574b122f71087e53ae1", - "url_id": "2f9ffdedd860", - "url": "https://survey.shumengya.top", - "status_code": 502, - "latency": 42, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T13:11:50.7436019+08:00" - } - ], - "188cb5a19b836e80d93df86a_8a4ebcb7eb03": [ - { - "website_id": "188cb5a19b836e80d93df86a", - "url_id": "8a4ebcb7eb03", - "url": "https://short.shumengya.top", - "status_code": 200, - "latency": 186, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:01:30.4838945+08:00" - }, - { - "website_id": "188cb5a19b836e80d93df86a", - "url_id": "8a4ebcb7eb03", - "url": "https://short.shumengya.top", - "status_code": 200, - "latency": 50, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:06:30.3516696+08:00" - }, - { - "website_id": "188cb5a19b836e80d93df86a", - "url_id": "8a4ebcb7eb03", - "url": "https://short.shumengya.top", - "status_code": 200, - "latency": 209, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:10:58.1587354+08:00" - }, - { - "website_id": "188cb5a19b836e80d93df86a", - "url_id": "8a4ebcb7eb03", - "url": "https://short.shumengya.top", - "status_code": 200, - "latency": 63, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:15:58.2002149+08:00" - }, - { - "website_id": "188cb5a19b836e80d93df86a", - "url_id": "8a4ebcb7eb03", - "url": "https://short.shumengya.top", - "status_code": 200, - "latency": 193, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:20:58.2099257+08:00" - }, - { - "website_id": "188cb5a19b836e80d93df86a", - "url_id": "8a4ebcb7eb03", - "url": "https://short.shumengya.top", - "status_code": 200, - "latency": 50, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:25:58.2389192+08:00" - }, - { - "website_id": "188cb5a19b836e80d93df86a", - "url_id": "8a4ebcb7eb03", - "url": "https://short.shumengya.top", - "status_code": 200, - "latency": 75, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:30:58.0261897+08:00" - }, - { - "website_id": "188cb5a19b836e80d93df86a", - "url_id": "8a4ebcb7eb03", - "url": "https://short.shumengya.top", - "status_code": 200, - "latency": 72, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:35:58.0897618+08:00" - }, - { - "website_id": "188cb5a19b836e80d93df86a", - "url_id": "8a4ebcb7eb03", - "url": "https://short.shumengya.top", - "status_code": 200, - "latency": 64, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:40:58.3014967+08:00" - }, - { - "website_id": "188cb5a19b836e80d93df86a", - "url_id": "8a4ebcb7eb03", - "url": "https://short.shumengya.top", - "status_code": 200, - "latency": 88, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:45:58.039384+08:00" - }, - { - "website_id": "188cb5a19b836e80d93df86a", - "url_id": "8a4ebcb7eb03", - "url": "https://short.shumengya.top", - "status_code": 200, - "latency": 64, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:13:25.4167746+08:00" - }, - { - "website_id": "188cb5a19b836e80d93df86a", - "url_id": "8a4ebcb7eb03", - "url": "https://short.shumengya.top", - "status_code": 200, - "latency": 71, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:21:38.4794688+08:00" - }, - { - "website_id": "188cb5a19b836e80d93df86a", - "url_id": "8a4ebcb7eb03", - "url": "https://short.shumengya.top", - "status_code": 200, - "latency": 69, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:26:36.20857+08:00" - }, - { - "website_id": "188cb5a19b836e80d93df86a", - "url_id": "8a4ebcb7eb03", - "url": "https://short.shumengya.top", - "status_code": 200, - "latency": 64, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:31:37.3264248+08:00" - }, - { - "website_id": "188cb5a19b836e80d93df86a", - "url_id": "8a4ebcb7eb03", - "url": "https://short.shumengya.top", - "status_code": 200, - "latency": 64, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:36:35.9928867+08:00" - }, - { - "website_id": "188cb5a19b836e80d93df86a", - "url_id": "8a4ebcb7eb03", - "url": "https://short.shumengya.top", - "status_code": 200, - "latency": 59, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:41:36.056479+08:00" - }, - { - "website_id": "188cb5a19b836e80d93df86a", - "url_id": "8a4ebcb7eb03", - "url": "https://short.shumengya.top", - "status_code": 200, - "latency": 75, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:46:36.0027837+08:00" - }, - { - "website_id": "188cb5a19b836e80d93df86a", - "url_id": "8a4ebcb7eb03", - "url": "https://short.shumengya.top", - "status_code": 200, - "latency": 71, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:51:37.3122906+08:00" - }, - { - "website_id": "188cb5a19b836e80d93df86a", - "url_id": "8a4ebcb7eb03", - "url": "https://short.shumengya.top", - "status_code": 200, - "latency": 65, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:56:36.4378324+08:00" - }, - { - "website_id": "188cb5a19b836e80d93df86a", - "url_id": "8a4ebcb7eb03", - "url": "https://short.shumengya.top", - "status_code": 200, - "latency": 59, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:49:26.4368015+08:00" - }, - { - "website_id": "188cb5a19b836e80d93df86a", - "url_id": "8a4ebcb7eb03", - "url": "https://short.shumengya.top", - "status_code": 200, - "latency": 60, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:52:14.9653754+08:00" - }, - { - "website_id": "188cb5a19b836e80d93df86a", - "url_id": "8a4ebcb7eb03", - "url": "https://short.shumengya.top", - "status_code": 200, - "latency": 56, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:56:51.9306606+08:00" - }, - { - "website_id": "188cb5a19b836e80d93df86a", - "url_id": "8a4ebcb7eb03", - "url": "https://short.shumengya.top", - "status_code": 200, - "latency": 60, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:01:51.8845781+08:00" - }, - { - "website_id": "188cb5a19b836e80d93df86a", - "url_id": "8a4ebcb7eb03", - "url": "https://short.shumengya.top", - "status_code": 200, - "latency": 75, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:06:51.9673672+08:00" - }, - { - "website_id": "188cb5a19b836e80d93df86a", - "url_id": "8a4ebcb7eb03", - "url": "https://short.shumengya.top", - "status_code": 200, - "latency": 63, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:11:51.807611+08:00" - } - ], - "188cb6da20586d34df88b086_e7e250eb8985": [ - { - "website_id": "188cb6da20586d34df88b086", - "url_id": "e7e250eb8985", - "url": "https://bottle.shumengya.top", - "status_code": 200, - "latency": 181, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:01:30.4962871+08:00" - }, - { - "website_id": "188cb6da20586d34df88b086", - "url_id": "e7e250eb8985", - "url": "https://bottle.shumengya.top", - "status_code": 200, - "latency": 52, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:06:30.3225062+08:00" - }, - { - "website_id": "188cb6da20586d34df88b086", - "url_id": "e7e250eb8985", - "url": "https://bottle.shumengya.top", - "status_code": 200, - "latency": 197, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:10:58.1470186+08:00" - }, - { - "website_id": "188cb6da20586d34df88b086", - "url_id": "e7e250eb8985", - "url": "https://bottle.shumengya.top", - "status_code": 200, - "latency": 66, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:15:58.2049811+08:00" - }, - { - "website_id": "188cb6da20586d34df88b086", - "url_id": "e7e250eb8985", - "url": "https://bottle.shumengya.top", - "status_code": 200, - "latency": 71, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:20:58.2633083+08:00" - }, - { - "website_id": "188cb6da20586d34df88b086", - "url_id": "e7e250eb8985", - "url": "https://bottle.shumengya.top", - "status_code": 200, - "latency": 53, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:25:58.2671177+08:00" - }, - { - "website_id": "188cb6da20586d34df88b086", - "url_id": "e7e250eb8985", - "url": "https://bottle.shumengya.top", - "status_code": 200, - "latency": 162, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:30:58.1747423+08:00" - }, - { - "website_id": "188cb6da20586d34df88b086", - "url_id": "e7e250eb8985", - "url": "https://bottle.shumengya.top", - "status_code": 200, - "latency": 63, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:35:58.0845016+08:00" - }, - { - "website_id": "188cb6da20586d34df88b086", - "url_id": "e7e250eb8985", - "url": "https://bottle.shumengya.top", - "status_code": 200, - "latency": 170, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:40:58.1218393+08:00" - }, - { - "website_id": "188cb6da20586d34df88b086", - "url_id": "e7e250eb8985", - "url": "https://bottle.shumengya.top", - "status_code": 200, - "latency": 65, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:45:58.2166006+08:00" - }, - { - "website_id": "188cb6da20586d34df88b086", - "url_id": "e7e250eb8985", - "url": "https://bottle.shumengya.top", - "status_code": 200, - "latency": 72, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:13:25.434538+08:00" - }, - { - "website_id": "188cb6da20586d34df88b086", - "url_id": "e7e250eb8985", - "url": "https://bottle.shumengya.top", - "status_code": 200, - "latency": 66, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:21:38.5465937+08:00" - }, - { - "website_id": "188cb6da20586d34df88b086", - "url_id": "e7e250eb8985", - "url": "https://bottle.shumengya.top", - "status_code": 200, - "latency": 164, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:26:36.3086549+08:00" - }, - { - "website_id": "188cb6da20586d34df88b086", - "url_id": "e7e250eb8985", - "url": "https://bottle.shumengya.top", - "status_code": 200, - "latency": 60, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:31:37.261235+08:00" - }, - { - "website_id": "188cb6da20586d34df88b086", - "url_id": "e7e250eb8985", - "url": "https://bottle.shumengya.top", - "status_code": 200, - "latency": 60, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:36:36.18989+08:00" - }, - { - "website_id": "188cb6da20586d34df88b086", - "url_id": "e7e250eb8985", - "url": "https://bottle.shumengya.top", - "status_code": 200, - "latency": 59, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:41:36.0575461+08:00" - }, - { - "website_id": "188cb6da20586d34df88b086", - "url_id": "e7e250eb8985", - "url": "https://bottle.shumengya.top", - "status_code": 200, - "latency": 70, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:46:36.0579512+08:00" - }, - { - "website_id": "188cb6da20586d34df88b086", - "url_id": "e7e250eb8985", - "url": "https://bottle.shumengya.top", - "status_code": 200, - "latency": 65, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:51:38.7680572+08:00" - }, - { - "website_id": "188cb6da20586d34df88b086", - "url_id": "e7e250eb8985", - "url": "https://bottle.shumengya.top", - "status_code": 200, - "latency": 59, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:56:36.366661+08:00" - }, - { - "website_id": "188cb6da20586d34df88b086", - "url_id": "e7e250eb8985", - "url": "https://bottle.shumengya.top", - "status_code": 200, - "latency": 62, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:49:26.4993218+08:00" - }, - { - "website_id": "188cb6da20586d34df88b086", - "url_id": "e7e250eb8985", - "url": "https://bottle.shumengya.top", - "status_code": 200, - "latency": 65, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:52:16.0318489+08:00" - }, - { - "website_id": "188cb6da20586d34df88b086", - "url_id": "e7e250eb8985", - "url": "https://bottle.shumengya.top", - "status_code": 200, - "latency": 75, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:56:53.0149034+08:00" - }, - { - "website_id": "188cb6da20586d34df88b086", - "url_id": "e7e250eb8985", - "url": "https://bottle.shumengya.top", - "status_code": 200, - "latency": 62, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:01:52.95535+08:00" - }, - { - "website_id": "188cb6da20586d34df88b086", - "url_id": "e7e250eb8985", - "url": "https://bottle.shumengya.top", - "status_code": 200, - "latency": 80, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:06:53.0483776+08:00" - }, - { - "website_id": "188cb6da20586d34df88b086", - "url_id": "e7e250eb8985", - "url": "https://bottle.shumengya.top", - "status_code": 200, - "latency": 82, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:11:52.8981237+08:00" - } - ], - "188cb6ff716e2590408704ee_7bf1e5ddd890": null, - "188cb6ff716e2590408704ee_85620784f1a3": [ - { - "website_id": "188cb6ff716e2590408704ee", - "url_id": "85620784f1a3", - "url": "https://reference.smyhub.com", - "status_code": 200, - "latency": 1057, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:01:31.4309547+08:00" - }, - { - "website_id": "188cb6ff716e2590408704ee", - "url_id": "85620784f1a3", - "url": "https://reference.smyhub.com", - "status_code": 200, - "latency": 935, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:06:31.3944416+08:00" - }, - { - "website_id": "188cb6ff716e2590408704ee", - "url_id": "85620784f1a3", - "url": "https://reference.smyhub.com", - "status_code": 200, - "latency": 984, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:10:59.2015574+08:00" - }, - { - "website_id": "188cb6ff716e2590408704ee", - "url_id": "85620784f1a3", - "url": "https://reference.smyhub.com", - "status_code": 200, - "latency": 934, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:15:59.1395067+08:00" - }, - { - "website_id": "188cb6ff716e2590408704ee", - "url_id": "85620784f1a3", - "url": "https://reference.smyhub.com", - "status_code": 200, - "latency": 889, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:20:59.1395019+08:00" - }, - { - "website_id": "188cb6ff716e2590408704ee", - "url_id": "85620784f1a3", - "url": "https://reference.smyhub.com", - "status_code": 200, - "latency": 893, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:25:59.4205596+08:00" - }, - { - "website_id": "188cb6ff716e2590408704ee", - "url_id": "85620784f1a3", - "url": "https://reference.smyhub.com", - "status_code": 200, - "latency": 795, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:30:58.8590597+08:00" - }, - { - "website_id": "188cb6ff716e2590408704ee", - "url_id": "85620784f1a3", - "url": "https://reference.smyhub.com", - "status_code": 200, - "latency": 877, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:35:58.9582784+08:00" - }, - { - "website_id": "188cb6ff716e2590408704ee", - "url_id": "85620784f1a3", - "url": "https://reference.smyhub.com", - "status_code": 200, - "latency": 1895, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:40:59.8984226+08:00" - }, - { - "website_id": "188cb6ff716e2590408704ee", - "url_id": "85620784f1a3", - "url": "https://reference.smyhub.com", - "status_code": 200, - "latency": 753, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:45:59.0855824+08:00" - }, - { - "website_id": "188cb6ff716e2590408704ee", - "url_id": "85620784f1a3", - "url": "https://reference.smyhub.com", - "status_code": 200, - "latency": 744, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:13:26.1464952+08:00" - }, - { - "website_id": "188cb6ff716e2590408704ee", - "url_id": "85620784f1a3", - "url": "https://reference.smyhub.com", - "status_code": 200, - "latency": 766, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:21:39.3201729+08:00" - }, - { - "website_id": "188cb6ff716e2590408704ee", - "url_id": "85620784f1a3", - "url": "https://reference.smyhub.com", - "status_code": 200, - "latency": 771, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:26:36.9848466+08:00" - }, - { - "website_id": "188cb6ff716e2590408704ee", - "url_id": "85620784f1a3", - "url": "https://reference.smyhub.com", - "status_code": 200, - "latency": 1008, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:31:49.1286652+08:00" - }, - { - "website_id": "188cb6ff716e2590408704ee", - "url_id": "85620784f1a3", - "url": "https://reference.smyhub.com", - "status_code": 200, - "latency": 868, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:36:36.9889776+08:00" - }, - { - "website_id": "188cb6ff716e2590408704ee", - "url_id": "85620784f1a3", - "url": "https://reference.smyhub.com", - "status_code": 200, - "latency": 957, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:41:36.9600621+08:00" - }, - { - "website_id": "188cb6ff716e2590408704ee", - "url_id": "85620784f1a3", - "url": "https://reference.smyhub.com", - "status_code": 200, - "latency": 917, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:46:36.9215451+08:00" - }, - { - "website_id": "188cb6ff716e2590408704ee", - "url_id": "85620784f1a3", - "url": "https://reference.smyhub.com", - "status_code": 200, - "latency": 970, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:51:38.701653+08:00" - }, - { - "website_id": "188cb6ff716e2590408704ee", - "url_id": "85620784f1a3", - "url": "https://reference.smyhub.com", - "status_code": 200, - "latency": 768, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:56:37.2141041+08:00" - }, - { - "website_id": "188cb6ff716e2590408704ee", - "url_id": "85620784f1a3", - "url": "https://reference.smyhub.com", - "status_code": 200, - "latency": 793, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:49:27.295939+08:00" - }, - { - "website_id": "188cb6ff716e2590408704ee", - "url_id": "85620784f1a3", - "url": "https://reference.smyhub.com", - "status_code": 200, - "latency": 793, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:52:17.8409257+08:00" - }, - { - "website_id": "188cb6ff716e2590408704ee", - "url_id": "85620784f1a3", - "url": "https://reference.smyhub.com", - "status_code": 200, - "latency": 824, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:53:59.5761674+08:00" - }, - { - "website_id": "188cb6ff716e2590408704ee", - "url_id": "85620784f1a3", - "url": "https://reference.smyhub.com", - "status_code": 200, - "latency": 3552, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:56:58.1972327+08:00" - }, - { - "website_id": "188cb6ff716e2590408704ee", - "url_id": "85620784f1a3", - "url": "https://reference.smyhub.com", - "status_code": 200, - "latency": 908, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:01:54.8688611+08:00" - }, - { - "website_id": "188cb6ff716e2590408704ee", - "url_id": "85620784f1a3", - "url": "https://reference.smyhub.com", - "status_code": 200, - "latency": 898, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:06:55.274691+08:00" - }, - { - "website_id": "188cb6ff716e2590408704ee", - "url_id": "85620784f1a3", - "url": "https://reference.smyhub.com", - "status_code": 200, - "latency": 869, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:11:55.0292809+08:00" - } - ], - "188cb708e8cf52a49a436570_4d9ec8c9ed04": null, - "188cb708e8cf52a49a436570_dcc07e30a25a": [ - { - "website_id": "188cb708e8cf52a49a436570", - "url_id": "dcc07e30a25a", - "url": "https://mind-map.smyhub.com", - "status_code": 0, - "latency": 102, - "is_up": false, - "error": "Get \"https://mind-map.smyhub.com\": dial tcp: lookup mind-map.smyhub.com: no such host", - "checked_at": "2026-03-06T10:01:30.4248443+08:00" - }, - { - "website_id": "188cb708e8cf52a49a436570", - "url_id": "dcc07e30a25a", - "url": "https://mind-map.smyhub.com", - "status_code": 0, - "latency": 95, - "is_up": false, - "error": "Get \"https://mind-map.smyhub.com\": dial tcp: lookup mind-map.smyhub.com: no such host", - "checked_at": "2026-03-06T10:06:30.4931186+08:00" - }, - { - "website_id": "188cb708e8cf52a49a436570", - "url_id": "dcc07e30a25a", - "url": "https://mind-map.smyhub.com", - "status_code": 0, - "latency": 26, - "is_up": false, - "error": "Get \"https://mind-map.smyhub.com\": dial tcp: lookup mind-map.smyhub.com: no such host", - "checked_at": "2026-03-06T10:10:58.610474+08:00" - }, - { - "website_id": "188cb708e8cf52a49a436570", - "url_id": "dcc07e30a25a", - "url": "https://mind-map.smyhub.com", - "status_code": 0, - "latency": 19, - "is_up": false, - "error": "Get \"https://mind-map.smyhub.com\": dial tcp: lookup mind-map.smyhub.com: no such host", - "checked_at": "2026-03-06T10:15:58.1757636+08:00" - }, - { - "website_id": "188cb708e8cf52a49a436570", - "url_id": "dcc07e30a25a", - "url": "https://mind-map.smyhub.com", - "status_code": 0, - "latency": 10, - "is_up": false, - "error": "Get \"https://mind-map.smyhub.com\": dial tcp: lookup mind-map.smyhub.com: no such host", - "checked_at": "2026-03-06T10:20:58.2122057+08:00" - }, - { - "website_id": "188cb708e8cf52a49a436570", - "url_id": "dcc07e30a25a", - "url": "https://mind-map.smyhub.com", - "status_code": 0, - "latency": 6, - "is_up": false, - "error": "Get \"https://mind-map.smyhub.com\": dial tcp: lookup mind-map.smyhub.com: no such host", - "checked_at": "2026-03-06T10:25:58.2454282+08:00" - }, - { - "website_id": "188cb708e8cf52a49a436570", - "url_id": "dcc07e30a25a", - "url": "https://mind-map.smyhub.com", - "status_code": 0, - "latency": 23, - "is_up": false, - "error": "Get \"https://mind-map.smyhub.com\": dial tcp: lookup mind-map.smyhub.com: no such host", - "checked_at": "2026-03-06T10:30:58.0493386+08:00" - }, - { - "website_id": "188cb708e8cf52a49a436570", - "url_id": "dcc07e30a25a", - "url": "https://mind-map.smyhub.com", - "status_code": 0, - "latency": 49, - "is_up": false, - "error": "Get \"https://mind-map.smyhub.com\": dial tcp: lookup mind-map.smyhub.com: no such host", - "checked_at": "2026-03-06T10:35:58.1180827+08:00" - }, - { - "website_id": "188cb708e8cf52a49a436570", - "url_id": "dcc07e30a25a", - "url": "https://mind-map.smyhub.com", - "status_code": 0, - "latency": 27, - "is_up": false, - "error": "Get \"https://mind-map.smyhub.com\": dial tcp: lookup mind-map.smyhub.com: no such host", - "checked_at": "2026-03-06T10:40:58.35469+08:00" - }, - { - "website_id": "188cb708e8cf52a49a436570", - "url_id": "dcc07e30a25a", - "url": "https://mind-map.smyhub.com", - "status_code": 0, - "latency": 10, - "is_up": false, - "error": "Get \"https://mind-map.smyhub.com\": dial tcp: lookup mind-map.smyhub.com: no such host", - "checked_at": "2026-03-06T10:45:58.1770443+08:00" - }, - { - "website_id": "188cb708e8cf52a49a436570", - "url_id": "dcc07e30a25a", - "url": "https://mind-map.smyhub.com", - "status_code": 0, - "latency": 73, - "is_up": false, - "error": "Get \"https://mind-map.smyhub.com\": dial tcp: lookup mind-map.smyhub.com: no such host", - "checked_at": "2026-03-06T11:13:37.5424503+08:00" - }, - { - "website_id": "188cb708e8cf52a49a436570", - "url_id": "dcc07e30a25a", - "url": "https://mind-map.smyhub.com", - "status_code": 0, - "latency": 28, - "is_up": false, - "error": "Get \"https://mind-map.smyhub.com\": dial tcp: lookup mind-map.smyhub.com: no such host", - "checked_at": "2026-03-06T11:21:51.4120639+08:00" - }, - { - "website_id": "188cb708e8cf52a49a436570", - "url_id": "dcc07e30a25a", - "url": "https://mind-map.smyhub.com", - "status_code": 0, - "latency": 33, - "is_up": false, - "error": "Get \"https://mind-map.smyhub.com\": dial tcp: lookup mind-map.smyhub.com: no such host", - "checked_at": "2026-03-06T11:26:48.3330248+08:00" - }, - { - "website_id": "188cb708e8cf52a49a436570", - "url_id": "dcc07e30a25a", - "url": "https://mind-map.smyhub.com", - "status_code": 0, - "latency": 46, - "is_up": false, - "error": "Get \"https://mind-map.smyhub.com\": dial tcp: lookup mind-map.smyhub.com: no such host", - "checked_at": "2026-03-06T11:31:48.1156444+08:00" - }, - { - "website_id": "188cb708e8cf52a49a436570", - "url_id": "dcc07e30a25a", - "url": "https://mind-map.smyhub.com", - "status_code": 0, - "latency": 18, - "is_up": false, - "error": "Get \"https://mind-map.smyhub.com\": dial tcp: lookup mind-map.smyhub.com: no such host", - "checked_at": "2026-03-06T11:36:50.4066682+08:00" - }, - { - "website_id": "188cb708e8cf52a49a436570", - "url_id": "dcc07e30a25a", - "url": "https://mind-map.smyhub.com", - "status_code": 0, - "latency": 66, - "is_up": false, - "error": "Get \"https://mind-map.smyhub.com\": dial tcp: lookup mind-map.smyhub.com: no such host", - "checked_at": "2026-03-06T11:41:48.1104391+08:00" - }, - { - "website_id": "188cb708e8cf52a49a436570", - "url_id": "dcc07e30a25a", - "url": "https://mind-map.smyhub.com", - "status_code": 0, - "latency": 53, - "is_up": false, - "error": "Get \"https://mind-map.smyhub.com\": dial tcp: lookup mind-map.smyhub.com: no such host", - "checked_at": "2026-03-06T11:46:48.1641784+08:00" - }, - { - "website_id": "188cb708e8cf52a49a436570", - "url_id": "dcc07e30a25a", - "url": "https://mind-map.smyhub.com", - "status_code": 0, - "latency": 37, - "is_up": false, - "error": "Get \"https://mind-map.smyhub.com\": dial tcp: lookup mind-map.smyhub.com: no such host", - "checked_at": "2026-03-06T11:52:00.6622626+08:00" - }, - { - "website_id": "188cb708e8cf52a49a436570", - "url_id": "dcc07e30a25a", - "url": "https://mind-map.smyhub.com", - "status_code": 0, - "latency": 28, - "is_up": false, - "error": "Get \"https://mind-map.smyhub.com\": dial tcp: lookup mind-map.smyhub.com: no such host", - "checked_at": "2026-03-06T11:56:49.3937433+08:00" - }, - { - "website_id": "188cb708e8cf52a49a436570", - "url_id": "dcc07e30a25a", - "url": "https://mind-map.smyhub.com", - "status_code": 0, - "latency": 74, - "is_up": false, - "error": "Get \"https://mind-map.smyhub.com\": dial tcp: lookup mind-map.smyhub.com: no such host", - "checked_at": "2026-03-06T12:49:39.4233602+08:00" - }, - { - "website_id": "188cb708e8cf52a49a436570", - "url_id": "dcc07e30a25a", - "url": "https://mind-map.smyhub.com", - "status_code": 0, - "latency": 29, - "is_up": false, - "error": "Get \"https://mind-map.smyhub.com\": dial tcp: lookup mind-map.smyhub.com: no such host", - "checked_at": "2026-03-06T12:52:31.210864+08:00" - }, - { - "website_id": "188cb708e8cf52a49a436570", - "url_id": "dcc07e30a25a", - "url": "https://mind-map.smyhub.com", - "status_code": 0, - "latency": 30, - "is_up": false, - "error": "Get \"https://mind-map.smyhub.com\": dial tcp: lookup mind-map.smyhub.com: no such host", - "checked_at": "2026-03-06T12:57:11.2884299+08:00" - }, - { - "website_id": "188cb708e8cf52a49a436570", - "url_id": "dcc07e30a25a", - "url": "https://mind-map.smyhub.com", - "status_code": 0, - "latency": 29, - "is_up": false, - "error": "Get \"https://mind-map.smyhub.com\": dial tcp: lookup mind-map.smyhub.com: no such host", - "checked_at": "2026-03-06T13:02:07.9738561+08:00" - }, - { - "website_id": "188cb708e8cf52a49a436570", - "url_id": "dcc07e30a25a", - "url": "https://mind-map.smyhub.com", - "status_code": 0, - "latency": 56, - "is_up": false, - "error": "Get \"https://mind-map.smyhub.com\": dial tcp: lookup mind-map.smyhub.com: no such host", - "checked_at": "2026-03-06T13:07:08.7970095+08:00" - }, - { - "website_id": "188cb708e8cf52a49a436570", - "url_id": "dcc07e30a25a", - "url": "https://mind-map.smyhub.com", - "status_code": 0, - "latency": 39, - "is_up": false, - "error": "Get \"https://mind-map.smyhub.com\": dial tcp: lookup mind-map.smyhub.com: no such host", - "checked_at": "2026-03-06T13:12:08.2059615+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": 861, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:01:31.2022304+08:00" - }, - { - "website_id": "188cb71029ad43105a3dc6c0", - "url_id": "f278e65dd498", - "url": "https://it-tools.smyhub.com", - "status_code": 200, - "latency": 267, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:06:30.6869854+08:00" - }, - { - "website_id": "188cb71029ad43105a3dc6c0", - "url_id": "f278e65dd498", - "url": "https://it-tools.smyhub.com", - "status_code": 200, - "latency": 872, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:10:59.4840706+08:00" - }, - { - "website_id": "188cb71029ad43105a3dc6c0", - "url_id": "f278e65dd498", - "url": "https://it-tools.smyhub.com", - "status_code": 200, - "latency": 267, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:15:58.3372038+08:00" - }, - { - "website_id": "188cb71029ad43105a3dc6c0", - "url_id": "f278e65dd498", - "url": "https://it-tools.smyhub.com", - "status_code": 200, - "latency": 262, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:20:58.4226525+08:00" - }, - { - "website_id": "188cb71029ad43105a3dc6c0", - "url_id": "f278e65dd498", - "url": "https://it-tools.smyhub.com", - "status_code": 200, - "latency": 266, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:25:58.4108321+08:00" - }, - { - "website_id": "188cb71029ad43105a3dc6c0", - "url_id": "f278e65dd498", - "url": "https://it-tools.smyhub.com", - "status_code": 200, - "latency": 269, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:30:58.2834228+08:00" - }, - { - "website_id": "188cb71029ad43105a3dc6c0", - "url_id": "f278e65dd498", - "url": "https://it-tools.smyhub.com", - "status_code": 200, - "latency": 265, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:35:58.2813095+08:00" - }, - { - "website_id": "188cb71029ad43105a3dc6c0", - "url_id": "f278e65dd498", - "url": "https://it-tools.smyhub.com", - "status_code": 200, - "latency": 270, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:40:58.6038038+08:00" - }, - { - "website_id": "188cb71029ad43105a3dc6c0", - "url_id": "f278e65dd498", - "url": "https://it-tools.smyhub.com", - "status_code": 200, - "latency": 267, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:45:58.3857454+08:00" - }, - { - "website_id": "188cb71029ad43105a3dc6c0", - "url_id": "f278e65dd498", - "url": "https://it-tools.smyhub.com", - "status_code": 200, - "latency": 892, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:13:26.3106692+08:00" - }, - { - "website_id": "188cb71029ad43105a3dc6c0", - "url_id": "f278e65dd498", - "url": "https://it-tools.smyhub.com", - "status_code": 200, - "latency": 907, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:21:38.4066527+08:00" - }, - { - "website_id": "188cb71029ad43105a3dc6c0", - "url_id": "f278e65dd498", - "url": "https://it-tools.smyhub.com", - "status_code": 200, - "latency": 788, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:26:36.8676305+08:00" - }, - { - "website_id": "188cb71029ad43105a3dc6c0", - "url_id": "f278e65dd498", - "url": "https://it-tools.smyhub.com", - "status_code": 200, - "latency": 977, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:31:36.9970355+08:00" - }, - { - "website_id": "188cb71029ad43105a3dc6c0", - "url_id": "f278e65dd498", - "url": "https://it-tools.smyhub.com", - "status_code": 200, - "latency": 698, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:36:49.0510742+08:00" - }, - { - "website_id": "188cb71029ad43105a3dc6c0", - "url_id": "f278e65dd498", - "url": "https://it-tools.smyhub.com", - "status_code": 200, - "latency": 780, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:41:36.7937312+08:00" - }, - { - "website_id": "188cb71029ad43105a3dc6c0", - "url_id": "f278e65dd498", - "url": "https://it-tools.smyhub.com", - "status_code": 200, - "latency": 792, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:46:36.7998946+08:00" - }, - { - "website_id": "188cb71029ad43105a3dc6c0", - "url_id": "f278e65dd498", - "url": "https://it-tools.smyhub.com", - "status_code": 200, - "latency": 851, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:51:36.7806946+08:00" - }, - { - "website_id": "188cb71029ad43105a3dc6c0", - "url_id": "f278e65dd498", - "url": "https://it-tools.smyhub.com", - "status_code": 200, - "latency": 699, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:56:36.6279652+08:00" - }, - { - "website_id": "188cb71029ad43105a3dc6c0", - "url_id": "f278e65dd498", - "url": "https://it-tools.smyhub.com", - "status_code": 200, - "latency": 692, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:49:26.3770737+08:00" - }, - { - "website_id": "188cb71029ad43105a3dc6c0", - "url_id": "f278e65dd498", - "url": "https://it-tools.smyhub.com", - "status_code": 200, - "latency": 859, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:52:33.0717708+08:00" - }, - { - "website_id": "188cb71029ad43105a3dc6c0", - "url_id": "f278e65dd498", - "url": "https://it-tools.smyhub.com", - "status_code": 200, - "latency": 818, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:57:14.9364778+08:00" - }, - { - "website_id": "188cb71029ad43105a3dc6c0", - "url_id": "f278e65dd498", - "url": "https://it-tools.smyhub.com", - "status_code": 200, - "latency": 1994, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:02:10.9757271+08:00" - }, - { - "website_id": "188cb71029ad43105a3dc6c0", - "url_id": "f278e65dd498", - "url": "https://it-tools.smyhub.com", - "status_code": 200, - "latency": 2282, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:07:12.0801858+08:00" - }, - { - "website_id": "188cb71029ad43105a3dc6c0", - "url_id": "f278e65dd498", - "url": "https://it-tools.smyhub.com", - "status_code": 200, - "latency": 799, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:12:10.006806+08:00" - } - ], - "188cb71029ad43105a3dc6c0_f5e2bfc4a2e5": null, - "188cb71b527c48bc3c344acc_13cdc2afab17": [ - { - "website_id": "188cb71b527c48bc3c344acc", - "url_id": "13cdc2afab17", - "url": "https://xtools.smyhub.com", - "status_code": 200, - "latency": 839, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:01:31.200581+08:00" - }, - { - "website_id": "188cb71b527c48bc3c344acc", - "url_id": "13cdc2afab17", - "url": "https://xtools.smyhub.com", - "status_code": 200, - "latency": 757, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:06:30.904799+08:00" - }, - { - "website_id": "188cb71b527c48bc3c344acc", - "url_id": "13cdc2afab17", - "url": "https://xtools.smyhub.com", - "status_code": 200, - "latency": 906, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:10:59.5332534+08:00" - }, - { - "website_id": "188cb71b527c48bc3c344acc", - "url_id": "13cdc2afab17", - "url": "https://xtools.smyhub.com", - "status_code": 200, - "latency": 865, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:15:59.1294065+08:00" - }, - { - "website_id": "188cb71b527c48bc3c344acc", - "url_id": "13cdc2afab17", - "url": "https://xtools.smyhub.com", - "status_code": 200, - "latency": 773, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:20:59.0554158+08:00" - }, - { - "website_id": "188cb71b527c48bc3c344acc", - "url_id": "13cdc2afab17", - "url": "https://xtools.smyhub.com", - "status_code": 200, - "latency": 688, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:25:58.9405457+08:00" - }, - { - "website_id": "188cb71b527c48bc3c344acc", - "url_id": "13cdc2afab17", - "url": "https://xtools.smyhub.com", - "status_code": 200, - "latency": 883, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:30:59.2215312+08:00" - }, - { - "website_id": "188cb71b527c48bc3c344acc", - "url_id": "13cdc2afab17", - "url": "https://xtools.smyhub.com", - "status_code": 200, - "latency": 693, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:35:58.946968+08:00" - }, - { - "website_id": "188cb71b527c48bc3c344acc", - "url_id": "13cdc2afab17", - "url": "https://xtools.smyhub.com", - "status_code": 200, - "latency": 804, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:40:59.1315622+08:00" - }, - { - "website_id": "188cb71b527c48bc3c344acc", - "url_id": "13cdc2afab17", - "url": "https://xtools.smyhub.com", - "status_code": 200, - "latency": 782, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:45:59.0843522+08:00" - }, - { - "website_id": "188cb71b527c48bc3c344acc", - "url_id": "13cdc2afab17", - "url": "https://xtools.smyhub.com", - "status_code": 200, - "latency": 923, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:13:26.3501491+08:00" - }, - { - "website_id": "188cb71b527c48bc3c344acc", - "url_id": "13cdc2afab17", - "url": "https://xtools.smyhub.com", - "status_code": 200, - "latency": 802, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:21:49.1549298+08:00" - }, - { - "website_id": "188cb71b527c48bc3c344acc", - "url_id": "13cdc2afab17", - "url": "https://xtools.smyhub.com", - "status_code": 200, - "latency": 791, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:26:37.6592742+08:00" - }, - { - "website_id": "188cb71b527c48bc3c344acc", - "url_id": "13cdc2afab17", - "url": "https://xtools.smyhub.com", - "status_code": 200, - "latency": 875, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:31:36.9044689+08:00" - }, - { - "website_id": "188cb71b527c48bc3c344acc", - "url_id": "13cdc2afab17", - "url": "https://xtools.smyhub.com", - "status_code": 200, - "latency": 788, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:36:36.7170444+08:00" - }, - { - "website_id": "188cb71b527c48bc3c344acc", - "url_id": "13cdc2afab17", - "url": "https://xtools.smyhub.com", - "status_code": 200, - "latency": 701, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:41:36.7103277+08:00" - }, - { - "website_id": "188cb71b527c48bc3c344acc", - "url_id": "13cdc2afab17", - "url": "https://xtools.smyhub.com", - "status_code": 200, - "latency": 767, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:46:36.7701423+08:00" - }, - { - "website_id": "188cb71b527c48bc3c344acc", - "url_id": "13cdc2afab17", - "url": "https://xtools.smyhub.com", - "status_code": 200, - "latency": 883, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:51:37.7283078+08:00" - }, - { - "website_id": "188cb71b527c48bc3c344acc", - "url_id": "13cdc2afab17", - "url": "https://xtools.smyhub.com", - "status_code": 200, - "latency": 788, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:56:36.7180393+08:00" - }, - { - "website_id": "188cb71b527c48bc3c344acc", - "url_id": "13cdc2afab17", - "url": "https://xtools.smyhub.com", - "status_code": 200, - "latency": 878, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:49:35.765435+08:00" - }, - { - "website_id": "188cb71b527c48bc3c344acc", - "url_id": "13cdc2afab17", - "url": "https://xtools.smyhub.com", - "status_code": 200, - "latency": 812, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:52:34.8931519+08:00" - }, - { - "website_id": "188cb71b527c48bc3c344acc", - "url_id": "13cdc2afab17", - "url": "https://xtools.smyhub.com", - "status_code": 200, - "latency": 1032, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:57:16.9695414+08:00" - }, - { - "website_id": "188cb71b527c48bc3c344acc", - "url_id": "13cdc2afab17", - "url": "https://xtools.smyhub.com", - "status_code": 200, - "latency": 892, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:02:12.8720785+08:00" - }, - { - "website_id": "188cb71b527c48bc3c344acc", - "url_id": "13cdc2afab17", - "url": "https://xtools.smyhub.com", - "status_code": 200, - "latency": 822, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:07:15.4564067+08:00" - }, - { - "website_id": "188cb71b527c48bc3c344acc", - "url_id": "13cdc2afab17", - "url": "https://xtools.smyhub.com", - "status_code": 200, - "latency": 1134, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:12:12.4023543+08:00" - } - ], - "188cb71b527c48bc3c344acc_377c1524ef52": null, - "188cb72555e8d5c4f0d7aa98_421fe08ece5e": [ - { - "website_id": "188cb72555e8d5c4f0d7aa98", - "url_id": "421fe08ece5e", - "url": "http://monitor.shumengya.top", - "status_code": 200, - "latency": 183, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:01:30.540952+08:00" - }, - { - "website_id": "188cb72555e8d5c4f0d7aa98", - "url_id": "421fe08ece5e", - "url": "http://monitor.shumengya.top", - "status_code": 200, - "latency": 40, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:06:30.1919034+08:00" - }, - { - "website_id": "188cb72555e8d5c4f0d7aa98", - "url_id": "421fe08ece5e", - "url": "http://monitor.shumengya.top", - "status_code": 200, - "latency": 30, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:10:58.5839813+08:00" - }, - { - "website_id": "188cb72555e8d5c4f0d7aa98", - "url_id": "421fe08ece5e", - "url": "http://monitor.shumengya.top", - "status_code": 200, - "latency": 45, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:15:58.3271348+08:00" - }, - { - "website_id": "188cb72555e8d5c4f0d7aa98", - "url_id": "421fe08ece5e", - "url": "http://monitor.shumengya.top", - "status_code": 200, - "latency": 169, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:20:58.4341402+08:00" - }, - { - "website_id": "188cb72555e8d5c4f0d7aa98", - "url_id": "421fe08ece5e", - "url": "http://monitor.shumengya.top", - "status_code": 200, - "latency": 36, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:25:58.2790673+08:00" - }, - { - "website_id": "188cb72555e8d5c4f0d7aa98", - "url_id": "421fe08ece5e", - "url": "http://monitor.shumengya.top", - "status_code": 200, - "latency": 188, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:30:58.4725522+08:00" - }, - { - "website_id": "188cb72555e8d5c4f0d7aa98", - "url_id": "421fe08ece5e", - "url": "http://monitor.shumengya.top", - "status_code": 200, - "latency": 40, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:35:58.2471157+08:00" - }, - { - "website_id": "188cb72555e8d5c4f0d7aa98", - "url_id": "421fe08ece5e", - "url": "http://monitor.shumengya.top", - "status_code": 200, - "latency": 186, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:40:58.5382443+08:00" - }, - { - "website_id": "188cb72555e8d5c4f0d7aa98", - "url_id": "421fe08ece5e", - "url": "http://monitor.shumengya.top", - "status_code": 200, - "latency": 43, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:45:58.2983982+08:00" - }, - { - "website_id": "188cb72555e8d5c4f0d7aa98", - "url_id": "421fe08ece5e", - "url": "http://monitor.shumengya.top", - "status_code": 200, - "latency": 179, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:13:25.6083855+08:00" - }, - { - "website_id": "188cb72555e8d5c4f0d7aa98", - "url_id": "421fe08ece5e", - "url": "http://monitor.shumengya.top", - "status_code": 200, - "latency": 56, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:21:48.4571738+08:00" - }, - { - "website_id": "188cb72555e8d5c4f0d7aa98", - "url_id": "421fe08ece5e", - "url": "http://monitor.shumengya.top", - "status_code": 200, - "latency": 44, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:26:37.0324895+08:00" - }, - { - "website_id": "188cb72555e8d5c4f0d7aa98", - "url_id": "421fe08ece5e", - "url": "http://monitor.shumengya.top", - "status_code": 200, - "latency": 50, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:31:36.0853928+08:00" - }, - { - "website_id": "188cb72555e8d5c4f0d7aa98", - "url_id": "421fe08ece5e", - "url": "http://monitor.shumengya.top", - "status_code": 200, - "latency": 50, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:36:36.2473503+08:00" - }, - { - "website_id": "188cb72555e8d5c4f0d7aa98", - "url_id": "421fe08ece5e", - "url": "http://monitor.shumengya.top", - "status_code": 200, - "latency": 50, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:41:36.0616968+08:00" - }, - { - "website_id": "188cb72555e8d5c4f0d7aa98", - "url_id": "421fe08ece5e", - "url": "http://monitor.shumengya.top", - "status_code": 200, - "latency": 61, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:46:36.0721291+08:00" - }, - { - "website_id": "188cb72555e8d5c4f0d7aa98", - "url_id": "421fe08ece5e", - "url": "http://monitor.shumengya.top", - "status_code": 200, - "latency": 55, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:51:36.836845+08:00" - }, - { - "website_id": "188cb72555e8d5c4f0d7aa98", - "url_id": "421fe08ece5e", - "url": "http://monitor.shumengya.top", - "status_code": 200, - "latency": 50, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:56:36.0495987+08:00" - }, - { - "website_id": "188cb72555e8d5c4f0d7aa98", - "url_id": "421fe08ece5e", - "url": "http://monitor.shumengya.top", - "status_code": 200, - "latency": 49, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:49:34.9134661+08:00" - }, - { - "website_id": "188cb72555e8d5c4f0d7aa98", - "url_id": "421fe08ece5e", - "url": "http://monitor.shumengya.top", - "status_code": 200, - "latency": 64, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:52:35.9581338+08:00" - }, - { - "website_id": "188cb72555e8d5c4f0d7aa98", - "url_id": "421fe08ece5e", - "url": "http://monitor.shumengya.top", - "status_code": 200, - "latency": 42, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:57:18.0193997+08:00" - }, - { - "website_id": "188cb72555e8d5c4f0d7aa98", - "url_id": "421fe08ece5e", - "url": "http://monitor.shumengya.top", - "status_code": 200, - "latency": 60, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:02:13.9352761+08:00" - }, - { - "website_id": "188cb72555e8d5c4f0d7aa98", - "url_id": "421fe08ece5e", - "url": "http://monitor.shumengya.top", - "status_code": 200, - "latency": 44, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:07:16.5082229+08:00" - }, - { - "website_id": "188cb72555e8d5c4f0d7aa98", - "url_id": "421fe08ece5e", - "url": "http://monitor.shumengya.top", - "status_code": 200, - "latency": 57, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:12:13.4609472+08:00" - } - ], - "188cb73272be1c9c5a5d6812_cba98c3c329b": [ - { - "website_id": "188cb73272be1c9c5a5d6812", - "url_id": "cba98c3c329b", - "url": "https://office.shumengya.top", - "status_code": 404, - "latency": 199, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:01:30.5580101+08:00" - }, - { - "website_id": "188cb73272be1c9c5a5d6812", - "url_id": "cba98c3c329b", - "url": "https://office.shumengya.top", - "status_code": 404, - "latency": 50, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:06:30.2428329+08:00" - }, - { - "website_id": "188cb73272be1c9c5a5d6812", - "url_id": "cba98c3c329b", - "url": "https://office.shumengya.top", - "status_code": 404, - "latency": 73, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:10:58.8361017+08:00" - }, - { - "website_id": "188cb73272be1c9c5a5d6812", - "url_id": "cba98c3c329b", - "url": "https://office.shumengya.top", - "status_code": 404, - "latency": 62, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:15:58.3524185+08:00" - }, - { - "website_id": "188cb73272be1c9c5a5d6812", - "url_id": "cba98c3c329b", - "url": "https://office.shumengya.top", - "status_code": 404, - "latency": 59, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:20:58.4133314+08:00" - }, - { - "website_id": "188cb73272be1c9c5a5d6812", - "url_id": "cba98c3c329b", - "url": "https://office.shumengya.top", - "status_code": 404, - "latency": 58, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:25:58.3383679+08:00" - }, - { - "website_id": "188cb73272be1c9c5a5d6812", - "url_id": "cba98c3c329b", - "url": "https://office.shumengya.top", - "status_code": 404, - "latency": 64, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:30:58.4175497+08:00" - }, - { - "website_id": "188cb73272be1c9c5a5d6812", - "url_id": "cba98c3c329b", - "url": "https://office.shumengya.top", - "status_code": 404, - "latency": 67, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:35:58.3151915+08:00" - }, - { - "website_id": "188cb73272be1c9c5a5d6812", - "url_id": "cba98c3c329b", - "url": "https://office.shumengya.top", - "status_code": 404, - "latency": 113, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:40:58.4834691+08:00" - }, - { - "website_id": "188cb73272be1c9c5a5d6812", - "url_id": "cba98c3c329b", - "url": "https://office.shumengya.top", - "status_code": 404, - "latency": 67, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:45:58.3656722+08:00" - }, - { - "website_id": "188cb73272be1c9c5a5d6812", - "url_id": "cba98c3c329b", - "url": "https://office.shumengya.top", - "status_code": 404, - "latency": 53, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T11:13:37.7593983+08:00" - }, - { - "website_id": "188cb73272be1c9c5a5d6812", - "url_id": "cba98c3c329b", - "url": "https://office.shumengya.top", - "status_code": 404, - "latency": 77, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T11:22:00.5398513+08:00" - }, - { - "website_id": "188cb73272be1c9c5a5d6812", - "url_id": "cba98c3c329b", - "url": "https://office.shumengya.top", - "status_code": 404, - "latency": 61, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T11:26:49.2783978+08:00" - }, - { - "website_id": "188cb73272be1c9c5a5d6812", - "url_id": "cba98c3c329b", - "url": "https://office.shumengya.top", - "status_code": 404, - "latency": 65, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T11:31:48.2806053+08:00" - }, - { - "website_id": "188cb73272be1c9c5a5d6812", - "url_id": "cba98c3c329b", - "url": "https://office.shumengya.top", - "status_code": 404, - "latency": 51, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T11:36:48.1672136+08:00" - }, - { - "website_id": "188cb73272be1c9c5a5d6812", - "url_id": "cba98c3c329b", - "url": "https://office.shumengya.top", - "status_code": 404, - "latency": 61, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T11:41:48.3337965+08:00" - }, - { - "website_id": "188cb73272be1c9c5a5d6812", - "url_id": "cba98c3c329b", - "url": "https://office.shumengya.top", - "status_code": 404, - "latency": 71, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T11:46:48.3278351+08:00" - }, - { - "website_id": "188cb73272be1c9c5a5d6812", - "url_id": "cba98c3c329b", - "url": "https://office.shumengya.top", - "status_code": 404, - "latency": 58, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T11:52:00.6952371+08:00" - }, - { - "website_id": "188cb73272be1c9c5a5d6812", - "url_id": "cba98c3c329b", - "url": "https://office.shumengya.top", - "status_code": 404, - "latency": 55, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T11:56:48.2161897+08:00" - }, - { - "website_id": "188cb73272be1c9c5a5d6812", - "url_id": "cba98c3c329b", - "url": "https://office.shumengya.top", - "status_code": 404, - "latency": 78, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T12:49:47.1756539+08:00" - }, - { - "website_id": "188cb73272be1c9c5a5d6812", - "url_id": "cba98c3c329b", - "url": "https://office.shumengya.top", - "status_code": 404, - "latency": 84, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T12:52:49.2920098+08:00" - }, - { - "website_id": "188cb73272be1c9c5a5d6812", - "url_id": "cba98c3c329b", - "url": "https://office.shumengya.top", - "status_code": 404, - "latency": 69, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T12:57:31.2968053+08:00" - }, - { - "website_id": "188cb73272be1c9c5a5d6812", - "url_id": "cba98c3c329b", - "url": "https://office.shumengya.top", - "status_code": 404, - "latency": 84, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T13:02:27.2298866+08:00" - }, - { - "website_id": "188cb73272be1c9c5a5d6812", - "url_id": "cba98c3c329b", - "url": "https://office.shumengya.top", - "status_code": 404, - "latency": 76, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T13:07:29.7921189+08:00" - }, - { - "website_id": "188cb73272be1c9c5a5d6812", - "url_id": "cba98c3c329b", - "url": "https://office.shumengya.top", - "status_code": 404, - "latency": 80, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T13:12:26.7457987+08:00" - } - ], - "188cb7372655f3841e82a210_299e19afc16d": null, - "188cb7372655f3841e82a210_ed4c7dcd0565": [ - { - "website_id": "188cb7372655f3841e82a210", - "url_id": "ed4c7dcd0565", - "url": "https://cube.smyhub.com", - "status_code": 0, - "latency": 108, - "is_up": false, - "error": "Get \"https://cube.smyhub.com\": dial tcp: lookup cube.smyhub.com: no such host", - "checked_at": "2026-03-06T10:01:30.4956272+08:00" - }, - { - "website_id": "188cb7372655f3841e82a210", - "url_id": "ed4c7dcd0565", - "url": "https://cube.smyhub.com", - "status_code": 0, - "latency": 14, - "is_up": false, - "error": "Get \"https://cube.smyhub.com\": dial tcp: lookup cube.smyhub.com: no such host", - "checked_at": "2026-03-06T10:06:30.206507+08:00" - }, - { - "website_id": "188cb7372655f3841e82a210", - "url_id": "ed4c7dcd0565", - "url": "https://cube.smyhub.com", - "status_code": 0, - "latency": 12, - "is_up": false, - "error": "Get \"https://cube.smyhub.com\": dial tcp: lookup cube.smyhub.com: no such host", - "checked_at": "2026-03-06T10:10:58.7604802+08:00" - }, - { - "website_id": "188cb7372655f3841e82a210", - "url_id": "ed4c7dcd0565", - "url": "https://cube.smyhub.com", - "status_code": 0, - "latency": 32, - "is_up": false, - "error": "Get \"https://cube.smyhub.com\": dial tcp: lookup cube.smyhub.com: no such host", - "checked_at": "2026-03-06T10:15:58.2745643+08:00" - }, - { - "website_id": "188cb7372655f3841e82a210", - "url_id": "ed4c7dcd0565", - "url": "https://cube.smyhub.com", - "status_code": 0, - "latency": 11, - "is_up": false, - "error": "Get \"https://cube.smyhub.com\": dial tcp: lookup cube.smyhub.com: no such host", - "checked_at": "2026-03-06T10:20:58.3796926+08:00" - }, - { - "website_id": "188cb7372655f3841e82a210", - "url_id": "ed4c7dcd0565", - "url": "https://cube.smyhub.com", - "status_code": 0, - "latency": 9, - "is_up": false, - "error": "Get \"https://cube.smyhub.com\": dial tcp: lookup cube.smyhub.com: no such host", - "checked_at": "2026-03-06T10:25:58.3378201+08:00" - }, - { - "website_id": "188cb7372655f3841e82a210", - "url_id": "ed4c7dcd0565", - "url": "https://cube.smyhub.com", - "status_code": 0, - "latency": 8, - "is_up": false, - "error": "Get \"https://cube.smyhub.com\": dial tcp: lookup cube.smyhub.com: no such host", - "checked_at": "2026-03-06T10:30:58.352429+08:00" - }, - { - "website_id": "188cb7372655f3841e82a210", - "url_id": "ed4c7dcd0565", - "url": "https://cube.smyhub.com", - "status_code": 0, - "latency": 140, - "is_up": false, - "error": "Get \"https://cube.smyhub.com\": dial tcp: lookup cube.smyhub.com: no such host", - "checked_at": "2026-03-06T10:35:58.4053196+08:00" - }, - { - "website_id": "188cb7372655f3841e82a210", - "url_id": "ed4c7dcd0565", - "url": "https://cube.smyhub.com", - "status_code": 0, - "latency": 18, - "is_up": false, - "error": "Get \"https://cube.smyhub.com\": dial tcp: lookup cube.smyhub.com: no such host", - "checked_at": "2026-03-06T10:40:57.9705039+08:00" - }, - { - "website_id": "188cb7372655f3841e82a210", - "url_id": "ed4c7dcd0565", - "url": "https://cube.smyhub.com", - "status_code": 0, - "latency": 15, - "is_up": false, - "error": "Get \"https://cube.smyhub.com\": dial tcp: lookup cube.smyhub.com: no such host", - "checked_at": "2026-03-06T10:45:58.3338156+08:00" - }, - { - "website_id": "188cb7372655f3841e82a210", - "url_id": "ed4c7dcd0565", - "url": "https://cube.smyhub.com", - "status_code": 0, - "latency": 26, - "is_up": false, - "error": "Get \"https://cube.smyhub.com\": dial tcp: lookup cube.smyhub.com: no such host", - "checked_at": "2026-03-06T11:13:37.7006283+08:00" - }, - { - "website_id": "188cb7372655f3841e82a210", - "url_id": "ed4c7dcd0565", - "url": "https://cube.smyhub.com", - "status_code": 0, - "latency": 252, - "is_up": false, - "error": "Get \"https://cube.smyhub.com\": dial tcp: lookup cube.smyhub.com: no such host", - "checked_at": "2026-03-06T11:22:00.7475304+08:00" - }, - { - "website_id": "188cb7372655f3841e82a210", - "url_id": "ed4c7dcd0565", - "url": "https://cube.smyhub.com", - "status_code": 0, - "latency": 49, - "is_up": false, - "error": "Get \"https://cube.smyhub.com\": dial tcp: lookup cube.smyhub.com: no such host", - "checked_at": "2026-03-06T11:26:48.5370842+08:00" - }, - { - "website_id": "188cb7372655f3841e82a210", - "url_id": "ed4c7dcd0565", - "url": "https://cube.smyhub.com", - "status_code": 0, - "latency": 39, - "is_up": false, - "error": "Get \"https://cube.smyhub.com\": dial tcp: lookup cube.smyhub.com: no such host", - "checked_at": "2026-03-06T11:31:49.1346109+08:00" - }, - { - "website_id": "188cb7372655f3841e82a210", - "url_id": "ed4c7dcd0565", - "url": "https://cube.smyhub.com", - "status_code": 0, - "latency": 268, - "is_up": false, - "error": "Get \"https://cube.smyhub.com\": dial tcp: lookup cube.smyhub.com: no such host", - "checked_at": "2026-03-06T11:36:48.3520905+08:00" - }, - { - "website_id": "188cb7372655f3841e82a210", - "url_id": "ed4c7dcd0565", - "url": "https://cube.smyhub.com", - "status_code": 0, - "latency": 34, - "is_up": false, - "error": "Get \"https://cube.smyhub.com\": dial tcp: lookup cube.smyhub.com: no such host", - "checked_at": "2026-03-06T11:41:48.3307222+08:00" - }, - { - "website_id": "188cb7372655f3841e82a210", - "url_id": "ed4c7dcd0565", - "url": "https://cube.smyhub.com", - "status_code": 0, - "latency": 41, - "is_up": false, - "error": "Get \"https://cube.smyhub.com\": dial tcp: lookup cube.smyhub.com: no such host", - "checked_at": "2026-03-06T11:46:48.1641784+08:00" - }, - { - "website_id": "188cb7372655f3841e82a210", - "url_id": "ed4c7dcd0565", - "url": "https://cube.smyhub.com", - "status_code": 0, - "latency": 414, - "is_up": false, - "error": "Get \"https://cube.smyhub.com\": dial tcp: lookup cube.smyhub.com: no such host", - "checked_at": "2026-03-06T11:51:48.4432568+08:00" - }, - { - "website_id": "188cb7372655f3841e82a210", - "url_id": "ed4c7dcd0565", - "url": "https://cube.smyhub.com", - "status_code": 0, - "latency": 23, - "is_up": false, - "error": "Get \"https://cube.smyhub.com\": dial tcp: lookup cube.smyhub.com: no such host", - "checked_at": "2026-03-06T11:56:48.146813+08:00" - }, - { - "website_id": "188cb7372655f3841e82a210", - "url_id": "ed4c7dcd0565", - "url": "https://cube.smyhub.com", - "status_code": 0, - "latency": 58, - "is_up": false, - "error": "Get \"https://cube.smyhub.com\": dial tcp: lookup cube.smyhub.com: no such host", - "checked_at": "2026-03-06T12:49:47.1991401+08:00" - }, - { - "website_id": "188cb7372655f3841e82a210", - "url_id": "ed4c7dcd0565", - "url": "https://cube.smyhub.com", - "status_code": 0, - "latency": 45, - "is_up": false, - "error": "Get \"https://cube.smyhub.com\": dial tcp: lookup cube.smyhub.com: no such host", - "checked_at": "2026-03-06T12:53:02.4411535+08:00" - }, - { - "website_id": "188cb7372655f3841e82a210", - "url_id": "ed4c7dcd0565", - "url": "https://cube.smyhub.com", - "status_code": 0, - "latency": 39, - "is_up": false, - "error": "Get \"https://cube.smyhub.com\": dial tcp: lookup cube.smyhub.com: no such host", - "checked_at": "2026-03-06T12:57:44.4640751+08:00" - }, - { - "website_id": "188cb7372655f3841e82a210", - "url_id": "ed4c7dcd0565", - "url": "https://cube.smyhub.com", - "status_code": 0, - "latency": 31, - "is_up": false, - "error": "Get \"https://cube.smyhub.com\": dial tcp: lookup cube.smyhub.com: no such host", - "checked_at": "2026-03-06T13:02:40.5461907+08:00" - }, - { - "website_id": "188cb7372655f3841e82a210", - "url_id": "ed4c7dcd0565", - "url": "https://cube.smyhub.com", - "status_code": 0, - "latency": 43, - "is_up": false, - "error": "Get \"https://cube.smyhub.com\": dial tcp: lookup cube.smyhub.com: no such host", - "checked_at": "2026-03-06T13:07:43.2325422+08:00" - }, - { - "website_id": "188cb7372655f3841e82a210", - "url_id": "ed4c7dcd0565", - "url": "https://cube.smyhub.com", - "status_code": 0, - "latency": 199, - "is_up": false, - "error": "Get \"https://cube.smyhub.com\": dial tcp: lookup cube.smyhub.com: no such host", - "checked_at": "2026-03-06T13:12:40.095624+08:00" - } - ], - "188cb764a98dbb2453c14b8f_2f0596587997": [ - { - "website_id": "188cb764a98dbb2453c14b8f", - "url_id": "2f0596587997", - "url": "https://60s.api.shumengya.top", - "status_code": 200, - "latency": 181, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:01:30.3197729+08:00" - }, - { - "website_id": "188cb764a98dbb2453c14b8f", - "url_id": "2f0596587997", - "url": "https://60s.api.shumengya.top", - "status_code": 200, - "latency": 63, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:06:30.2562873+08:00" - }, - { - "website_id": "188cb764a98dbb2453c14b8f", - "url_id": "2f0596587997", - "url": "https://60s.api.shumengya.top", - "status_code": 200, - "latency": 178, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:10:59.2296056+08:00" - }, - { - "website_id": "188cb764a98dbb2453c14b8f", - "url_id": "2f0596587997", - "url": "https://60s.api.shumengya.top", - "status_code": 200, - "latency": 62, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:15:58.4494586+08:00" - }, - { - "website_id": "188cb764a98dbb2453c14b8f", - "url_id": "2f0596587997", - "url": "https://60s.api.shumengya.top", - "status_code": 200, - "latency": 62, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:20:58.0127576+08:00" - }, - { - "website_id": "188cb764a98dbb2453c14b8f", - "url_id": "2f0596587997", - "url": "https://60s.api.shumengya.top", - "status_code": 200, - "latency": 69, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:25:58.0219684+08:00" - }, - { - "website_id": "188cb764a98dbb2453c14b8f", - "url_id": "2f0596587997", - "url": "https://60s.api.shumengya.top", - "status_code": 200, - "latency": 62, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:30:58.0118098+08:00" - }, - { - "website_id": "188cb764a98dbb2453c14b8f", - "url_id": "2f0596587997", - "url": "https://60s.api.shumengya.top", - "status_code": 200, - "latency": 67, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:35:58.0176916+08:00" - }, - { - "website_id": "188cb764a98dbb2453c14b8f", - "url_id": "2f0596587997", - "url": "https://60s.api.shumengya.top", - "status_code": 200, - "latency": 67, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:40:58.3176506+08:00" - }, - { - "website_id": "188cb764a98dbb2453c14b8f", - "url_id": "2f0596587997", - "url": "https://60s.api.shumengya.top", - "status_code": 200, - "latency": 91, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:45:58.0406602+08:00" - }, - { - "website_id": "188cb764a98dbb2453c14b8f", - "url_id": "2f0596587997", - "url": "https://60s.api.shumengya.top", - "status_code": 200, - "latency": 82, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:13:25.3409878+08:00" - }, - { - "website_id": "188cb764a98dbb2453c14b8f", - "url_id": "2f0596587997", - "url": "https://60s.api.shumengya.top", - "status_code": 200, - "latency": 188, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:21:36.1150949+08:00" - }, - { - "website_id": "188cb764a98dbb2453c14b8f", - "url_id": "2f0596587997", - "url": "https://60s.api.shumengya.top", - "status_code": 200, - "latency": 56, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:26:48.383325+08:00" - }, - { - "website_id": "188cb764a98dbb2453c14b8f", - "url_id": "2f0596587997", - "url": "https://60s.api.shumengya.top", - "status_code": 200, - "latency": 60, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:31:36.9651118+08:00" - }, - { - "website_id": "188cb764a98dbb2453c14b8f", - "url_id": "2f0596587997", - "url": "https://60s.api.shumengya.top", - "status_code": 200, - "latency": 64, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:36:36.1962412+08:00" - }, - { - "website_id": "188cb764a98dbb2453c14b8f", - "url_id": "2f0596587997", - "url": "https://60s.api.shumengya.top", - "status_code": 200, - "latency": 61, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:41:35.9983026+08:00" - }, - { - "website_id": "188cb764a98dbb2453c14b8f", - "url_id": "2f0596587997", - "url": "https://60s.api.shumengya.top", - "status_code": 200, - "latency": 219, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:46:36.1463128+08:00" - }, - { - "website_id": "188cb764a98dbb2453c14b8f", - "url_id": "2f0596587997", - "url": "https://60s.api.shumengya.top", - "status_code": 200, - "latency": 70, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:51:37.120725+08:00" - }, - { - "website_id": "188cb764a98dbb2453c14b8f", - "url_id": "2f0596587997", - "url": "https://60s.api.shumengya.top", - "status_code": 200, - "latency": 73, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:56:36.0007121+08:00" - }, - { - "website_id": "188cb764a98dbb2453c14b8f", - "url_id": "2f0596587997", - "url": "https://60s.api.shumengya.top", - "status_code": 200, - "latency": 768, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:49:22.5223405+08:00" - }, - { - "website_id": "188cb764a98dbb2453c14b8f", - "url_id": "2f0596587997", - "url": "https://60s.api.shumengya.top", - "status_code": 200, - "latency": 84, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:53:03.5304788+08:00" - }, - { - "website_id": "188cb764a98dbb2453c14b8f", - "url_id": "2f0596587997", - "url": "https://60s.api.shumengya.top", - "status_code": 200, - "latency": 61, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:57:45.5360117+08:00" - }, - { - "website_id": "188cb764a98dbb2453c14b8f", - "url_id": "2f0596587997", - "url": "https://60s.api.shumengya.top", - "status_code": 200, - "latency": 82, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:02:41.6345678+08:00" - }, - { - "website_id": "188cb764a98dbb2453c14b8f", - "url_id": "2f0596587997", - "url": "https://60s.api.shumengya.top", - "status_code": 200, - "latency": 65, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:07:44.2985458+08:00" - }, - { - "website_id": "188cb764a98dbb2453c14b8f", - "url_id": "2f0596587997", - "url": "https://60s.api.shumengya.top", - "status_code": 200, - "latency": 73, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:12:41.1694231+08:00" - } - ], - "188cb77f231fe68c73e24b29_c182709178d9": [ - { - "website_id": "188cb77f231fe68c73e24b29", - "url_id": "c182709178d9", - "url": "https://nav.api.shumengya.top", - "status_code": 200, - "latency": 64, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:01:30.5487661+08:00" - }, - { - "website_id": "188cb77f231fe68c73e24b29", - "url_id": "c182709178d9", - "url": "https://nav.api.shumengya.top", - "status_code": 200, - "latency": 66, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:06:30.2689303+08:00" - }, - { - "website_id": "188cb77f231fe68c73e24b29", - "url_id": "c182709178d9", - "url": "https://nav.api.shumengya.top", - "status_code": 200, - "latency": 86, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:10:58.0360248+08:00" - }, - { - "website_id": "188cb77f231fe68c73e24b29", - "url_id": "c182709178d9", - "url": "https://nav.api.shumengya.top", - "status_code": 200, - "latency": 66, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:15:58.4193768+08:00" - }, - { - "website_id": "188cb77f231fe68c73e24b29", - "url_id": "c182709178d9", - "url": "https://nav.api.shumengya.top", - "status_code": 200, - "latency": 199, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:20:58.150532+08:00" - }, - { - "website_id": "188cb77f231fe68c73e24b29", - "url_id": "c182709178d9", - "url": "https://nav.api.shumengya.top", - "status_code": 200, - "latency": 68, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:25:58.0783968+08:00" - }, - { - "website_id": "188cb77f231fe68c73e24b29", - "url_id": "c182709178d9", - "url": "https://nav.api.shumengya.top", - "status_code": 200, - "latency": 122, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:30:58.0738107+08:00" - }, - { - "website_id": "188cb77f231fe68c73e24b29", - "url_id": "c182709178d9", - "url": "https://nav.api.shumengya.top", - "status_code": 200, - "latency": 70, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:35:58.021041+08:00" - }, - { - "website_id": "188cb77f231fe68c73e24b29", - "url_id": "c182709178d9", - "url": "https://nav.api.shumengya.top", - "status_code": 200, - "latency": 198, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:40:58.4363569+08:00" - }, - { - "website_id": "188cb77f231fe68c73e24b29", - "url_id": "c182709178d9", - "url": "https://nav.api.shumengya.top", - "status_code": 200, - "latency": 100, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:45:58.0512019+08:00" - }, - { - "website_id": "188cb77f231fe68c73e24b29", - "url_id": "c182709178d9", - "url": "https://nav.api.shumengya.top", - "status_code": 200, - "latency": 216, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:13:26.5667596+08:00" - }, - { - "website_id": "188cb77f231fe68c73e24b29", - "url_id": "c182709178d9", - "url": "https://nav.api.shumengya.top", - "status_code": 200, - "latency": 85, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:21:36.0129781+08:00" - }, - { - "website_id": "188cb77f231fe68c73e24b29", - "url_id": "c182709178d9", - "url": "https://nav.api.shumengya.top", - "status_code": 200, - "latency": 69, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:26:48.3954791+08:00" - }, - { - "website_id": "188cb77f231fe68c73e24b29", - "url_id": "c182709178d9", - "url": "https://nav.api.shumengya.top", - "status_code": 200, - "latency": 67, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:31:37.2005214+08:00" - }, - { - "website_id": "188cb77f231fe68c73e24b29", - "url_id": "c182709178d9", - "url": "https://nav.api.shumengya.top", - "status_code": 200, - "latency": 81, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:36:36.0103328+08:00" - }, - { - "website_id": "188cb77f231fe68c73e24b29", - "url_id": "c182709178d9", - "url": "https://nav.api.shumengya.top", - "status_code": 200, - "latency": 75, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:41:36.2563376+08:00" - }, - { - "website_id": "188cb77f231fe68c73e24b29", - "url_id": "c182709178d9", - "url": "https://nav.api.shumengya.top", - "status_code": 200, - "latency": 74, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:46:36.0011437+08:00" - }, - { - "website_id": "188cb77f231fe68c73e24b29", - "url_id": "c182709178d9", - "url": "https://nav.api.shumengya.top", - "status_code": 200, - "latency": 90, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:51:37.0497252+08:00" - }, - { - "website_id": "188cb77f231fe68c73e24b29", - "url_id": "c182709178d9", - "url": "https://nav.api.shumengya.top", - "status_code": 200, - "latency": 70, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:56:35.9988544+08:00" - }, - { - "website_id": "188cb77f231fe68c73e24b29", - "url_id": "c182709178d9", - "url": "https://nav.api.shumengya.top", - "status_code": 200, - "latency": 923, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:49:22.8225435+08:00" - }, - { - "website_id": "188cb77f231fe68c73e24b29", - "url_id": "c182709178d9", - "url": "https://nav.api.shumengya.top", - "status_code": 200, - "latency": 77, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:53:04.6132278+08:00" - }, - { - "website_id": "188cb77f231fe68c73e24b29", - "url_id": "c182709178d9", - "url": "https://nav.api.shumengya.top", - "status_code": 200, - "latency": 68, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:57:46.6061253+08:00" - }, - { - "website_id": "188cb77f231fe68c73e24b29", - "url_id": "c182709178d9", - "url": "https://nav.api.shumengya.top", - "status_code": 200, - "latency": 70, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:02:42.7051969+08:00" - }, - { - "website_id": "188cb77f231fe68c73e24b29", - "url_id": "c182709178d9", - "url": "https://nav.api.shumengya.top", - "status_code": 200, - "latency": 73, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:07:45.3727266+08:00" - }, - { - "website_id": "188cb77f231fe68c73e24b29", - "url_id": "c182709178d9", - "url": "https://nav.api.shumengya.top", - "status_code": 200, - "latency": 103, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:12:42.2737616+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": 230, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:01:30.7279386+08:00" - }, - { - "website_id": "188cb789a76896788f139a96", - "url_id": "2b42ed612dd3", - "url": "https://note.api.shumengya.top/api/tree", - "status_code": 200, - "latency": 93, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:06:30.2964764+08:00" - }, - { - "website_id": "188cb789a76896788f139a96", - "url_id": "2b42ed612dd3", - "url": "https://note.api.shumengya.top/api/tree", - "status_code": 200, - "latency": 242, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:10:58.1919073+08:00" - }, - { - "website_id": "188cb789a76896788f139a96", - "url_id": "2b42ed612dd3", - "url": "https://note.api.shumengya.top/api/tree", - "status_code": 200, - "latency": 113, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:15:58.5332649+08:00" - }, - { - "website_id": "188cb789a76896788f139a96", - "url_id": "2b42ed612dd3", - "url": "https://note.api.shumengya.top/api/tree", - "status_code": 200, - "latency": 107, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:20:58.0628846+08:00" - }, - { - "website_id": "188cb789a76896788f139a96", - "url_id": "2b42ed612dd3", - "url": "https://note.api.shumengya.top/api/tree", - "status_code": 200, - "latency": 111, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:25:58.0643811+08:00" - }, - { - "website_id": "188cb789a76896788f139a96", - "url_id": "2b42ed612dd3", - "url": "https://note.api.shumengya.top/api/tree", - "status_code": 200, - "latency": 111, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:30:58.2288562+08:00" - }, - { - "website_id": "188cb789a76896788f139a96", - "url_id": "2b42ed612dd3", - "url": "https://note.api.shumengya.top/api/tree", - "status_code": 200, - "latency": 116, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:35:58.2644698+08:00" - }, - { - "website_id": "188cb789a76896788f139a96", - "url_id": "2b42ed612dd3", - "url": "https://note.api.shumengya.top/api/tree", - "status_code": 200, - "latency": 116, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:40:58.4717617+08:00" - }, - { - "website_id": "188cb789a76896788f139a96", - "url_id": "2b42ed612dd3", - "url": "https://note.api.shumengya.top/api/tree", - "status_code": 200, - "latency": 110, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:45:58.1504604+08:00" - }, - { - "website_id": "188cb789a76896788f139a96", - "url_id": "2b42ed612dd3", - "url": "https://note.api.shumengya.top/api/tree", - "status_code": 200, - "latency": 172, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:13:26.5539105+08:00" - }, - { - "website_id": "188cb789a76896788f139a96", - "url_id": "2b42ed612dd3", - "url": "https://note.api.shumengya.top/api/tree", - "status_code": 200, - "latency": 131, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:21:36.0590636+08:00" - }, - { - "website_id": "188cb789a76896788f139a96", - "url_id": "2b42ed612dd3", - "url": "https://note.api.shumengya.top/api/tree", - "status_code": 200, - "latency": 126, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:26:48.5144311+08:00" - }, - { - "website_id": "188cb789a76896788f139a96", - "url_id": "2b42ed612dd3", - "url": "https://note.api.shumengya.top/api/tree", - "status_code": 200, - "latency": 129, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:31:37.4557189+08:00" - }, - { - "website_id": "188cb789a76896788f139a96", - "url_id": "2b42ed612dd3", - "url": "https://note.api.shumengya.top/api/tree", - "status_code": 200, - "latency": 124, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:36:36.0534254+08:00" - }, - { - "website_id": "188cb789a76896788f139a96", - "url_id": "2b42ed612dd3", - "url": "https://note.api.shumengya.top/api/tree", - "status_code": 200, - "latency": 118, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:41:36.1807883+08:00" - }, - { - "website_id": "188cb789a76896788f139a96", - "url_id": "2b42ed612dd3", - "url": "https://note.api.shumengya.top/api/tree", - "status_code": 200, - "latency": 126, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:46:37.7759208+08:00" - }, - { - "website_id": "188cb789a76896788f139a96", - "url_id": "2b42ed612dd3", - "url": "https://note.api.shumengya.top/api/tree", - "status_code": 200, - "latency": 118, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:51:37.2404091+08:00" - }, - { - "website_id": "188cb789a76896788f139a96", - "url_id": "2b42ed612dd3", - "url": "https://note.api.shumengya.top/api/tree", - "status_code": 200, - "latency": 119, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:56:36.1182048+08:00" - }, - { - "website_id": "188cb789a76896788f139a96", - "url_id": "2b42ed612dd3", - "url": "https://note.api.shumengya.top/api/tree", - "status_code": 200, - "latency": 115, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:49:25.5321526+08:00" - }, - { - "website_id": "188cb789a76896788f139a96", - "url_id": "2b42ed612dd3", - "url": "https://note.api.shumengya.top/api/tree", - "status_code": 200, - "latency": 120, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:53:05.7409363+08:00" - }, - { - "website_id": "188cb789a76896788f139a96", - "url_id": "2b42ed612dd3", - "url": "https://note.api.shumengya.top/api/tree", - "status_code": 200, - "latency": 112, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:57:47.7195649+08:00" - }, - { - "website_id": "188cb789a76896788f139a96", - "url_id": "2b42ed612dd3", - "url": "https://note.api.shumengya.top/api/tree", - "status_code": 200, - "latency": 121, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:02:43.8342375+08:00" - }, - { - "website_id": "188cb789a76896788f139a96", - "url_id": "2b42ed612dd3", - "url": "https://note.api.shumengya.top/api/tree", - "status_code": 200, - "latency": 129, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:07:46.5027347+08:00" - }, - { - "website_id": "188cb789a76896788f139a96", - "url_id": "2b42ed612dd3", - "url": "https://note.api.shumengya.top/api/tree", - "status_code": 200, - "latency": 109, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:12:43.3849332+08:00" - } - ], - "188cb7934d5d413890dea61f_bebd3848b644": null, - "188cb7934d5d413890dea61f_d841646c7b41": [ - { - "website_id": "188cb7934d5d413890dea61f", - "url_id": "d841646c7b41", - "url": "https://work.api.shumengya.top", - "status_code": 200, - "latency": 180, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:01:30.6772133+08:00" - }, - { - "website_id": "188cb7934d5d413890dea61f", - "url_id": "d841646c7b41", - "url": "https://work.api.shumengya.top", - "status_code": 200, - "latency": 57, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:06:30.2642712+08:00" - }, - { - "website_id": "188cb7934d5d413890dea61f", - "url_id": "d841646c7b41", - "url": "https://work.api.shumengya.top", - "status_code": 200, - "latency": 219, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:10:58.1692978+08:00" - }, - { - "website_id": "188cb7934d5d413890dea61f", - "url_id": "d841646c7b41", - "url": "https://work.api.shumengya.top", - "status_code": 200, - "latency": 76, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:15:58.0263155+08:00" - }, - { - "website_id": "188cb7934d5d413890dea61f", - "url_id": "d841646c7b41", - "url": "https://work.api.shumengya.top", - "status_code": 200, - "latency": 64, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:20:58.0167654+08:00" - }, - { - "website_id": "188cb7934d5d413890dea61f", - "url_id": "d841646c7b41", - "url": "https://work.api.shumengya.top", - "status_code": 200, - "latency": 61, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:25:58.0620642+08:00" - }, - { - "website_id": "188cb7934d5d413890dea61f", - "url_id": "d841646c7b41", - "url": "https://work.api.shumengya.top", - "status_code": 200, - "latency": 69, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:30:58.1995911+08:00" - }, - { - "website_id": "188cb7934d5d413890dea61f", - "url_id": "d841646c7b41", - "url": "https://work.api.shumengya.top", - "status_code": 200, - "latency": 56, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:35:58.2453498+08:00" - }, - { - "website_id": "188cb7934d5d413890dea61f", - "url_id": "d841646c7b41", - "url": "https://work.api.shumengya.top", - "status_code": 200, - "latency": 192, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:40:58.1452851+08:00" - }, - { - "website_id": "188cb7934d5d413890dea61f", - "url_id": "d841646c7b41", - "url": "https://work.api.shumengya.top", - "status_code": 200, - "latency": 60, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:45:58.1014934+08:00" - }, - { - "website_id": "188cb7934d5d413890dea61f", - "url_id": "d841646c7b41", - "url": "https://work.api.shumengya.top", - "status_code": 200, - "latency": 189, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:13:26.7330356+08:00" - }, - { - "website_id": "188cb7934d5d413890dea61f", - "url_id": "d841646c7b41", - "url": "https://work.api.shumengya.top", - "status_code": 200, - "latency": 70, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:21:35.9983941+08:00" - }, - { - "website_id": "188cb7934d5d413890dea61f", - "url_id": "d841646c7b41", - "url": "https://work.api.shumengya.top", - "status_code": 200, - "latency": 79, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:26:36.0178597+08:00" - }, - { - "website_id": "188cb7934d5d413890dea61f", - "url_id": "d841646c7b41", - "url": "https://work.api.shumengya.top", - "status_code": 200, - "latency": 87, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:31:36.0192552+08:00" - }, - { - "website_id": "188cb7934d5d413890dea61f", - "url_id": "d841646c7b41", - "url": "https://work.api.shumengya.top", - "status_code": 200, - "latency": 64, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:36:36.3138919+08:00" - }, - { - "website_id": "188cb7934d5d413890dea61f", - "url_id": "d841646c7b41", - "url": "https://work.api.shumengya.top", - "status_code": 200, - "latency": 144, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:41:37.1690783+08:00" - }, - { - "website_id": "188cb7934d5d413890dea61f", - "url_id": "d841646c7b41", - "url": "https://work.api.shumengya.top", - "status_code": 200, - "latency": 65, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:46:37.7446949+08:00" - }, - { - "website_id": "188cb7934d5d413890dea61f", - "url_id": "d841646c7b41", - "url": "https://work.api.shumengya.top", - "status_code": 200, - "latency": 65, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:51:48.5099247+08:00" - }, - { - "website_id": "188cb7934d5d413890dea61f", - "url_id": "d841646c7b41", - "url": "https://work.api.shumengya.top", - "status_code": 200, - "latency": 67, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:56:36.0682418+08:00" - }, - { - "website_id": "188cb7934d5d413890dea61f", - "url_id": "d841646c7b41", - "url": "https://work.api.shumengya.top", - "status_code": 200, - "latency": 79, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:49:25.4163929+08:00" - }, - { - "website_id": "188cb7934d5d413890dea61f", - "url_id": "d841646c7b41", - "url": "https://work.api.shumengya.top", - "status_code": 200, - "latency": 59, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:53:06.8010801+08:00" - }, - { - "website_id": "188cb7934d5d413890dea61f", - "url_id": "d841646c7b41", - "url": "https://work.api.shumengya.top", - "status_code": 200, - "latency": 69, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:57:48.7900122+08:00" - }, - { - "website_id": "188cb7934d5d413890dea61f", - "url_id": "d841646c7b41", - "url": "https://work.api.shumengya.top", - "status_code": 200, - "latency": 68, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:02:44.903351+08:00" - }, - { - "website_id": "188cb7934d5d413890dea61f", - "url_id": "d841646c7b41", - "url": "https://work.api.shumengya.top", - "status_code": 200, - "latency": 68, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:07:47.572619+08:00" - }, - { - "website_id": "188cb7934d5d413890dea61f", - "url_id": "d841646c7b41", - "url": "https://work.api.shumengya.top", - "status_code": 200, - "latency": 72, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:12:44.4573778+08:00" - } - ], - "188cb79ed2aeb85cabcf6e66_7c8ff0e48be4": [ - { - "website_id": "188cb79ed2aeb85cabcf6e66", - "url_id": "7c8ff0e48be4", - "url": "https://infogenie.api.shumengya.top", - "status_code": 200, - "latency": 159, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:01:30.6998268+08:00" - }, - { - "website_id": "188cb79ed2aeb85cabcf6e66", - "url_id": "7c8ff0e48be4", - "url": "https://infogenie.api.shumengya.top", - "status_code": 200, - "latency": 54, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:06:30.1924139+08:00" - }, - { - "website_id": "188cb79ed2aeb85cabcf6e66", - "url_id": "7c8ff0e48be4", - "url": "https://infogenie.api.shumengya.top", - "status_code": 200, - "latency": 218, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:10:58.1685108+08:00" - }, - { - "website_id": "188cb79ed2aeb85cabcf6e66", - "url_id": "7c8ff0e48be4", - "url": "https://infogenie.api.shumengya.top", - "status_code": 200, - "latency": 60, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:15:58.04085+08:00" - }, - { - "website_id": "188cb79ed2aeb85cabcf6e66", - "url_id": "7c8ff0e48be4", - "url": "https://infogenie.api.shumengya.top", - "status_code": 200, - "latency": 65, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:20:58.0773066+08:00" - }, - { - "website_id": "188cb79ed2aeb85cabcf6e66", - "url_id": "7c8ff0e48be4", - "url": "https://infogenie.api.shumengya.top", - "status_code": 200, - "latency": 61, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:25:58.0744634+08:00" - }, - { - "website_id": "188cb79ed2aeb85cabcf6e66", - "url_id": "7c8ff0e48be4", - "url": "https://infogenie.api.shumengya.top", - "status_code": 200, - "latency": 64, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:30:58.2121164+08:00" - }, - { - "website_id": "188cb79ed2aeb85cabcf6e66", - "url_id": "7c8ff0e48be4", - "url": "https://infogenie.api.shumengya.top", - "status_code": 200, - "latency": 57, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:35:58.2474699+08:00" - }, - { - "website_id": "188cb79ed2aeb85cabcf6e66", - "url_id": "7c8ff0e48be4", - "url": "https://infogenie.api.shumengya.top", - "status_code": 200, - "latency": 70, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:40:58.029498+08:00" - }, - { - "website_id": "188cb79ed2aeb85cabcf6e66", - "url_id": "7c8ff0e48be4", - "url": "https://infogenie.api.shumengya.top", - "status_code": 200, - "latency": 62, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:45:58.1059332+08:00" - }, - { - "website_id": "188cb79ed2aeb85cabcf6e66", - "url_id": "7c8ff0e48be4", - "url": "https://infogenie.api.shumengya.top", - "status_code": 200, - "latency": 188, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:13:26.7426589+08:00" - }, - { - "website_id": "188cb79ed2aeb85cabcf6e66", - "url_id": "7c8ff0e48be4", - "url": "https://infogenie.api.shumengya.top", - "status_code": 200, - "latency": 70, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:21:35.9983941+08:00" - }, - { - "website_id": "188cb79ed2aeb85cabcf6e66", - "url_id": "7c8ff0e48be4", - "url": "https://infogenie.api.shumengya.top", - "status_code": 200, - "latency": 64, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:26:36.0789775+08:00" - }, - { - "website_id": "188cb79ed2aeb85cabcf6e66", - "url_id": "7c8ff0e48be4", - "url": "https://infogenie.api.shumengya.top", - "status_code": 200, - "latency": 66, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:31:36.15177+08:00" - }, - { - "website_id": "188cb79ed2aeb85cabcf6e66", - "url_id": "7c8ff0e48be4", - "url": "https://infogenie.api.shumengya.top", - "status_code": 200, - "latency": 60, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:36:36.3343775+08:00" - }, - { - "website_id": "188cb79ed2aeb85cabcf6e66", - "url_id": "7c8ff0e48be4", - "url": "https://infogenie.api.shumengya.top", - "status_code": 200, - "latency": 63, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:41:48.1745676+08:00" - }, - { - "website_id": "188cb79ed2aeb85cabcf6e66", - "url_id": "7c8ff0e48be4", - "url": "https://infogenie.api.shumengya.top", - "status_code": 200, - "latency": 67, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:46:37.8123637+08:00" - }, - { - "website_id": "188cb79ed2aeb85cabcf6e66", - "url_id": "7c8ff0e48be4", - "url": "https://infogenie.api.shumengya.top", - "status_code": 200, - "latency": 66, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:51:35.9936441+08:00" - }, - { - "website_id": "188cb79ed2aeb85cabcf6e66", - "url_id": "7c8ff0e48be4", - "url": "https://infogenie.api.shumengya.top", - "status_code": 200, - "latency": 58, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:56:36.1110109+08:00" - }, - { - "website_id": "188cb79ed2aeb85cabcf6e66", - "url_id": "7c8ff0e48be4", - "url": "https://infogenie.api.shumengya.top", - "status_code": 200, - "latency": 85, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:49:25.6187041+08:00" - }, - { - "website_id": "188cb79ed2aeb85cabcf6e66", - "url_id": "7c8ff0e48be4", - "url": "https://infogenie.api.shumengya.top", - "status_code": 200, - "latency": 76, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:53:07.8780829+08:00" - }, - { - "website_id": "188cb79ed2aeb85cabcf6e66", - "url_id": "7c8ff0e48be4", - "url": "https://infogenie.api.shumengya.top", - "status_code": 200, - "latency": 79, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:57:49.8722454+08:00" - }, - { - "website_id": "188cb79ed2aeb85cabcf6e66", - "url_id": "7c8ff0e48be4", - "url": "https://infogenie.api.shumengya.top", - "status_code": 200, - "latency": 85, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:02:45.9962574+08:00" - }, - { - "website_id": "188cb79ed2aeb85cabcf6e66", - "url_id": "7c8ff0e48be4", - "url": "https://infogenie.api.shumengya.top", - "status_code": 200, - "latency": 83, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:07:48.656311+08:00" - }, - { - "website_id": "188cb79ed2aeb85cabcf6e66", - "url_id": "7c8ff0e48be4", - "url": "https://infogenie.api.shumengya.top", - "status_code": 200, - "latency": 91, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:12:45.5549051+08:00" - } - ], - "188cb7b7b154ddbc1dc4b14d_ef0443476dd8": [ - { - "website_id": "188cb7b7b154ddbc1dc4b14d", - "url_id": "ef0443476dd8", - "url": "https://short.api.shumengya.top", - "status_code": 200, - "latency": 187, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:01:30.7285846+08:00" - }, - { - "website_id": "188cb7b7b154ddbc1dc4b14d", - "url_id": "ef0443476dd8", - "url": "https://short.api.shumengya.top", - "status_code": 200, - "latency": 45, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:06:30.3015358+08:00" - }, - { - "website_id": "188cb7b7b154ddbc1dc4b14d", - "url_id": "ef0443476dd8", - "url": "https://short.api.shumengya.top", - "status_code": 200, - "latency": 219, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:10:58.1692978+08:00" - }, - { - "website_id": "188cb7b7b154ddbc1dc4b14d", - "url_id": "ef0443476dd8", - "url": "https://short.api.shumengya.top", - "status_code": 200, - "latency": 60, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:15:58.0692121+08:00" - }, - { - "website_id": "188cb7b7b154ddbc1dc4b14d", - "url_id": "ef0443476dd8", - "url": "https://short.api.shumengya.top", - "status_code": 200, - "latency": 187, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:20:58.2000162+08:00" - }, - { - "website_id": "188cb7b7b154ddbc1dc4b14d", - "url_id": "ef0443476dd8", - "url": "https://short.api.shumengya.top", - "status_code": 200, - "latency": 56, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:25:58.0095669+08:00" - }, - { - "website_id": "188cb7b7b154ddbc1dc4b14d", - "url_id": "ef0443476dd8", - "url": "https://short.api.shumengya.top", - "status_code": 200, - "latency": 116, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:30:58.2123859+08:00" - }, - { - "website_id": "188cb7b7b154ddbc1dc4b14d", - "url_id": "ef0443476dd8", - "url": "https://short.api.shumengya.top", - "status_code": 200, - "latency": 59, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:35:58.2063489+08:00" - }, - { - "website_id": "188cb7b7b154ddbc1dc4b14d", - "url_id": "ef0443476dd8", - "url": "https://short.api.shumengya.top", - "status_code": 200, - "latency": 215, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:40:58.1862345+08:00" - }, - { - "website_id": "188cb7b7b154ddbc1dc4b14d", - "url_id": "ef0443476dd8", - "url": "https://short.api.shumengya.top", - "status_code": 200, - "latency": 57, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:45:58.0972452+08:00" - }, - { - "website_id": "188cb7b7b154ddbc1dc4b14d", - "url_id": "ef0443476dd8", - "url": "https://short.api.shumengya.top", - "status_code": 200, - "latency": 69, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:13:26.3799124+08:00" - }, - { - "website_id": "188cb7b7b154ddbc1dc4b14d", - "url_id": "ef0443476dd8", - "url": "https://short.api.shumengya.top", - "status_code": 200, - "latency": 171, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:21:36.0990553+08:00" - }, - { - "website_id": "188cb7b7b154ddbc1dc4b14d", - "url_id": "ef0443476dd8", - "url": "https://short.api.shumengya.top", - "status_code": 200, - "latency": 71, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:26:36.0773031+08:00" - }, - { - "website_id": "188cb7b7b154ddbc1dc4b14d", - "url_id": "ef0443476dd8", - "url": "https://short.api.shumengya.top", - "status_code": 200, - "latency": 62, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:31:36.1488853+08:00" - }, - { - "website_id": "188cb7b7b154ddbc1dc4b14d", - "url_id": "ef0443476dd8", - "url": "https://short.api.shumengya.top", - "status_code": 200, - "latency": 59, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:36:36.373118+08:00" - }, - { - "website_id": "188cb7b7b154ddbc1dc4b14d", - "url_id": "ef0443476dd8", - "url": "https://short.api.shumengya.top", - "status_code": 200, - "latency": 61, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:41:35.998855+08:00" - }, - { - "website_id": "188cb7b7b154ddbc1dc4b14d", - "url_id": "ef0443476dd8", - "url": "https://short.api.shumengya.top", - "status_code": 200, - "latency": 71, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:46:37.6448066+08:00" - }, - { - "website_id": "188cb7b7b154ddbc1dc4b14d", - "url_id": "ef0443476dd8", - "url": "https://short.api.shumengya.top", - "status_code": 200, - "latency": 62, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:51:35.9911733+08:00" - }, - { - "website_id": "188cb7b7b154ddbc1dc4b14d", - "url_id": "ef0443476dd8", - "url": "https://short.api.shumengya.top", - "status_code": 200, - "latency": 58, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:56:36.0568957+08:00" - }, - { - "website_id": "188cb7b7b154ddbc1dc4b14d", - "url_id": "ef0443476dd8", - "url": "https://short.api.shumengya.top", - "status_code": 200, - "latency": 852, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:49:22.7519626+08:00" - }, - { - "website_id": "188cb7b7b154ddbc1dc4b14d", - "url_id": "ef0443476dd8", - "url": "https://short.api.shumengya.top", - "status_code": 200, - "latency": 58, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:53:08.9372622+08:00" - }, - { - "website_id": "188cb7b7b154ddbc1dc4b14d", - "url_id": "ef0443476dd8", - "url": "https://short.api.shumengya.top", - "status_code": 200, - "latency": 64, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:57:50.9424798+08:00" - }, - { - "website_id": "188cb7b7b154ddbc1dc4b14d", - "url_id": "ef0443476dd8", - "url": "https://short.api.shumengya.top", - "status_code": 200, - "latency": 71, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:02:47.0746974+08:00" - }, - { - "website_id": "188cb7b7b154ddbc1dc4b14d", - "url_id": "ef0443476dd8", - "url": "https://short.api.shumengya.top", - "status_code": 200, - "latency": 65, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:07:49.7249765+08:00" - }, - { - "website_id": "188cb7b7b154ddbc1dc4b14d", - "url_id": "ef0443476dd8", - "url": "https://short.api.shumengya.top", - "status_code": 200, - "latency": 62, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:12:46.6198048+08:00" - } - ], - "188cb7c8586dd2c4dcf4bd1e_432740a25595": [ - { - "website_id": "188cb7c8586dd2c4dcf4bd1e", - "url_id": "432740a25595", - "url": "https://bottle.api.shumengya.top", - "status_code": 200, - "latency": 197, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:01:30.7464116+08:00" - }, - { - "website_id": "188cb7c8586dd2c4dcf4bd1e", - "url_id": "432740a25595", - "url": "https://bottle.api.shumengya.top", - "status_code": 200, - "latency": 55, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:06:30.2982425+08:00" - }, - { - "website_id": "188cb7c8586dd2c4dcf4bd1e", - "url_id": "432740a25595", - "url": "https://bottle.api.shumengya.top", - "status_code": 200, - "latency": 94, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:10:58.0442056+08:00" - }, - { - "website_id": "188cb7c8586dd2c4dcf4bd1e", - "url_id": "432740a25595", - "url": "https://bottle.api.shumengya.top", - "status_code": 200, - "latency": 72, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:15:58.0863885+08:00" - }, - { - "website_id": "188cb7c8586dd2c4dcf4bd1e", - "url_id": "432740a25595", - "url": "https://bottle.api.shumengya.top", - "status_code": 200, - "latency": 70, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:20:58.0818807+08:00" - }, - { - "website_id": "188cb7c8586dd2c4dcf4bd1e", - "url_id": "432740a25595", - "url": "https://bottle.api.shumengya.top", - "status_code": 200, - "latency": 74, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:25:58.0266973+08:00" - }, - { - "website_id": "188cb7c8586dd2c4dcf4bd1e", - "url_id": "432740a25595", - "url": "https://bottle.api.shumengya.top", - "status_code": 200, - "latency": 64, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:30:58.263104+08:00" - }, - { - "website_id": "188cb7c8586dd2c4dcf4bd1e", - "url_id": "432740a25595", - "url": "https://bottle.api.shumengya.top", - "status_code": 200, - "latency": 69, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:35:58.351494+08:00" - }, - { - "website_id": "188cb7c8586dd2c4dcf4bd1e", - "url_id": "432740a25595", - "url": "https://bottle.api.shumengya.top", - "status_code": 200, - "latency": 112, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:40:58.0646163+08:00" - }, - { - "website_id": "188cb7c8586dd2c4dcf4bd1e", - "url_id": "432740a25595", - "url": "https://bottle.api.shumengya.top", - "status_code": 200, - "latency": 68, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:45:58.2543601+08:00" - }, - { - "website_id": "188cb7c8586dd2c4dcf4bd1e", - "url_id": "432740a25595", - "url": "https://bottle.api.shumengya.top", - "status_code": 200, - "latency": 70, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:13:27.6682422+08:00" - }, - { - "website_id": "188cb7c8586dd2c4dcf4bd1e", - "url_id": "432740a25595", - "url": "https://bottle.api.shumengya.top", - "status_code": 200, - "latency": 210, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:21:36.1383343+08:00" - }, - { - "website_id": "188cb7c8586dd2c4dcf4bd1e", - "url_id": "432740a25595", - "url": "https://bottle.api.shumengya.top", - "status_code": 200, - "latency": 72, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:26:36.3811642+08:00" - }, - { - "website_id": "188cb7c8586dd2c4dcf4bd1e", - "url_id": "432740a25595", - "url": "https://bottle.api.shumengya.top", - "status_code": 200, - "latency": 77, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:31:36.2273867+08:00" - }, - { - "website_id": "188cb7c8586dd2c4dcf4bd1e", - "url_id": "432740a25595", - "url": "https://bottle.api.shumengya.top", - "status_code": 200, - "latency": 64, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:36:36.2731434+08:00" - }, - { - "website_id": "188cb7c8586dd2c4dcf4bd1e", - "url_id": "432740a25595", - "url": "https://bottle.api.shumengya.top", - "status_code": 200, - "latency": 69, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:41:36.0060409+08:00" - }, - { - "website_id": "188cb7c8586dd2c4dcf4bd1e", - "url_id": "432740a25595", - "url": "https://bottle.api.shumengya.top", - "status_code": 200, - "latency": 82, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:46:48.1445319+08:00" - }, - { - "website_id": "188cb7c8586dd2c4dcf4bd1e", - "url_id": "432740a25595", - "url": "https://bottle.api.shumengya.top", - "status_code": 200, - "latency": 73, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:51:36.0648486+08:00" - }, - { - "website_id": "188cb7c8586dd2c4dcf4bd1e", - "url_id": "432740a25595", - "url": "https://bottle.api.shumengya.top", - "status_code": 200, - "latency": 79, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:56:37.2937533+08:00" - }, - { - "website_id": "188cb7c8586dd2c4dcf4bd1e", - "url_id": "432740a25595", - "url": "https://bottle.api.shumengya.top", - "status_code": 200, - "latency": 720, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:49:22.6196686+08:00" - }, - { - "website_id": "188cb7c8586dd2c4dcf4bd1e", - "url_id": "432740a25595", - "url": "https://bottle.api.shumengya.top", - "status_code": 200, - "latency": 63, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:53:10.0089992+08:00" - }, - { - "website_id": "188cb7c8586dd2c4dcf4bd1e", - "url_id": "432740a25595", - "url": "https://bottle.api.shumengya.top", - "status_code": 200, - "latency": 76, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:57:52.0238192+08:00" - }, - { - "website_id": "188cb7c8586dd2c4dcf4bd1e", - "url_id": "432740a25595", - "url": "https://bottle.api.shumengya.top", - "status_code": 200, - "latency": 86, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:02:48.1703743+08:00" - }, - { - "website_id": "188cb7c8586dd2c4dcf4bd1e", - "url_id": "432740a25595", - "url": "https://bottle.api.shumengya.top", - "status_code": 200, - "latency": 76, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:07:50.8018139+08:00" - }, - { - "website_id": "188cb7c8586dd2c4dcf4bd1e", - "url_id": "432740a25595", - "url": "https://bottle.api.shumengya.top", - "status_code": 200, - "latency": 77, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:12:47.7026518+08:00" - } - ], - "188cf6ed769b75e497cd2a4a_23ba1b4ea7db": null, - "188cf6ed769b75e497cd2a4a_a3e3ff7839f1": [ - { - "website_id": "188cf6ed769b75e497cd2a4a", - "url_id": "a3e3ff7839f1", - "url": "http://10.0.0.233:19132/smy", - "status_code": 0, - "latency": 10002, - "is_up": false, - "error": "Get \"http://10.0.0.233:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:01:40.5606259+08:00" - }, - { - "website_id": "188cf6ed769b75e497cd2a4a", - "url_id": "a3e3ff7839f1", - "url": "http://10.0.0.233:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:06:40.2650118+08:00" - }, - { - "website_id": "188cf6ed769b75e497cd2a4a", - "url_id": "a3e3ff7839f1", - "url": "http://10.0.0.233:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:11:07.9960581+08:00" - }, - { - "website_id": "188cf6ed769b75e497cd2a4a", - "url_id": "a3e3ff7839f1", - "url": "http://10.0.0.233:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:16:07.9504714+08:00" - }, - { - "website_id": "188cf6ed769b75e497cd2a4a", - "url_id": "a3e3ff7839f1", - "url": "http://10.0.0.233:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:21:08.4140593+08:00" - }, - { - "website_id": "188cf6ed769b75e497cd2a4a", - "url_id": "a3e3ff7839f1", - "url": "http://10.0.0.233:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:26:08.1236262+08:00" - }, - { - "website_id": "188cf6ed769b75e497cd2a4a", - "url_id": "a3e3ff7839f1", - "url": "http://10.0.0.233:19132/smy", - "status_code": 0, - "latency": 10003, - "is_up": false, - "error": "Get \"http://10.0.0.233:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:31:08.1791585+08:00" - }, - { - "website_id": "188cf6ed769b75e497cd2a4a", - "url_id": "a3e3ff7839f1", - "url": "http://10.0.0.233:19132/smy", - "status_code": 0, - "latency": 10003, - "is_up": false, - "error": "Get \"http://10.0.0.233:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:36:08.2744981+08:00" - }, - { - "website_id": "188cf6ed769b75e497cd2a4a", - "url_id": "a3e3ff7839f1", - "url": "http://10.0.0.233:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:41:08.402227+08:00" - }, - { - "website_id": "188cf6ed769b75e497cd2a4a", - "url_id": "a3e3ff7839f1", - "url": "http://10.0.0.233:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:46:08.1778069+08:00" - }, - { - "website_id": "188cf6ed769b75e497cd2a4a", - "url_id": "a3e3ff7839f1", - "url": "http://10.0.0.233:19132/smy", - "status_code": 0, - "latency": 10005, - "is_up": false, - "error": "Get \"http://10.0.0.233:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:14:18.8051009+08:00" - }, - { - "website_id": "188cf6ed769b75e497cd2a4a", - "url_id": "a3e3ff7839f1", - "url": "http://10.0.0.233:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:22:28.0100853+08:00" - }, - { - "website_id": "188cf6ed769b75e497cd2a4a", - "url_id": "a3e3ff7839f1", - "url": "http://10.0.0.233:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:27:27.952505+08:00" - }, - { - "website_id": "188cf6ed769b75e497cd2a4a", - "url_id": "a3e3ff7839f1", - "url": "http://10.0.0.233:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:32:28.1236435+08:00" - }, - { - "website_id": "188cf6ed769b75e497cd2a4a", - "url_id": "a3e3ff7839f1", - "url": "http://10.0.0.233:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:37:29.0114839+08:00" - }, - { - "website_id": "188cf6ed769b75e497cd2a4a", - "url_id": "a3e3ff7839f1", - "url": "http://10.0.0.233:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:42:28.8672833+08:00" - }, - { - "website_id": "188cf6ed769b75e497cd2a4a", - "url_id": "a3e3ff7839f1", - "url": "http://10.0.0.233:19132/smy", - "status_code": 0, - "latency": 10002, - "is_up": false, - "error": "Get \"http://10.0.0.233:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:47:29.9814463+08:00" - }, - { - "website_id": "188cf6ed769b75e497cd2a4a", - "url_id": "a3e3ff7839f1", - "url": "http://10.0.0.233:19132/smy", - "status_code": 0, - "latency": 10008, - "is_up": false, - "error": "Get \"http://10.0.0.233:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:52:28.0188469+08:00" - }, - { - "website_id": "188cf6ed769b75e497cd2a4a", - "url_id": "a3e3ff7839f1", - "url": "http://10.0.0.233:19132/smy", - "status_code": 0, - "latency": 10006, - "is_up": false, - "error": "Get \"http://10.0.0.233:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:57:29.2213981+08:00" - }, - { - "website_id": "188cf6ed769b75e497cd2a4a", - "url_id": "a3e3ff7839f1", - "url": "http://10.0.0.233:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T12:50:13.9026203+08:00" - }, - { - "website_id": "188cf6ed769b75e497cd2a4a", - "url_id": "a3e3ff7839f1", - "url": "http://10.0.0.233:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T12:54:03.0220505+08:00" - }, - { - "website_id": "188cf6ed769b75e497cd2a4a", - "url_id": "a3e3ff7839f1", - "url": "http://10.0.0.233:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T12:58:45.0472983+08:00" - }, - { - "website_id": "188cf6ed769b75e497cd2a4a", - "url_id": "a3e3ff7839f1", - "url": "http://10.0.0.233:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T13:03:41.1764874+08:00" - }, - { - "website_id": "188cf6ed769b75e497cd2a4a", - "url_id": "a3e3ff7839f1", - "url": "http://10.0.0.233:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T13:08:43.8163999+08:00" - } - ], - "188cf6f322f76b90c35c3484_397f5ededede": null, - "188cf6f322f76b90c35c3484_76b6f9fe4f04": [ - { - "website_id": "188cf6f322f76b90c35c3484", - "url_id": "76b6f9fe4f04", - "url": "http://10.0.0.100:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.100:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:01:40.6795729+08:00" - }, - { - "website_id": "188cf6f322f76b90c35c3484", - "url_id": "76b6f9fe4f04", - "url": "http://10.0.0.100:19132/smy", - "status_code": 0, - "latency": 10004, - "is_up": false, - "error": "Get \"http://10.0.0.100:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:06:40.1434301+08:00" - }, - { - "website_id": "188cf6f322f76b90c35c3484", - "url_id": "76b6f9fe4f04", - "url": "http://10.0.0.100:19132/smy", - "status_code": 0, - "latency": 10006, - "is_up": false, - "error": "Get \"http://10.0.0.100:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:11:08.0422156+08:00" - }, - { - "website_id": "188cf6f322f76b90c35c3484", - "url_id": "76b6f9fe4f04", - "url": "http://10.0.0.100:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.100:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:16:07.9504714+08:00" - }, - { - "website_id": "188cf6f322f76b90c35c3484", - "url_id": "76b6f9fe4f04", - "url": "http://10.0.0.100:19132/smy", - "status_code": 0, - "latency": 10002, - "is_up": false, - "error": "Get \"http://10.0.0.100:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:21:08.3665506+08:00" - }, - { - "website_id": "188cf6f322f76b90c35c3484", - "url_id": "76b6f9fe4f04", - "url": "http://10.0.0.100:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.100:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:26:08.0866511+08:00" - }, - { - "website_id": "188cf6f322f76b90c35c3484", - "url_id": "76b6f9fe4f04", - "url": "http://10.0.0.100:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.100:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:31:08.2299461+08:00" - }, - { - "website_id": "188cf6f322f76b90c35c3484", - "url_id": "76b6f9fe4f04", - "url": "http://10.0.0.100:19132/smy", - "status_code": 0, - "latency": 10002, - "is_up": false, - "error": "Get \"http://10.0.0.100:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:36:08.3036422+08:00" - }, - { - "website_id": "188cf6f322f76b90c35c3484", - "url_id": "76b6f9fe4f04", - "url": "http://10.0.0.100:19132/smy", - "status_code": 0, - "latency": 10002, - "is_up": false, - "error": "Get \"http://10.0.0.100:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:41:08.4391333+08:00" - }, - { - "website_id": "188cf6f322f76b90c35c3484", - "url_id": "76b6f9fe4f04", - "url": "http://10.0.0.100:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.100:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:46:08.2375927+08:00" - }, - { - "website_id": "188cf6f322f76b90c35c3484", - "url_id": "76b6f9fe4f04", - "url": "http://10.0.0.100:19132/smy", - "status_code": 0, - "latency": 10005, - "is_up": false, - "error": "Get \"http://10.0.0.100:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:14:19.6847059+08:00" - }, - { - "website_id": "188cf6f322f76b90c35c3484", - "url_id": "76b6f9fe4f04", - "url": "http://10.0.0.100:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.100:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:22:28.0092059+08:00" - }, - { - "website_id": "188cf6f322f76b90c35c3484", - "url_id": "76b6f9fe4f04", - "url": "http://10.0.0.100:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.100:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:27:28.0337123+08:00" - }, - { - "website_id": "188cf6f322f76b90c35c3484", - "url_id": "76b6f9fe4f04", - "url": "http://10.0.0.100:19132/smy", - "status_code": 0, - "latency": 10001, - "is_up": false, - "error": "Get \"http://10.0.0.100:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:32:40.1326651+08:00" - }, - { - "website_id": "188cf6f322f76b90c35c3484", - "url_id": "76b6f9fe4f04", - "url": "http://10.0.0.100:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.100:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:37:28.8493808+08:00" - }, - { - "website_id": "188cf6f322f76b90c35c3484", - "url_id": "76b6f9fe4f04", - "url": "http://10.0.0.100:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.100:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:42:28.8216032+08:00" - }, - { - "website_id": "188cf6f322f76b90c35c3484", - "url_id": "76b6f9fe4f04", - "url": "http://10.0.0.100:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.100:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:47:40.163012+08:00" - }, - { - "website_id": "188cf6f322f76b90c35c3484", - "url_id": "76b6f9fe4f04", - "url": "http://10.0.0.100:19132/smy", - "status_code": 0, - "latency": 10008, - "is_up": false, - "error": "Get \"http://10.0.0.100:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:52:28.0187936+08:00" - }, - { - "website_id": "188cf6f322f76b90c35c3484", - "url_id": "76b6f9fe4f04", - "url": "http://10.0.0.100:19132/smy", - "status_code": 0, - "latency": 10010, - "is_up": false, - "error": "Get \"http://10.0.0.100:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:57:40.0748003+08:00" - }, - { - "website_id": "188cf6f322f76b90c35c3484", - "url_id": "76b6f9fe4f04", - "url": "http://10.0.0.100:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.100:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T12:50:16.2655756+08:00" - }, - { - "website_id": "188cf6f322f76b90c35c3484", - "url_id": "76b6f9fe4f04", - "url": "http://10.0.0.100:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.100:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T12:54:56.0404083+08:00" - }, - { - "website_id": "188cf6f322f76b90c35c3484", - "url_id": "76b6f9fe4f04", - "url": "http://10.0.0.100:19132/smy", - "status_code": 0, - "latency": 10007, - "is_up": false, - "error": "Get \"http://10.0.0.100:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T12:59:38.0666251+08:00" - }, - { - "website_id": "188cf6f322f76b90c35c3484", - "url_id": "76b6f9fe4f04", - "url": "http://10.0.0.100:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.100:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T13:04:34.1927657+08:00" - }, - { - "website_id": "188cf6f322f76b90c35c3484", - "url_id": "76b6f9fe4f04", - "url": "http://10.0.0.100:19132/smy", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.100:19132/smy\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T13:09:36.8300814+08:00" - } - ], - "188cf7019e56e618827f4927_093d47789bb5": [ - { - "website_id": "188cf7019e56e618827f4927", - "url_id": "093d47789bb5", - "url": "http://10.0.0.233:8484/", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:01:40.7006769+08:00" - }, - { - "website_id": "188cf7019e56e618827f4927", - "url_id": "093d47789bb5", - "url": "http://10.0.0.233:8484/", - "status_code": 0, - "latency": 10006, - "is_up": false, - "error": "Get \"http://10.0.0.233:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:06:40.2255874+08:00" - }, - { - "website_id": "188cf7019e56e618827f4927", - "url_id": "093d47789bb5", - "url": "http://10.0.0.233:8484/", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:11:08.0444398+08:00" - }, - { - "website_id": "188cf7019e56e618827f4927", - "url_id": "093d47789bb5", - "url": "http://10.0.0.233:8484/", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:16:07.9504714+08:00" - }, - { - "website_id": "188cf7019e56e618827f4927", - "url_id": "093d47789bb5", - "url": "http://10.0.0.233:8484/", - "status_code": 0, - "latency": 10004, - "is_up": false, - "error": "Get \"http://10.0.0.233:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:21:08.3847769+08:00" - }, - { - "website_id": "188cf7019e56e618827f4927", - "url_id": "093d47789bb5", - "url": "http://10.0.0.233:8484/", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:26:08.1301227+08:00" - }, - { - "website_id": "188cf7019e56e618827f4927", - "url_id": "093d47789bb5", - "url": "http://10.0.0.233:8484/", - "status_code": 0, - "latency": 10001, - "is_up": false, - "error": "Get \"http://10.0.0.233:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:31:08.2791726+08:00" - }, - { - "website_id": "188cf7019e56e618827f4927", - "url_id": "093d47789bb5", - "url": "http://10.0.0.233:8484/", - "status_code": 0, - "latency": 10004, - "is_up": false, - "error": "Get \"http://10.0.0.233:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:36:08.3562871+08:00" - }, - { - "website_id": "188cf7019e56e618827f4927", - "url_id": "093d47789bb5", - "url": "http://10.0.0.233:8484/", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:41:07.9522989+08:00" - }, - { - "website_id": "188cf7019e56e618827f4927", - "url_id": "093d47789bb5", - "url": "http://10.0.0.233:8484/", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:46:08.2936102+08:00" - }, - { - "website_id": "188cf7019e56e618827f4927", - "url_id": "093d47789bb5", - "url": "http://10.0.0.233:8484/", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:14:29.5558267+08:00" - }, - { - "website_id": "188cf7019e56e618827f4927", - "url_id": "093d47789bb5", - "url": "http://10.0.0.233:8484/", - "status_code": 0, - "latency": 10006, - "is_up": false, - "error": "Get \"http://10.0.0.233:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:22:28.0303322+08:00" - }, - { - "website_id": "188cf7019e56e618827f4927", - "url_id": "093d47789bb5", - "url": "http://10.0.0.233:8484/", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:27:28.0333848+08:00" - }, - { - "website_id": "188cf7019e56e618827f4927", - "url_id": "093d47789bb5", - "url": "http://10.0.0.233:8484/", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:32:29.8746448+08:00" - }, - { - "website_id": "188cf7019e56e618827f4927", - "url_id": "093d47789bb5", - "url": "http://10.0.0.233:8484/", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:37:40.370772+08:00" - }, - { - "website_id": "188cf7019e56e618827f4927", - "url_id": "093d47789bb5", - "url": "http://10.0.0.233:8484/", - "status_code": 0, - "latency": 10003, - "is_up": false, - "error": "Get \"http://10.0.0.233:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:42:40.1928092+08:00" - }, - { - "website_id": "188cf7019e56e618827f4927", - "url_id": "093d47789bb5", - "url": "http://10.0.0.233:8484/", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:47:40.1922672+08:00" - }, - { - "website_id": "188cf7019e56e618827f4927", - "url_id": "093d47789bb5", - "url": "http://10.0.0.233:8484/", - "status_code": 0, - "latency": 10008, - "is_up": false, - "error": "Get \"http://10.0.0.233:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:52:28.0186137+08:00" - }, - { - "website_id": "188cf7019e56e618827f4927", - "url_id": "093d47789bb5", - "url": "http://10.0.0.233:8484/", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:57:40.1233094+08:00" - }, - { - "website_id": "188cf7019e56e618827f4927", - "url_id": "093d47789bb5", - "url": "http://10.0.0.233:8484/", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T12:50:16.763396+08:00" - }, - { - "website_id": "188cf7019e56e618827f4927", - "url_id": "093d47789bb5", - "url": "http://10.0.0.233:8484/", - "status_code": 0, - "latency": 10008, - "is_up": false, - "error": "Get \"http://10.0.0.233:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T12:55:49.0575201+08:00" - }, - { - "website_id": "188cf7019e56e618827f4927", - "url_id": "093d47789bb5", - "url": "http://10.0.0.233:8484/", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T13:00:31.0837335+08:00" - }, - { - "website_id": "188cf7019e56e618827f4927", - "url_id": "093d47789bb5", - "url": "http://10.0.0.233:8484/", - "status_code": 0, - "latency": 10008, - "is_up": false, - "error": "Get \"http://10.0.0.233:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T13:05:27.2224784+08:00" - }, - { - "website_id": "188cf7019e56e618827f4927", - "url_id": "093d47789bb5", - "url": "http://10.0.0.233:8484/", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.233:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T13:10:29.8496296+08:00" - } - ], - "188cf7019e56e618827f4927_56250450a6bf": null, - "188cf70700922fa4041f6470_777c292c7696": [ - { - "website_id": "188cf70700922fa4041f6470", - "url_id": "777c292c7696", - "url": "http://10.0.0.100:8484/", - "status_code": 0, - "latency": 10004, - "is_up": false, - "error": "Get \"http://10.0.0.100:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:01:40.4301734+08:00" - }, - { - "website_id": "188cf70700922fa4041f6470", - "url_id": "777c292c7696", - "url": "http://10.0.0.100:8484/", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.100:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:06:40.2101134+08:00" - }, - { - "website_id": "188cf70700922fa4041f6470", - "url_id": "777c292c7696", - "url": "http://10.0.0.100:8484/", - "status_code": 0, - "latency": 10005, - "is_up": false, - "error": "Get \"http://10.0.0.100:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:11:08.1521525+08:00" - }, - { - "website_id": "188cf70700922fa4041f6470", - "url_id": "777c292c7696", - "url": "http://10.0.0.100:8484/", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.100:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:16:07.9504714+08:00" - }, - { - "website_id": "188cf70700922fa4041f6470", - "url_id": "777c292c7696", - "url": "http://10.0.0.100:8484/", - "status_code": 0, - "latency": 10001, - "is_up": false, - "error": "Get \"http://10.0.0.100:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:21:07.9519276+08:00" - }, - { - "website_id": "188cf70700922fa4041f6470", - "url_id": "777c292c7696", - "url": "http://10.0.0.100:8484/", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.100:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:26:07.9525122+08:00" - }, - { - "website_id": "188cf70700922fa4041f6470", - "url_id": "777c292c7696", - "url": "http://10.0.0.100:8484/", - "status_code": 0, - "latency": 10006, - "is_up": false, - "error": "Get \"http://10.0.0.100:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:31:07.9568038+08:00" - }, - { - "website_id": "188cf70700922fa4041f6470", - "url_id": "777c292c7696", - "url": "http://10.0.0.100:8484/", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.100:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:36:07.9497683+08:00" - }, - { - "website_id": "188cf70700922fa4041f6470", - "url_id": "777c292c7696", - "url": "http://10.0.0.100:8484/", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.100:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:41:07.9529112+08:00" - }, - { - "website_id": "188cf70700922fa4041f6470", - "url_id": "777c292c7696", - "url": "http://10.0.0.100:8484/", - "status_code": 0, - "latency": 10006, - "is_up": false, - "error": "Get \"http://10.0.0.100:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T10:46:07.9556634+08:00" - }, - { - "website_id": "188cf70700922fa4041f6470", - "url_id": "777c292c7696", - "url": "http://10.0.0.100:8484/", - "status_code": 0, - "latency": 10006, - "is_up": false, - "error": "Get \"http://10.0.0.100:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:14:17.2821345+08:00" - }, - { - "website_id": "188cf70700922fa4041f6470", - "url_id": "777c292c7696", - "url": "http://10.0.0.100:8484/", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.100:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:22:28.0735007+08:00" - }, - { - "website_id": "188cf70700922fa4041f6470", - "url_id": "777c292c7696", - "url": "http://10.0.0.100:8484/", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.100:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:27:29.686492+08:00" - }, - { - "website_id": "188cf70700922fa4041f6470", - "url_id": "777c292c7696", - "url": "http://10.0.0.100:8484/", - "status_code": 0, - "latency": 10004, - "is_up": false, - "error": "Get \"http://10.0.0.100:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:32:40.1905386+08:00" - }, - { - "website_id": "188cf70700922fa4041f6470", - "url_id": "777c292c7696", - "url": "http://10.0.0.100:8484/", - "status_code": 0, - "latency": 10001, - "is_up": false, - "error": "Get \"http://10.0.0.100:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:37:27.9403521+08:00" - }, - { - "website_id": "188cf70700922fa4041f6470", - "url_id": "777c292c7696", - "url": "http://10.0.0.100:8484/", - "status_code": 0, - "latency": 10009, - "is_up": false, - "error": "Get \"http://10.0.0.100:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:42:27.9558966+08:00" - }, - { - "website_id": "188cf70700922fa4041f6470", - "url_id": "777c292c7696", - "url": "http://10.0.0.100:8484/", - "status_code": 0, - "latency": 10001, - "is_up": false, - "error": "Get \"http://10.0.0.100:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:47:27.9477972+08:00" - }, - { - "website_id": "188cf70700922fa4041f6470", - "url_id": "777c292c7696", - "url": "http://10.0.0.100:8484/", - "status_code": 0, - "latency": 10008, - "is_up": false, - "error": "Get \"http://10.0.0.100:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:52:28.0188469+08:00" - }, - { - "website_id": "188cf70700922fa4041f6470", - "url_id": "777c292c7696", - "url": "http://10.0.0.100:8484/", - "status_code": 0, - "latency": 10001, - "is_up": false, - "error": "Get \"http://10.0.0.100:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T11:57:27.9539136+08:00" - }, - { - "website_id": "188cf70700922fa4041f6470", - "url_id": "777c292c7696", - "url": "http://10.0.0.100:8484/", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.100:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T12:50:13.9028825+08:00" - }, - { - "website_id": "188cf70700922fa4041f6470", - "url_id": "777c292c7696", - "url": "http://10.0.0.100:8484/", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.100:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T12:56:42.0763178+08:00" - }, - { - "website_id": "188cf70700922fa4041f6470", - "url_id": "777c292c7696", - "url": "http://10.0.0.100:8484/", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.100:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T13:01:24.1074538+08:00" - }, - { - "website_id": "188cf70700922fa4041f6470", - "url_id": "777c292c7696", - "url": "http://10.0.0.100:8484/", - "status_code": 0, - "latency": 10000, - "is_up": false, - "error": "Get \"http://10.0.0.100:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T13:06:20.2333278+08:00" - }, - { - "website_id": "188cf70700922fa4041f6470", - "url_id": "777c292c7696", - "url": "http://10.0.0.100:8484/", - "status_code": 0, - "latency": 10005, - "is_up": false, - "error": "Get \"http://10.0.0.100:8484/\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)", - "checked_at": "2026-03-06T13:11:22.8676568+08:00" - } - ], - "188cf70700922fa4041f6470_791643dbecab": null, - "188cf728ed2992f06a8f9e52_0a0a59069228": [ - { - "website_id": "188cf728ed2992f06a8f9e52", - "url_id": "0a0a59069228", - "url": "http://easytier.shumengya.top", - "status_code": 200, - "latency": 57, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:01:30.7881685+08:00" - }, - { - "website_id": "188cf728ed2992f06a8f9e52", - "url_id": "0a0a59069228", - "url": "http://easytier.shumengya.top", - "status_code": 200, - "latency": 185, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:06:30.609613+08:00" - }, - { - "website_id": "188cf728ed2992f06a8f9e52", - "url_id": "0a0a59069228", - "url": "http://easytier.shumengya.top", - "status_code": 200, - "latency": 55, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:10:58.2179881+08:00" - }, - { - "website_id": "188cf728ed2992f06a8f9e52", - "url_id": "0a0a59069228", - "url": "http://easytier.shumengya.top", - "status_code": 200, - "latency": 62, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:15:58.0137853+08:00" - }, - { - "website_id": "188cf728ed2992f06a8f9e52", - "url_id": "0a0a59069228", - "url": "http://easytier.shumengya.top", - "status_code": 200, - "latency": 194, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:20:58.1460864+08:00" - }, - { - "website_id": "188cf728ed2992f06a8f9e52", - "url_id": "0a0a59069228", - "url": "http://easytier.shumengya.top", - "status_code": 200, - "latency": 78, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:25:58.2136575+08:00" - }, - { - "website_id": "188cf728ed2992f06a8f9e52", - "url_id": "0a0a59069228", - "url": "http://easytier.shumengya.top", - "status_code": 200, - "latency": 196, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:30:58.1475443+08:00" - }, - { - "website_id": "188cf728ed2992f06a8f9e52", - "url_id": "0a0a59069228", - "url": "http://easytier.shumengya.top", - "status_code": 200, - "latency": 78, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:35:58.0290719+08:00" - }, - { - "website_id": "188cf728ed2992f06a8f9e52", - "url_id": "0a0a59069228", - "url": "http://easytier.shumengya.top", - "status_code": 200, - "latency": 66, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:40:58.3270898+08:00" - }, - { - "website_id": "188cf728ed2992f06a8f9e52", - "url_id": "0a0a59069228", - "url": "http://easytier.shumengya.top", - "status_code": 200, - "latency": 104, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:45:58.0549829+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-03-06T11:13:26.7000878+08:00" - }, - { - "website_id": "188cf728ed2992f06a8f9e52", - "url_id": "0a0a59069228", - "url": "http://easytier.shumengya.top", - "status_code": 200, - "latency": 62, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:21:36.1636689+08:00" - }, - { - "website_id": "188cf728ed2992f06a8f9e52", - "url_id": "0a0a59069228", - "url": "http://easytier.shumengya.top", - "status_code": 200, - "latency": 76, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:26:36.0149594+08:00" - }, - { - "website_id": "188cf728ed2992f06a8f9e52", - "url_id": "0a0a59069228", - "url": "http://easytier.shumengya.top", - "status_code": 200, - "latency": 96, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:31:36.0276805+08:00" - }, - { - "website_id": "188cf728ed2992f06a8f9e52", - "url_id": "0a0a59069228", - "url": "http://easytier.shumengya.top", - "status_code": 200, - "latency": 79, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:36:36.0730123+08:00" - }, - { - "website_id": "188cf728ed2992f06a8f9e52", - "url_id": "0a0a59069228", - "url": "http://easytier.shumengya.top", - "status_code": 200, - "latency": 70, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:41:36.7330859+08:00" - }, - { - "website_id": "188cf728ed2992f06a8f9e52", - "url_id": "0a0a59069228", - "url": "http://easytier.shumengya.top", - "status_code": 200, - "latency": 57, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:46:35.9860997+08:00" - }, - { - "website_id": "188cf728ed2992f06a8f9e52", - "url_id": "0a0a59069228", - "url": "http://easytier.shumengya.top", - "status_code": 200, - "latency": 64, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:51:36.129555+08:00" - }, - { - "website_id": "188cf728ed2992f06a8f9e52", - "url_id": "0a0a59069228", - "url": "http://easytier.shumengya.top", - "status_code": 200, - "latency": 83, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:56:36.2170485+08:00" - }, - { - "website_id": "188cf728ed2992f06a8f9e52", - "url_id": "0a0a59069228", - "url": "http://easytier.shumengya.top", - "status_code": 200, - "latency": 339, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:49:22.8624484+08:00" - }, - { - "website_id": "188cf728ed2992f06a8f9e52", - "url_id": "0a0a59069228", - "url": "http://easytier.shumengya.top", - "status_code": 200, - "latency": 68, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:56:43.1471902+08:00" - }, - { - "website_id": "188cf728ed2992f06a8f9e52", - "url_id": "0a0a59069228", - "url": "http://easytier.shumengya.top", - "status_code": 200, - "latency": 100, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:01:25.2162719+08:00" - }, - { - "website_id": "188cf728ed2992f06a8f9e52", - "url_id": "0a0a59069228", - "url": "http://easytier.shumengya.top", - "status_code": 200, - "latency": 79, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:06:21.3143458+08:00" - }, - { - "website_id": "188cf728ed2992f06a8f9e52", - "url_id": "0a0a59069228", - "url": "http://easytier.shumengya.top", - "status_code": 200, - "latency": 91, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:11:23.963916+08:00" - } - ], - "188cf72edea71c4cb2284c93_170fab2239ad": [ - { - "website_id": "188cf72edea71c4cb2284c93", - "url_id": "170fab2239ad", - "url": "https://wireguard.shumengya.top", - "status_code": 200, - "latency": 200, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:01:30.9469074+08:00" - }, - { - "website_id": "188cf72edea71c4cb2284c93", - "url_id": "170fab2239ad", - "url": "https://wireguard.shumengya.top", - "status_code": 200, - "latency": 51, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:06:30.1919034+08:00" - }, - { - "website_id": "188cf72edea71c4cb2284c93", - "url_id": "170fab2239ad", - "url": "https://wireguard.shumengya.top", - "status_code": 200, - "latency": 189, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:10:58.358679+08:00" - }, - { - "website_id": "188cf72edea71c4cb2284c93", - "url_id": "170fab2239ad", - "url": "https://wireguard.shumengya.top", - "status_code": 200, - "latency": 55, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:15:58.2560943+08:00" - }, - { - "website_id": "188cf72edea71c4cb2284c93", - "url_id": "170fab2239ad", - "url": "https://wireguard.shumengya.top", - "status_code": 200, - "latency": 196, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:20:58.2012624+08:00" - }, - { - "website_id": "188cf72edea71c4cb2284c93", - "url_id": "170fab2239ad", - "url": "https://wireguard.shumengya.top", - "status_code": 200, - "latency": 54, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:25:58.1879181+08:00" - }, - { - "website_id": "188cf72edea71c4cb2284c93", - "url_id": "170fab2239ad", - "url": "https://wireguard.shumengya.top", - "status_code": 200, - "latency": 59, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:30:58.1151722+08:00" - }, - { - "website_id": "188cf72edea71c4cb2284c93", - "url_id": "170fab2239ad", - "url": "https://wireguard.shumengya.top", - "status_code": 200, - "latency": 74, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:35:58.0687316+08:00" - }, - { - "website_id": "188cf72edea71c4cb2284c93", - "url_id": "170fab2239ad", - "url": "https://wireguard.shumengya.top", - "status_code": 200, - "latency": 66, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:40:58.3689868+08:00" - }, - { - "website_id": "188cf72edea71c4cb2284c93", - "url_id": "170fab2239ad", - "url": "https://wireguard.shumengya.top", - "status_code": 200, - "latency": 63, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:45:58.2932774+08:00" - }, - { - "website_id": "188cf72edea71c4cb2284c93", - "url_id": "170fab2239ad", - "url": "https://wireguard.shumengya.top", - "status_code": 200, - "latency": 59, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:13:26.6262311+08:00" - }, - { - "website_id": "188cf72edea71c4cb2284c93", - "url_id": "170fab2239ad", - "url": "https://wireguard.shumengya.top", - "status_code": 200, - "latency": 64, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:21:36.1792766+08:00" - }, - { - "website_id": "188cf72edea71c4cb2284c93", - "url_id": "170fab2239ad", - "url": "https://wireguard.shumengya.top", - "status_code": 200, - "latency": 56, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:26:36.0822304+08:00" - }, - { - "website_id": "188cf72edea71c4cb2284c93", - "url_id": "170fab2239ad", - "url": "https://wireguard.shumengya.top", - "status_code": 200, - "latency": 62, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:31:36.2827382+08:00" - }, - { - "website_id": "188cf72edea71c4cb2284c93", - "url_id": "170fab2239ad", - "url": "https://wireguard.shumengya.top", - "status_code": 200, - "latency": 55, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:36:36.0500321+08:00" - }, - { - "website_id": "188cf72edea71c4cb2284c93", - "url_id": "170fab2239ad", - "url": "https://wireguard.shumengya.top", - "status_code": 200, - "latency": 64, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:41:36.7755212+08:00" - }, - { - "website_id": "188cf72edea71c4cb2284c93", - "url_id": "170fab2239ad", - "url": "https://wireguard.shumengya.top", - "status_code": 200, - "latency": 68, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:46:36.2842698+08:00" - }, - { - "website_id": "188cf72edea71c4cb2284c93", - "url_id": "170fab2239ad", - "url": "https://wireguard.shumengya.top", - "status_code": 200, - "latency": 59, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:51:36.1895051+08:00" - }, - { - "website_id": "188cf72edea71c4cb2284c93", - "url_id": "170fab2239ad", - "url": "https://wireguard.shumengya.top", - "status_code": 200, - "latency": 61, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:56:36.2444953+08:00" - }, - { - "website_id": "188cf72edea71c4cb2284c93", - "url_id": "170fab2239ad", - "url": "https://wireguard.shumengya.top", - "status_code": 200, - "latency": 807, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:49:23.550941+08:00" - }, - { - "website_id": "188cf72edea71c4cb2284c93", - "url_id": "170fab2239ad", - "url": "https://wireguard.shumengya.top", - "status_code": 200, - "latency": 65, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:56:44.2148032+08:00" - }, - { - "website_id": "188cf72edea71c4cb2284c93", - "url_id": "170fab2239ad", - "url": "https://wireguard.shumengya.top", - "status_code": 200, - "latency": 67, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:01:26.2895414+08:00" - }, - { - "website_id": "188cf72edea71c4cb2284c93", - "url_id": "170fab2239ad", - "url": "https://wireguard.shumengya.top", - "status_code": 200, - "latency": 70, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:06:22.3920123+08:00" - }, - { - "website_id": "188cf72edea71c4cb2284c93", - "url_id": "170fab2239ad", - "url": "https://wireguard.shumengya.top", - "status_code": 200, - "latency": 67, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:11:25.0358364+08:00" - } - ], - "188cf746b16a83dc4d5e3d13_822329e621c2": [ - { - "website_id": "188cf746b16a83dc4d5e3d13", - "url_id": "822329e621c2", - "url": "https://repo.docker.shumengya.top", - "status_code": 0, - "latency": 138, - "is_up": false, - "error": "Get \"https://repo.docker.shumengya.top\": dial tcp: lookup repo.docker.shumengya.top: no such host", - "checked_at": "2026-03-06T10:01:30.9263024+08:00" - }, - { - "website_id": "188cf746b16a83dc4d5e3d13", - "url_id": "822329e621c2", - "url": "https://repo.docker.shumengya.top", - "status_code": 0, - "latency": 25, - "is_up": false, - "error": "Get \"https://repo.docker.shumengya.top\": dial tcp: lookup repo.docker.shumengya.top: no such host", - "checked_at": "2026-03-06T10:06:30.3478768+08:00" - }, - { - "website_id": "188cf746b16a83dc4d5e3d13", - "url_id": "822329e621c2", - "url": "https://repo.docker.shumengya.top", - "status_code": 0, - "latency": 10, - "is_up": false, - "error": "Get \"https://repo.docker.shumengya.top\": dial tcp: lookup repo.docker.shumengya.top: no such host", - "checked_at": "2026-03-06T10:10:58.179906+08:00" - }, - { - "website_id": "188cf746b16a83dc4d5e3d13", - "url_id": "822329e621c2", - "url": "https://repo.docker.shumengya.top", - "status_code": 0, - "latency": 34, - "is_up": false, - "error": "Get \"https://repo.docker.shumengya.top\": dial tcp: lookup repo.docker.shumengya.top: no such host", - "checked_at": "2026-03-06T10:15:58.2392592+08:00" - }, - { - "website_id": "188cf746b16a83dc4d5e3d13", - "url_id": "822329e621c2", - "url": "https://repo.docker.shumengya.top", - "status_code": 0, - "latency": 26, - "is_up": false, - "error": "Get \"https://repo.docker.shumengya.top\": dial tcp: lookup repo.docker.shumengya.top: no such host", - "checked_at": "2026-03-06T10:20:58.0051352+08:00" - }, - { - "website_id": "188cf746b16a83dc4d5e3d13", - "url_id": "822329e621c2", - "url": "https://repo.docker.shumengya.top", - "status_code": 0, - "latency": 7, - "is_up": false, - "error": "Get \"https://repo.docker.shumengya.top\": dial tcp: lookup repo.docker.shumengya.top: no such host", - "checked_at": "2026-03-06T10:25:58.1435372+08:00" - }, - { - "website_id": "188cf746b16a83dc4d5e3d13", - "url_id": "822329e621c2", - "url": "https://repo.docker.shumengya.top", - "status_code": 0, - "latency": 8, - "is_up": false, - "error": "Get \"https://repo.docker.shumengya.top\": dial tcp: lookup repo.docker.shumengya.top: no such host", - "checked_at": "2026-03-06T10:30:58.075461+08:00" - }, - { - "website_id": "188cf746b16a83dc4d5e3d13", - "url_id": "822329e621c2", - "url": "https://repo.docker.shumengya.top", - "status_code": 0, - "latency": 20, - "is_up": false, - "error": "Get \"https://repo.docker.shumengya.top\": dial tcp: lookup repo.docker.shumengya.top: no such host", - "checked_at": "2026-03-06T10:35:57.9921885+08:00" - }, - { - "website_id": "188cf746b16a83dc4d5e3d13", - "url_id": "822329e621c2", - "url": "https://repo.docker.shumengya.top", - "status_code": 0, - "latency": 7, - "is_up": false, - "error": "Get \"https://repo.docker.shumengya.top\": dial tcp: lookup repo.docker.shumengya.top: no such host", - "checked_at": "2026-03-06T10:40:57.9590639+08:00" - }, - { - "website_id": "188cf746b16a83dc4d5e3d13", - "url_id": "822329e621c2", - "url": "https://repo.docker.shumengya.top", - "status_code": 0, - "latency": 12, - "is_up": false, - "error": "Get \"https://repo.docker.shumengya.top\": dial tcp: lookup repo.docker.shumengya.top: no such host", - "checked_at": "2026-03-06T10:45:58.2295509+08:00" - }, - { - "website_id": "188cf746b16a83dc4d5e3d13", - "url_id": "822329e621c2", - "url": "https://repo.docker.shumengya.top", - "status_code": 0, - "latency": 178, - "is_up": false, - "error": "Get \"https://repo.docker.shumengya.top\": dial tcp: lookup repo.docker.shumengya.top: no such host", - "checked_at": "2026-03-06T11:13:38.9758485+08:00" - }, - { - "website_id": "188cf746b16a83dc4d5e3d13", - "url_id": "822329e621c2", - "url": "https://repo.docker.shumengya.top", - "status_code": 0, - "latency": 42, - "is_up": false, - "error": "Get \"https://repo.docker.shumengya.top\": dial tcp: lookup repo.docker.shumengya.top: no such host", - "checked_at": "2026-03-06T11:21:48.3512242+08:00" - }, - { - "website_id": "188cf746b16a83dc4d5e3d13", - "url_id": "822329e621c2", - "url": "https://repo.docker.shumengya.top", - "status_code": 0, - "latency": 148, - "is_up": false, - "error": "Get \"https://repo.docker.shumengya.top\": dial tcp: lookup repo.docker.shumengya.top: no such host", - "checked_at": "2026-03-06T11:26:48.325657+08:00" - }, - { - "website_id": "188cf746b16a83dc4d5e3d13", - "url_id": "822329e621c2", - "url": "https://repo.docker.shumengya.top", - "status_code": 0, - "latency": 17, - "is_up": false, - "error": "Get \"https://repo.docker.shumengya.top\": dial tcp: lookup repo.docker.shumengya.top: no such host", - "checked_at": "2026-03-06T11:31:48.3341323+08:00" - }, - { - "website_id": "188cf746b16a83dc4d5e3d13", - "url_id": "822329e621c2", - "url": "https://repo.docker.shumengya.top", - "status_code": 0, - "latency": 285, - "is_up": false, - "error": "Get \"https://repo.docker.shumengya.top\": dial tcp: lookup repo.docker.shumengya.top: no such host", - "checked_at": "2026-03-06T11:36:48.3520905+08:00" - }, - { - "website_id": "188cf746b16a83dc4d5e3d13", - "url_id": "822329e621c2", - "url": "https://repo.docker.shumengya.top", - "status_code": 0, - "latency": 32, - "is_up": false, - "error": "Get \"https://repo.docker.shumengya.top\": dial tcp: lookup repo.docker.shumengya.top: no such host", - "checked_at": "2026-03-06T11:41:48.8480551+08:00" - }, - { - "website_id": "188cf746b16a83dc4d5e3d13", - "url_id": "822329e621c2", - "url": "https://repo.docker.shumengya.top", - "status_code": 0, - "latency": 20, - "is_up": false, - "error": "Get \"https://repo.docker.shumengya.top\": dial tcp: lookup repo.docker.shumengya.top: no such host", - "checked_at": "2026-03-06T11:46:48.3437233+08:00" - }, - { - "website_id": "188cf746b16a83dc4d5e3d13", - "url_id": "822329e621c2", - "url": "https://repo.docker.shumengya.top", - "status_code": 0, - "latency": 333, - "is_up": false, - "error": "Get \"https://repo.docker.shumengya.top\": dial tcp: lookup repo.docker.shumengya.top: no such host", - "checked_at": "2026-03-06T11:51:48.4432568+08:00" - }, - { - "website_id": "188cf746b16a83dc4d5e3d13", - "url_id": "822329e621c2", - "url": "https://repo.docker.shumengya.top", - "status_code": 0, - "latency": 31, - "is_up": false, - "error": "Get \"https://repo.docker.shumengya.top\": dial tcp: lookup repo.docker.shumengya.top: no such host", - "checked_at": "2026-03-06T11:56:48.3357056+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-03-06T12:49:34.8863658+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-03-06T12:56:57.4485457+08:00" - }, - { - "website_id": "188cf746b16a83dc4d5e3d13", - "url_id": "822329e621c2", - "url": "https://repo.docker.shumengya.top", - "status_code": 0, - "latency": 25, - "is_up": false, - "error": "Get \"https://repo.docker.shumengya.top\": dial tcp: lookup repo.docker.shumengya.top: no such host", - "checked_at": "2026-03-06T13:01:39.3818967+08:00" - }, - { - "website_id": "188cf746b16a83dc4d5e3d13", - "url_id": "822329e621c2", - "url": "https://repo.docker.shumengya.top", - "status_code": 0, - "latency": 65, - "is_up": false, - "error": "Get \"https://repo.docker.shumengya.top\": dial tcp: lookup repo.docker.shumengya.top: no such host", - "checked_at": "2026-03-06T13:06:35.8325982+08:00" - }, - { - "website_id": "188cf746b16a83dc4d5e3d13", - "url_id": "822329e621c2", - "url": "https://repo.docker.shumengya.top", - "status_code": 0, - "latency": 112, - "is_up": false, - "error": "Get \"https://repo.docker.shumengya.top\": dial tcp: lookup repo.docker.shumengya.top: no such host", - "checked_at": "2026-03-06T13:11:38.237086+08:00" - } - ], - "188cf74b2fa5da7c8e2b588e_2f580f90f979": [ - { - "website_id": "188cf74b2fa5da7c8e2b588e", - "url_id": "2f580f90f979", - "url": "https://notice.shumengya.top", - "status_code": 200, - "latency": 188, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:01:31.0773606+08:00" - }, - { - "website_id": "188cf74b2fa5da7c8e2b588e", - "url_id": "2f580f90f979", - "url": "https://notice.shumengya.top", - "status_code": 200, - "latency": 49, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:06:30.3976749+08:00" - }, - { - "website_id": "188cf74b2fa5da7c8e2b588e", - "url_id": "2f580f90f979", - "url": "https://notice.shumengya.top", - "status_code": 200, - "latency": 57, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:10:58.2268534+08:00" - }, - { - "website_id": "188cf74b2fa5da7c8e2b588e", - "url_id": "2f580f90f979", - "url": "https://notice.shumengya.top", - "status_code": 200, - "latency": 66, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:15:58.3065638+08:00" - }, - { - "website_id": "188cf74b2fa5da7c8e2b588e", - "url_id": "2f580f90f979", - "url": "https://notice.shumengya.top", - "status_code": 200, - "latency": 160, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:20:58.3678545+08:00" - }, - { - "website_id": "188cf74b2fa5da7c8e2b588e", - "url_id": "2f580f90f979", - "url": "https://notice.shumengya.top", - "status_code": 200, - "latency": 60, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:25:58.3282352+08:00" - }, - { - "website_id": "188cf74b2fa5da7c8e2b588e", - "url_id": "2f580f90f979", - "url": "https://notice.shumengya.top", - "status_code": 200, - "latency": 56, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:30:58.1301053+08:00" - }, - { - "website_id": "188cf74b2fa5da7c8e2b588e", - "url_id": "2f580f90f979", - "url": "https://notice.shumengya.top", - "status_code": 200, - "latency": 70, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:35:58.1884945+08:00" - }, - { - "website_id": "188cf74b2fa5da7c8e2b588e", - "url_id": "2f580f90f979", - "url": "https://notice.shumengya.top", - "status_code": 200, - "latency": 86, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:40:58.0728626+08:00" - }, - { - "website_id": "188cf74b2fa5da7c8e2b588e", - "url_id": "2f580f90f979", - "url": "https://notice.shumengya.top", - "status_code": 200, - "latency": 62, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:45:58.3816908+08:00" - }, - { - "website_id": "188cf74b2fa5da7c8e2b588e", - "url_id": "2f580f90f979", - "url": "https://notice.shumengya.top", - "status_code": 200, - "latency": 59, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:13:26.7933593+08:00" - }, - { - "website_id": "188cf74b2fa5da7c8e2b588e", - "url_id": "2f580f90f979", - "url": "https://notice.shumengya.top", - "status_code": 200, - "latency": 56, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:21:36.1959082+08:00" - }, - { - "website_id": "188cf74b2fa5da7c8e2b588e", - "url_id": "2f580f90f979", - "url": "https://notice.shumengya.top", - "status_code": 200, - "latency": 61, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:26:36.1391958+08:00" - }, - { - "website_id": "188cf74b2fa5da7c8e2b588e", - "url_id": "2f580f90f979", - "url": "https://notice.shumengya.top", - "status_code": 200, - "latency": 57, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:31:36.2851606+08:00" - }, - { - "website_id": "188cf74b2fa5da7c8e2b588e", - "url_id": "2f580f90f979", - "url": "https://notice.shumengya.top", - "status_code": 200, - "latency": 59, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:36:36.1109068+08:00" - }, - { - "website_id": "188cf74b2fa5da7c8e2b588e", - "url_id": "2f580f90f979", - "url": "https://notice.shumengya.top", - "status_code": 200, - "latency": 68, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:41:36.8439537+08:00" - }, - { - "website_id": "188cf74b2fa5da7c8e2b588e", - "url_id": "2f580f90f979", - "url": "https://notice.shumengya.top", - "status_code": 200, - "latency": 68, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:46:36.3530811+08:00" - }, - { - "website_id": "188cf74b2fa5da7c8e2b588e", - "url_id": "2f580f90f979", - "url": "https://notice.shumengya.top", - "status_code": 200, - "latency": 60, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:51:36.296586+08:00" - }, - { - "website_id": "188cf74b2fa5da7c8e2b588e", - "url_id": "2f580f90f979", - "url": "https://notice.shumengya.top", - "status_code": 200, - "latency": 60, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:56:36.3073882+08:00" - }, - { - "website_id": "188cf74b2fa5da7c8e2b588e", - "url_id": "2f580f90f979", - "url": "https://notice.shumengya.top", - "status_code": 200, - "latency": 747, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:49:23.5499358+08:00" - }, - { - "website_id": "188cf74b2fa5da7c8e2b588e", - "url_id": "2f580f90f979", - "url": "https://notice.shumengya.top", - "status_code": 200, - "latency": 80, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:56:58.5331107+08:00" - }, - { - "website_id": "188cf74b2fa5da7c8e2b588e", - "url_id": "2f580f90f979", - "url": "https://notice.shumengya.top", - "status_code": 200, - "latency": 80, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:01:40.4661759+08:00" - }, - { - "website_id": "188cf74b2fa5da7c8e2b588e", - "url_id": "2f580f90f979", - "url": "https://notice.shumengya.top", - "status_code": 200, - "latency": 61, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:06:36.8996853+08:00" - }, - { - "website_id": "188cf74b2fa5da7c8e2b588e", - "url_id": "2f580f90f979", - "url": "https://notice.shumengya.top", - "status_code": 200, - "latency": 66, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:11:39.3046808+08:00" - } - ], - "188cf74b2fa5da7c8e2b588e_65c5e5f59ef6": [ - { - "website_id": "188cf74b2fa5da7c8e2b588e", - "url_id": "65c5e5f59ef6", - "url": "http://monitor.shumengya.top", - "status_code": 200, - "latency": 180, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:10:58.3612917+08:00" - } - ], - "188d00db054cf7860dff3440_ac85b166ccc4": [ - { - "website_id": "188d00db054cf7860dff3440", - "url_id": "ac85b166ccc4", - "url": "https://ping.shumengya.top", - "status_code": 200, - "latency": 153, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:01:30.8836501+08:00" - }, - { - "website_id": "188d00db054cf7860dff3440", - "url_id": "ac85b166ccc4", - "url": "https://ping.shumengya.top", - "status_code": 200, - "latency": 53, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:06:30.4017999+08:00" - }, - { - "website_id": "188d00db054cf7860dff3440", - "url_id": "ac85b166ccc4", - "url": "https://ping.shumengya.top", - "status_code": 200, - "latency": 166, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:10:58.358679+08:00" - }, - { - "website_id": "188d00db054cf7860dff3440", - "url_id": "ac85b166ccc4", - "url": "https://ping.shumengya.top", - "status_code": 200, - "latency": 65, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:15:58.2418594+08:00" - }, - { - "website_id": "188d00db054cf7860dff3440", - "url_id": "ac85b166ccc4", - "url": "https://ping.shumengya.top", - "status_code": 200, - "latency": 59, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:20:58.0111047+08:00" - }, - { - "website_id": "188d00db054cf7860dff3440", - "url_id": "ac85b166ccc4", - "url": "https://ping.shumengya.top", - "status_code": 200, - "latency": 62, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:25:58.0151534+08:00" - }, - { - "website_id": "188d00db054cf7860dff3440", - "url_id": "ac85b166ccc4", - "url": "https://ping.shumengya.top", - "status_code": 200, - "latency": 145, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:30:58.1955467+08:00" - }, - { - "website_id": "188d00db054cf7860dff3440", - "url_id": "ac85b166ccc4", - "url": "https://ping.shumengya.top", - "status_code": 200, - "latency": 56, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:35:58.006384+08:00" - }, - { - "website_id": "188d00db054cf7860dff3440", - "url_id": "ac85b166ccc4", - "url": "https://ping.shumengya.top", - "status_code": 200, - "latency": 93, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:40:58.1218393+08:00" - }, - { - "website_id": "188d00db054cf7860dff3440", - "url_id": "ac85b166ccc4", - "url": "https://ping.shumengya.top", - "status_code": 200, - "latency": 89, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:45:58.039384+08:00" - }, - { - "website_id": "188d00db054cf7860dff3440", - "url_id": "ac85b166ccc4", - "url": "https://ping.shumengya.top", - "status_code": 200, - "latency": 84, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:13:25.343647+08:00" - }, - { - "website_id": "188d00db054cf7860dff3440", - "url_id": "ac85b166ccc4", - "url": "https://ping.shumengya.top", - "status_code": 200, - "latency": 66, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:21:36.2307789+08:00" - }, - { - "website_id": "188d00db054cf7860dff3440", - "url_id": "ac85b166ccc4", - "url": "https://ping.shumengya.top", - "status_code": 200, - "latency": 60, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:26:36.0781404+08:00" - }, - { - "website_id": "188d00db054cf7860dff3440", - "url_id": "ac85b166ccc4", - "url": "https://ping.shumengya.top", - "status_code": 200, - "latency": 67, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:31:36.2193011+08:00" - }, - { - "website_id": "188d00db054cf7860dff3440", - "url_id": "ac85b166ccc4", - "url": "https://ping.shumengya.top", - "status_code": 200, - "latency": 68, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:36:36.0611486+08:00" - }, - { - "website_id": "188d00db054cf7860dff3440", - "url_id": "ac85b166ccc4", - "url": "https://ping.shumengya.top", - "status_code": 200, - "latency": 67, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:41:36.6629836+08:00" - }, - { - "website_id": "188d00db054cf7860dff3440", - "url_id": "ac85b166ccc4", - "url": "https://ping.shumengya.top", - "status_code": 200, - "latency": 68, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:46:36.2156466+08:00" - }, - { - "website_id": "188d00db054cf7860dff3440", - "url_id": "ac85b166ccc4", - "url": "https://ping.shumengya.top", - "status_code": 200, - "latency": 67, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:51:36.3655273+08:00" - }, - { - "website_id": "188d00db054cf7860dff3440", - "url_id": "ac85b166ccc4", - "url": "https://ping.shumengya.top", - "status_code": 200, - "latency": 64, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:56:36.1831495+08:00" - }, - { - "website_id": "188d00db054cf7860dff3440", - "url_id": "ac85b166ccc4", - "url": "https://ping.shumengya.top", - "status_code": 200, - "latency": 626, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:49:23.4369702+08:00" - }, - { - "website_id": "188d00db054cf7860dff3440", - "url_id": "ac85b166ccc4", - "url": "https://ping.shumengya.top", - "status_code": 200, - "latency": 57, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:56:59.5973382+08:00" - }, - { - "website_id": "188d00db054cf7860dff3440", - "url_id": "ac85b166ccc4", - "url": "https://ping.shumengya.top", - "status_code": 200, - "latency": 78, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:01:41.5504432+08:00" - }, - { - "website_id": "188d00db054cf7860dff3440", - "url_id": "ac85b166ccc4", - "url": "https://ping.shumengya.top", - "status_code": 200, - "latency": 73, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:06:37.9736161+08:00" - }, - { - "website_id": "188d00db054cf7860dff3440", - "url_id": "ac85b166ccc4", - "url": "https://ping.shumengya.top", - "status_code": 200, - "latency": 81, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:11:40.3873018+08:00" - } - ], - "188d00edf571226f42ec8b20_0bf0fe1bb9cc": [ - { - "website_id": "188d00edf571226f42ec8b20", - "url_id": "0bf0fe1bb9cc", - "url": "https://ping.api.shumengya.top", - "status_code": 404, - "latency": 204, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:01:31.1528534+08:00" - }, - { - "website_id": "188d00edf571226f42ec8b20", - "url_id": "0bf0fe1bb9cc", - "url": "https://ping.api.shumengya.top", - "status_code": 404, - "latency": 57, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:06:30.3588872+08:00" - }, - { - "website_id": "188d00edf571226f42ec8b20", - "url_id": "0bf0fe1bb9cc", - "url": "https://ping.api.shumengya.top", - "status_code": 404, - "latency": 55, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:10:58.273834+08:00" - }, - { - "website_id": "188d00edf571226f42ec8b20", - "url_id": "0bf0fe1bb9cc", - "url": "https://ping.api.shumengya.top", - "status_code": 404, - "latency": 59, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:15:58.3871016+08:00" - }, - { - "website_id": "188d00edf571226f42ec8b20", - "url_id": "0bf0fe1bb9cc", - "url": "https://ping.api.shumengya.top", - "status_code": 404, - "latency": 151, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:20:58.3641271+08:00" - }, - { - "website_id": "188d00edf571226f42ec8b20", - "url_id": "0bf0fe1bb9cc", - "url": "https://ping.api.shumengya.top", - "status_code": 404, - "latency": 60, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:25:58.0129928+08:00" - }, - { - "website_id": "188d00edf571226f42ec8b20", - "url_id": "0bf0fe1bb9cc", - "url": "https://ping.api.shumengya.top", - "status_code": 404, - "latency": 63, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:30:58.2772517+08:00" - }, - { - "website_id": "188d00edf571226f42ec8b20", - "url_id": "0bf0fe1bb9cc", - "url": "https://ping.api.shumengya.top", - "status_code": 404, - "latency": 59, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:35:58.0107753+08:00" - }, - { - "website_id": "188d00edf571226f42ec8b20", - "url_id": "0bf0fe1bb9cc", - "url": "https://ping.api.shumengya.top", - "status_code": 404, - "latency": 65, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:40:58.0958937+08:00" - }, - { - "website_id": "188d00edf571226f42ec8b20", - "url_id": "0bf0fe1bb9cc", - "url": "https://ping.api.shumengya.top", - "status_code": 404, - "latency": 57, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T10:45:58.0853517+08:00" - }, - { - "website_id": "188d00edf571226f42ec8b20", - "url_id": "0bf0fe1bb9cc", - "url": "https://ping.api.shumengya.top", - "status_code": 404, - "latency": 51, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T11:13:38.3694905+08:00" - }, - { - "website_id": "188d00edf571226f42ec8b20", - "url_id": "0bf0fe1bb9cc", - "url": "https://ping.api.shumengya.top", - "status_code": 404, - "latency": 48, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T11:21:48.4006815+08:00" - }, - { - "website_id": "188d00edf571226f42ec8b20", - "url_id": "0bf0fe1bb9cc", - "url": "https://ping.api.shumengya.top", - "status_code": 404, - "latency": 71, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T11:27:00.6074678+08:00" - }, - { - "website_id": "188d00edf571226f42ec8b20", - "url_id": "0bf0fe1bb9cc", - "url": "https://ping.api.shumengya.top", - "status_code": 404, - "latency": 59, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T11:31:48.5340924+08:00" - }, - { - "website_id": "188d00edf571226f42ec8b20", - "url_id": "0bf0fe1bb9cc", - "url": "https://ping.api.shumengya.top", - "status_code": 404, - "latency": 53, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T11:36:48.6295341+08:00" - }, - { - "website_id": "188d00edf571226f42ec8b20", - "url_id": "0bf0fe1bb9cc", - "url": "https://ping.api.shumengya.top", - "status_code": 404, - "latency": 53, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T11:41:50.0731159+08:00" - }, - { - "website_id": "188d00edf571226f42ec8b20", - "url_id": "0bf0fe1bb9cc", - "url": "https://ping.api.shumengya.top", - "status_code": 404, - "latency": 52, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T11:46:49.0508201+08:00" - }, - { - "website_id": "188d00edf571226f42ec8b20", - "url_id": "0bf0fe1bb9cc", - "url": "https://ping.api.shumengya.top", - "status_code": 404, - "latency": 54, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T11:51:48.6138888+08:00" - }, - { - "website_id": "188d00edf571226f42ec8b20", - "url_id": "0bf0fe1bb9cc", - "url": "https://ping.api.shumengya.top", - "status_code": 404, - "latency": 46, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T11:56:48.8645376+08:00" - }, - { - "website_id": "188d00edf571226f42ec8b20", - "url_id": "0bf0fe1bb9cc", - "url": "https://ping.api.shumengya.top", - "status_code": 404, - "latency": 54, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T12:49:35.7371329+08:00" - }, - { - "website_id": "188d00edf571226f42ec8b20", - "url_id": "0bf0fe1bb9cc", - "url": "https://ping.api.shumengya.top", - "status_code": 404, - "latency": 55, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T12:57:12.8532476+08:00" - }, - { - "website_id": "188d00edf571226f42ec8b20", - "url_id": "0bf0fe1bb9cc", - "url": "https://ping.api.shumengya.top", - "status_code": 404, - "latency": 69, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T13:01:54.8337618+08:00" - }, - { - "website_id": "188d00edf571226f42ec8b20", - "url_id": "0bf0fe1bb9cc", - "url": "https://ping.api.shumengya.top", - "status_code": 404, - "latency": 52, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T13:06:51.2395225+08:00" - }, - { - "website_id": "188d00edf571226f42ec8b20", - "url_id": "0bf0fe1bb9cc", - "url": "https://ping.api.shumengya.top", - "status_code": 404, - "latency": 76, - "is_up": false, - "error": "", - "checked_at": "2026-03-06T13:11:53.6624053+08:00" - } - ], - "188e7f1a40cdc7bc3bba1817_9af024b3d387": [ - { - "website_id": "188e7f1a40cdc7bc3bba1817", - "url_id": "9af024b3d387", - "url": "https://cf-dns.smyhub.com/", - "status_code": 200, - "latency": 974, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:01:32.0557451+08:00" - }, - { - "website_id": "188e7f1a40cdc7bc3bba1817", - "url_id": "9af024b3d387", - "url": "https://cf-dns.smyhub.com/", - "status_code": 200, - "latency": 225, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:06:30.7255993+08:00" - }, - { - "website_id": "188e7f1a40cdc7bc3bba1817", - "url_id": "9af024b3d387", - "url": "https://cf-dns.smyhub.com/", - "status_code": 200, - "latency": 837, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:10:59.0707769+08:00" - }, - { - "website_id": "188e7f1a40cdc7bc3bba1817", - "url_id": "9af024b3d387", - "url": "https://cf-dns.smyhub.com/", - "status_code": 200, - "latency": 280, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:15:58.6014934+08:00" - }, - { - "website_id": "188e7f1a40cdc7bc3bba1817", - "url_id": "9af024b3d387", - "url": "https://cf-dns.smyhub.com/", - "status_code": 200, - "latency": 286, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:20:58.6202369+08:00" - }, - { - "website_id": "188e7f1a40cdc7bc3bba1817", - "url_id": "9af024b3d387", - "url": "https://cf-dns.smyhub.com/", - "status_code": 200, - "latency": 277, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:25:58.2413215+08:00" - }, - { - "website_id": "188e7f1a40cdc7bc3bba1817", - "url_id": "9af024b3d387", - "url": "https://cf-dns.smyhub.com/", - "status_code": 200, - "latency": 280, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:30:58.4928934+08:00" - }, - { - "website_id": "188e7f1a40cdc7bc3bba1817", - "url_id": "9af024b3d387", - "url": "https://cf-dns.smyhub.com/", - "status_code": 200, - "latency": 283, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:35:58.3005104+08:00" - }, - { - "website_id": "188e7f1a40cdc7bc3bba1817", - "url_id": "9af024b3d387", - "url": "https://cf-dns.smyhub.com/", - "status_code": 200, - "latency": 280, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:40:58.3511761+08:00" - }, - { - "website_id": "188e7f1a40cdc7bc3bba1817", - "url_id": "9af024b3d387", - "url": "https://cf-dns.smyhub.com/", - "status_code": 200, - "latency": 280, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:45:58.2370752+08:00" - }, - { - "website_id": "188e7f1a40cdc7bc3bba1817", - "url_id": "9af024b3d387", - "url": "https://cf-dns.smyhub.com/", - "status_code": 200, - "latency": 857, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:13:26.543312+08:00" - }, - { - "website_id": "188e7f1a40cdc7bc3bba1817", - "url_id": "9af024b3d387", - "url": "https://cf-dns.smyhub.com/", - "status_code": 200, - "latency": 758, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:21:36.955048+08:00" - }, - { - "website_id": "188e7f1a40cdc7bc3bba1817", - "url_id": "9af024b3d387", - "url": "https://cf-dns.smyhub.com/", - "status_code": 200, - "latency": 761, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:26:49.0941182+08:00" - }, - { - "website_id": "188e7f1a40cdc7bc3bba1817", - "url_id": "9af024b3d387", - "url": "https://cf-dns.smyhub.com/", - "status_code": 200, - "latency": 842, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:31:37.1335058+08:00" - }, - { - "website_id": "188e7f1a40cdc7bc3bba1817", - "url_id": "9af024b3d387", - "url": "https://cf-dns.smyhub.com/", - "status_code": 200, - "latency": 662, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:36:36.9972056+08:00" - }, - { - "website_id": "188e7f1a40cdc7bc3bba1817", - "url_id": "9af024b3d387", - "url": "https://cf-dns.smyhub.com/", - "status_code": 200, - "latency": 659, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:41:37.8285255+08:00" - }, - { - "website_id": "188e7f1a40cdc7bc3bba1817", - "url_id": "9af024b3d387", - "url": "https://cf-dns.smyhub.com/", - "status_code": 200, - "latency": 658, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:46:37.4285252+08:00" - }, - { - "website_id": "188e7f1a40cdc7bc3bba1817", - "url_id": "9af024b3d387", - "url": "https://cf-dns.smyhub.com/", - "status_code": 200, - "latency": 768, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:51:36.9587124+08:00" - }, - { - "website_id": "188e7f1a40cdc7bc3bba1817", - "url_id": "9af024b3d387", - "url": "https://cf-dns.smyhub.com/", - "status_code": 200, - "latency": 748, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:56:37.1902037+08:00" - }, - { - "website_id": "188e7f1a40cdc7bc3bba1817", - "url_id": "9af024b3d387", - "url": "https://cf-dns.smyhub.com/", - "status_code": 200, - "latency": 1397, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:49:24.2604395+08:00" - }, - { - "website_id": "188e7f1a40cdc7bc3bba1817", - "url_id": "9af024b3d387", - "url": "https://cf-dns.smyhub.com/", - "status_code": 200, - "latency": 2752, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:57:16.6112109+08:00" - }, - { - "website_id": "188e7f1a40cdc7bc3bba1817", - "url_id": "9af024b3d387", - "url": "https://cf-dns.smyhub.com/", - "status_code": 200, - "latency": 703, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:01:56.5386706+08:00" - }, - { - "website_id": "188e7f1a40cdc7bc3bba1817", - "url_id": "9af024b3d387", - "url": "https://cf-dns.smyhub.com/", - "status_code": 200, - "latency": 8240, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:07:00.912106+08:00" - }, - { - "website_id": "188e7f1a40cdc7bc3bba1817", - "url_id": "9af024b3d387", - "url": "https://cf-dns.smyhub.com/", - "status_code": 200, - "latency": 2529, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:11:58.8926642+08:00" - } - ], - "188e7f27a909fe247a2fcd15_b897ef0def67": [ - { - "website_id": "188e7f27a909fe247a2fcd15", - "url_id": "b897ef0def67", - "url": "https://gh-proxy.smyhub.com/", - "status_code": 200, - "latency": 909, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:01:31.8361517+08:00" - }, - { - "website_id": "188e7f27a909fe247a2fcd15", - "url_id": "b897ef0def67", - "url": "https://gh-proxy.smyhub.com/", - "status_code": 200, - "latency": 268, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:06:30.7386385+08:00" - }, - { - "website_id": "188e7f27a909fe247a2fcd15", - "url_id": "b897ef0def67", - "url": "https://gh-proxy.smyhub.com/", - "status_code": 200, - "latency": 775, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:10:59.0506247+08:00" - }, - { - "website_id": "188e7f27a909fe247a2fcd15", - "url_id": "b897ef0def67", - "url": "https://gh-proxy.smyhub.com/", - "status_code": 200, - "latency": 374, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:15:58.7117093+08:00" - }, - { - "website_id": "188e7f27a909fe247a2fcd15", - "url_id": "b897ef0def67", - "url": "https://gh-proxy.smyhub.com/", - "status_code": 200, - "latency": 258, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:20:58.4709832+08:00" - }, - { - "website_id": "188e7f27a909fe247a2fcd15", - "url_id": "b897ef0def67", - "url": "https://gh-proxy.smyhub.com/", - "status_code": 200, - "latency": 263, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:25:58.2181145+08:00" - }, - { - "website_id": "188e7f27a909fe247a2fcd15", - "url_id": "b897ef0def67", - "url": "https://gh-proxy.smyhub.com/", - "status_code": 200, - "latency": 259, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:30:58.474972+08:00" - }, - { - "website_id": "188e7f27a909fe247a2fcd15", - "url_id": "b897ef0def67", - "url": "https://gh-proxy.smyhub.com/", - "status_code": 200, - "latency": 262, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:35:58.2711774+08:00" - }, - { - "website_id": "188e7f27a909fe247a2fcd15", - "url_id": "b897ef0def67", - "url": "https://gh-proxy.smyhub.com/", - "status_code": 200, - "latency": 256, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:40:58.3321555+08:00" - }, - { - "website_id": "188e7f27a909fe247a2fcd15", - "url_id": "b897ef0def67", - "url": "https://gh-proxy.smyhub.com/", - "status_code": 200, - "latency": 260, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T10:45:58.2544344+08:00" - }, - { - "website_id": "188e7f27a909fe247a2fcd15", - "url_id": "b897ef0def67", - "url": "https://gh-proxy.smyhub.com/", - "status_code": 200, - "latency": 852, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:13:27.5955457+08:00" - }, - { - "website_id": "188e7f27a909fe247a2fcd15", - "url_id": "b897ef0def67", - "url": "https://gh-proxy.smyhub.com/", - "status_code": 200, - "latency": 809, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:21:37.0412795+08:00" - }, - { - "website_id": "188e7f27a909fe247a2fcd15", - "url_id": "b897ef0def67", - "url": "https://gh-proxy.smyhub.com/", - "status_code": 200, - "latency": 798, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:24:05.365268+08:00" - }, - { - "website_id": "188e7f27a909fe247a2fcd15", - "url_id": "b897ef0def67", - "url": "https://gh-proxy.smyhub.com/", - "status_code": 200, - "latency": 843, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:26:49.2404181+08:00" - }, - { - "website_id": "188e7f27a909fe247a2fcd15", - "url_id": "b897ef0def67", - "url": "https://gh-proxy.smyhub.com/", - "status_code": 200, - "latency": 790, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:31:37.8550234+08:00" - }, - { - "website_id": "188e7f27a909fe247a2fcd15", - "url_id": "b897ef0def67", - "url": "https://gh-proxy.smyhub.com/", - "status_code": 200, - "latency": 796, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:36:37.7951793+08:00" - }, - { - "website_id": "188e7f27a909fe247a2fcd15", - "url_id": "b897ef0def67", - "url": "https://gh-proxy.smyhub.com/", - "status_code": 200, - "latency": 895, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:41:49.0061678+08:00" - }, - { - "website_id": "188e7f27a909fe247a2fcd15", - "url_id": "b897ef0def67", - "url": "https://gh-proxy.smyhub.com/", - "status_code": 200, - "latency": 756, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:46:37.6782404+08:00" - }, - { - "website_id": "188e7f27a909fe247a2fcd15", - "url_id": "b897ef0def67", - "url": "https://gh-proxy.smyhub.com/", - "status_code": 200, - "latency": 772, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:51:48.847248+08:00" - }, - { - "website_id": "188e7f27a909fe247a2fcd15", - "url_id": "b897ef0def67", - "url": "https://gh-proxy.smyhub.com/", - "status_code": 200, - "latency": 769, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T11:56:37.487114+08:00" - }, - { - "website_id": "188e7f27a909fe247a2fcd15", - "url_id": "b897ef0def67", - "url": "https://gh-proxy.smyhub.com/", - "status_code": 200, - "latency": 1499, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:49:24.9643486+08:00" - }, - { - "website_id": "188e7f27a909fe247a2fcd15", - "url_id": "b897ef0def67", - "url": "https://gh-proxy.smyhub.com/", - "status_code": 200, - "latency": 902, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T12:57:18.5183821+08:00" - }, - { - "website_id": "188e7f27a909fe247a2fcd15", - "url_id": "b897ef0def67", - "url": "https://gh-proxy.smyhub.com/", - "status_code": 200, - "latency": 765, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:01:58.3069549+08:00" - }, - { - "website_id": "188e7f27a909fe247a2fcd15", - "url_id": "b897ef0def67", - "url": "https://gh-proxy.smyhub.com/", - "status_code": 200, - "latency": 2109, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:07:04.0301051+08:00" - }, - { - "website_id": "188e7f27a909fe247a2fcd15", - "url_id": "b897ef0def67", - "url": "https://gh-proxy.smyhub.com/", - "status_code": 200, - "latency": 793, - "is_up": true, - "error": "", - "checked_at": "2026-03-06T13:12:01.6812289+08:00" - } - ], - "188e7f33f087667c29da05f2_75a0cb223c2d": [ - { - "website_id": "188e7f33f087667c29da05f2", - "url_id": "75a0cb223c2d", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 0, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T10:01:31.1533812+08:00" - }, - { - "website_id": "188e7f33f087667c29da05f2", - "url_id": "75a0cb223c2d", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 0, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T10:06:30.610074+08:00" - }, - { - "website_id": "188e7f33f087667c29da05f2", - "url_id": "75a0cb223c2d", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 0, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T10:10:58.3592783+08:00" - }, - { - "website_id": "188e7f33f087667c29da05f2", - "url_id": "75a0cb223c2d", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 0, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T10:15:58.3529717+08:00" - }, - { - "website_id": "188e7f33f087667c29da05f2", - "url_id": "75a0cb223c2d", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 0, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T10:20:58.3526923+08:00" - }, - { - "website_id": "188e7f33f087667c29da05f2", - "url_id": "75a0cb223c2d", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 0, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T10:25:58.1362589+08:00" - }, - { - "website_id": "188e7f33f087667c29da05f2", - "url_id": "75a0cb223c2d", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 0, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T10:30:58.2293624+08:00" - }, - { - "website_id": "188e7f33f087667c29da05f2", - "url_id": "75a0cb223c2d", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 0, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T10:35:58.014182+08:00" - }, - { - "website_id": "188e7f33f087667c29da05f2", - "url_id": "75a0cb223c2d", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 0, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T10:40:58.0965098+08:00" - }, - { - "website_id": "188e7f33f087667c29da05f2", - "url_id": "75a0cb223c2d", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 0, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T10:45:58.254972+08:00" - }, - { - "website_id": "188e7f33f087667c29da05f2", - "url_id": "75a0cb223c2d", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 147, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T11:13:49.7240624+08:00" - }, - { - "website_id": "188e7f33f087667c29da05f2", - "url_id": "75a0cb223c2d", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 0, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T11:21:48.9630317+08:00" - }, - { - "website_id": "188e7f33f087667c29da05f2", - "url_id": "75a0cb223c2d", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 23, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T11:27:00.5729713+08:00" - }, - { - "website_id": "188e7f33f087667c29da05f2", - "url_id": "75a0cb223c2d", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 0, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T11:31:49.4699542+08:00" - }, - { - "website_id": "188e7f33f087667c29da05f2", - "url_id": "75a0cb223c2d", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 93, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T11:37:00.5173573+08:00" - }, - { - "website_id": "188e7f33f087667c29da05f2", - "url_id": "75a0cb223c2d", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 39, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T11:42:00.305662+08:00" - }, - { - "website_id": "188e7f33f087667c29da05f2", - "url_id": "75a0cb223c2d", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 0, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T11:46:49.8119253+08:00" - }, - { - "website_id": "188e7f33f087667c29da05f2", - "url_id": "75a0cb223c2d", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 0, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T11:51:50.8290435+08:00" - }, - { - "website_id": "188e7f33f087667c29da05f2", - "url_id": "75a0cb223c2d", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 0, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T11:56:49.6557761+08:00" - }, - { - "website_id": "188e7f33f087667c29da05f2", - "url_id": "75a0cb223c2d", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 0, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T12:49:35.5870644+08:00" - }, - { - "website_id": "188e7f33f087667c29da05f2", - "url_id": "75a0cb223c2d", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 40, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T12:57:31.6484632+08:00" - }, - { - "website_id": "188e7f33f087667c29da05f2", - "url_id": "75a0cb223c2d", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 60, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T13:02:11.4448246+08:00" - }, - { - "website_id": "188e7f33f087667c29da05f2", - "url_id": "75a0cb223c2d", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 206, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T13:07:17.3892239+08:00" - }, - { - "website_id": "188e7f33f087667c29da05f2", - "url_id": "75a0cb223c2d", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 178, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T13:12:15.0969822+08:00" - } - ], - "188e7f4cdd13a3bc80c68184_9f11daaedba4": [ - { - "website_id": "188e7f4cdd13a3bc80c68184", - "url_id": "9f11daaedba4", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 159, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T10:01:30.2977632+08:00" - }, - { - "website_id": "188e7f4cdd13a3bc80c68184", - "url_id": "9f11daaedba4", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 12, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T10:06:30.1506206+08:00" - }, - { - "website_id": "188e7f4cdd13a3bc80c68184", - "url_id": "9f11daaedba4", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 45, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T10:10:57.9948055+08:00" - }, - { - "website_id": "188e7f4cdd13a3bc80c68184", - "url_id": "9f11daaedba4", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 29, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T10:15:57.9793634+08:00" - }, - { - "website_id": "188e7f4cdd13a3bc80c68184", - "url_id": "9f11daaedba4", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 28, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T10:20:57.9790697+08:00" - }, - { - "website_id": "188e7f4cdd13a3bc80c68184", - "url_id": "9f11daaedba4", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 47, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T10:25:57.9998716+08:00" - }, - { - "website_id": "188e7f4cdd13a3bc80c68184", - "url_id": "9f11daaedba4", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 35, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T10:30:57.9847992+08:00" - }, - { - "website_id": "188e7f4cdd13a3bc80c68184", - "url_id": "9f11daaedba4", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 22, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T10:35:57.9720902+08:00" - }, - { - "website_id": "188e7f4cdd13a3bc80c68184", - "url_id": "9f11daaedba4", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 33, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T10:40:57.9849618+08:00" - }, - { - "website_id": "188e7f4cdd13a3bc80c68184", - "url_id": "9f11daaedba4", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 40, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T10:45:57.9900091+08:00" - }, - { - "website_id": "188e7f4cdd13a3bc80c68184", - "url_id": "9f11daaedba4", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 124, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T11:13:37.4870994+08:00" - }, - { - "website_id": "188e7f4cdd13a3bc80c68184", - "url_id": "9f11daaedba4", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 72, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T11:21:48.0911005+08:00" - }, - { - "website_id": "188e7f4cdd13a3bc80c68184", - "url_id": "9f11daaedba4", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 148, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T11:26:48.325657+08:00" - }, - { - "website_id": "188e7f4cdd13a3bc80c68184", - "url_id": "9f11daaedba4", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 45, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T11:31:48.1156444+08:00" - }, - { - "website_id": "188e7f4cdd13a3bc80c68184", - "url_id": "9f11daaedba4", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 286, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T11:36:48.3520905+08:00" - }, - { - "website_id": "188e7f4cdd13a3bc80c68184", - "url_id": "9f11daaedba4", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 65, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T11:41:48.1101991+08:00" - }, - { - "website_id": "188e7f4cdd13a3bc80c68184", - "url_id": "9f11daaedba4", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 49, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T11:46:48.0622693+08:00" - }, - { - "website_id": "188e7f4cdd13a3bc80c68184", - "url_id": "9f11daaedba4", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 80, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T11:51:48.0693242+08:00" - }, - { - "website_id": "188e7f4cdd13a3bc80c68184", - "url_id": "9f11daaedba4", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 39, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T11:56:48.045534+08:00" - }, - { - "website_id": "188e7f4cdd13a3bc80c68184", - "url_id": "9f11daaedba4", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 16, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T12:49:34.8643449+08:00" - }, - { - "website_id": "188e7f4cdd13a3bc80c68184", - "url_id": "9f11daaedba4", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 35, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T12:57:44.7503187+08:00" - }, - { - "website_id": "188e7f4cdd13a3bc80c68184", - "url_id": "9f11daaedba4", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 30, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T13:02:24.5141131+08:00" - }, - { - "website_id": "188e7f4cdd13a3bc80c68184", - "url_id": "9f11daaedba4", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 119, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T13:07:30.7785204+08:00" - }, - { - "website_id": "188e7f4cdd13a3bc80c68184", - "url_id": "9f11daaedba4", - "url": "https://floppy-bird.smyhub.com/", - "status_code": 0, - "latency": 306, - "is_up": false, - "error": "Get \"https://floppy-bird.smyhub.com/\": dial tcp: lookup floppy-bird.smyhub.com: no such host", - "checked_at": "2026-03-06T13:12:28.4734949+08:00" - } - ] -} \ No newline at end of file diff --git a/mengyaping-backend/data/websites.json b/mengyaping-backend/data/websites.json deleted file mode 100644 index 35b713a..0000000 --- a/mengyaping-backend/data/websites.json +++ /dev/null @@ -1,821 +0,0 @@ -[ - { - "id": "188cb48c619bd5d896bff546", - "name": "萌芽主页", - "groups": [ - "self-made" - ], - "urls": [ - { - "id": "d19b0a475334", - "url": "https://shumengya.top", - "remark": "" - } - ], - "ip_addresses": [ - "47.108.228.23" - ], - "favicon": "https://shumengya.top/favicon.ico", - "title": "萌芽主页", - "created_at": "2026-01-21T17:21:08.6757862+08:00", - "updated_at": "2026-03-06T13:12:33.6663492+08:00" - }, - { - "id": "188cb49298d72b78a37301a1", - "name": "萌芽备忘录", - "groups": [ - "self-deploy" - ], - "urls": [ - { - "id": "9d25ae024f36", - "url": "https://memos.shumengya.top", - "remark": "" - } - ], - "ip_addresses": [ - "47.108.228.23" - ], - "favicon": "https://memos.shumengya.top/logo.webp", - "title": "Memos", - "created_at": "2026-01-21T17:21:35.3722254+08:00", - "updated_at": "2026-03-06T13:12:34.4657931+08:00" - }, - { - "id": "188cb52c04cf639cfc2c8ba4", - "name": "萌芽盘", - "groups": [ - "self-deploy" - ], - "urls": [ - { - "id": "cfe6229fb86b", - "url": "https://pan.shumengya.top", - "remark": "" - } - ], - "ip_addresses": [ - "47.108.228.23" - ], - "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-03-06T13:12:35.6184204+08:00" - }, - { - "id": "188cb53623f46b7872bf17b6", - "name": "萌芽Git仓库", - "groups": [ - "self-deploy" - ], - "urls": [ - { - "id": "b53594e7285d", - "url": "https://git.shumengya.top", - "remark": "" - } - ], - "ip_addresses": [ - "47.108.228.23" - ], - "favicon": "https://git.shumengya.top/assets/img/favicon.svg", - "title": "萌芽Git仓库", - "created_at": "2026-01-21T17:33:17.7858446+08:00", - "updated_at": "2026-03-06T13:12:36.4115003+08:00" - }, - { - "id": "188cb54b93ddd1e487bf347e", - "name": "萌芽图床", - "groups": [ - "self-deploy" - ], - "urls": [ - { - "id": "46d822694242", - "url": "https://image.shumengya.top", - "remark": "" - } - ], - "ip_addresses": [ - "47.108.228.23" - ], - "favicon": "https://image.shumengya.top/favicon.ico", - "title": "萌芽图床", - "created_at": "2026-01-21T17:34:49.8577249+08:00", - "updated_at": "2026-03-06T13:12:38.40321+08:00" - }, - { - "id": "188cb5507dcd2da0559bd6dd", - "name": "萌芽笔记", - "groups": [ - "self-made" - ], - "urls": [ - { - "id": "6d5125db9d81", - "url": "https://note.shumengya.top", - "remark": "" - } - ], - "ip_addresses": [ - "47.108.228.23" - ], - "favicon": "https://note.shumengya.top/logo.png", - "title": "萌芽笔记", - "created_at": "2026-01-21T17:35:10.962372+08:00", - "updated_at": "2026-03-06T13:12:40.1738642+08:00" - }, - { - "id": "188cb55693193b80d2383cc3", - "name": "萌芽作品集", - "groups": [ - "self-made" - ], - "urls": [ - { - "id": "74a3b6e1f50e", - "url": "https://work.shumengya.top", - "remark": "" - } - ], - "ip_addresses": [ - "47.108.228.23" - ], - "favicon": "https://work.shumengya.top/favicon.ico", - "title": "502 Bad Gateway", - "created_at": "2026-01-21T17:35:37.0894816+08:00", - "updated_at": "2026-03-06T13:12:40.976149+08:00" - }, - { - "id": "188cb56d8df57dc8c66d42ee", - "name": "万象口袋", - "groups": [ - "self-made" - ], - "urls": [ - { - "id": "e8f8fd4cf9b9", - "url": "https://infogenie.shumengya.top", - "remark": "" - } - ], - "ip_addresses": [ - "47.108.228.23" - ], - "favicon": "https://infogenie.shumengya.top/assets/logo.png", - "title": "万象口袋", - "created_at": "2026-01-21T17:37:15.787501+08:00", - "updated_at": "2026-03-06T13:12:42.7325255+08:00" - }, - { - "id": "188cb574b122f71087e53ae1", - "name": "萌芽问卷", - "groups": [ - "self-deploy" - ], - "urls": [ - { - "id": "2f9ffdedd860", - "url": "https://survey.shumengya.top", - "remark": "" - } - ], - "ip_addresses": [ - "47.108.228.23" - ], - "favicon": "https://survey.shumengya.top/favicon.ico", - "title": "502 Bad Gateway", - "created_at": "2026-01-21T17:37:46.4424548+08:00", - "updated_at": "2026-03-06T13:12:44.9725726+08:00" - }, - { - "id": "188cb5a19b836e80d93df86a", - "name": "萌芽短链", - "groups": [ - "self-made" - ], - "urls": [ - { - "id": "8a4ebcb7eb03", - "url": "https://short.shumengya.top", - "remark": "" - } - ], - "ip_addresses": [ - "47.108.228.23" - ], - "favicon": "https://short.shumengya.top/logo.png", - "title": "萌芽短链", - "created_at": "2026-01-21T17:40:59.3532064+08:00", - "updated_at": "2026-03-06T13:12:45.7693627+08:00" - }, - { - "id": "188cb6da20586d34df88b086", - "name": "萌芽漂流瓶", - "groups": [ - "self-made" - ], - "urls": [ - { - "id": "e7e250eb8985", - "url": "https://bottle.shumengya.top", - "remark": "" - } - ], - "ip_addresses": [ - "47.108.228.23" - ], - "favicon": "https://bottle.shumengya.top/logo.png", - "title": "萌芽漂流瓶(´,,•ω•,,)♡", - "created_at": "2026-01-21T18:03:21.6115541+08:00", - "updated_at": "2026-03-06T13:12:46.5656927+08:00" - }, - { - "id": "188cb6ff716e2590408704ee", - "name": "编程速查表[CloudFlare]", - "groups": [ - "self-deploy" - ], - "urls": [ - { - "id": "85620784f1a3", - "url": "https://reference.smyhub.com", - "remark": "" - } - ], - "ip_addresses": [ - "188.114.96.0", - "188.114.97.0", - "2a06:98c1:3121::", - "2a06:98c1:3120::" - ], - "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-03-06T13:12:48.1727895+08:00" - }, - { - "id": "188cb708e8cf52a49a436570", - "name": "思绪思维导图[CloudFlare]", - "groups": [ - "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]", - "groups": [ - "self-deploy" - ], - "urls": [ - { - "id": "f278e65dd498", - "url": "https://it-tools.smyhub.com", - "remark": "" - } - ], - "ip_addresses": [ - "188.114.97.0", - "188.114.96.0", - "2a06:98c1:3121::", - "2a06:98c1:3120::" - ], - "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-03-06T13:12:49.7591315+08:00" - }, - { - "id": "188cb71b527c48bc3c344acc", - "name": "xtools工具集[CloudFlare]", - "groups": [ - "self-deploy" - ], - "urls": [ - { - "id": "13cdc2afab17", - "url": "https://xtools.smyhub.com", - "remark": "" - } - ], - "ip_addresses": [ - "188.114.96.0", - "188.114.97.0", - "2a06:98c1:3121::", - "2a06:98c1:3120::" - ], - "favicon": "https://xtools.smyhub.com/favicon.ico", - "title": "百川云常用工具", - "created_at": "2026-01-21T18:08:01.6256391+08:00", - "updated_at": "2026-03-06T13:12:52.8447254+08:00" - }, - { - "id": "188cb72555e8d5c4f0d7aa98", - "name": "萌芽监控面板", - "groups": [ - "admin" - ], - "urls": [ - { - "id": "421fe08ece5e", - "url": "http://monitor.shumengya.top", - "remark": "" - } - ], - "ip_addresses": [ - "47.108.228.23" - ], - "favicon": "http://monitor.shumengya.top/logo.png", - "title": "萌芽监控面板", - "created_at": "2026-01-21T18:08:44.6327577+08:00", - "updated_at": "2026-03-06T13:12:53.6453208+08:00" - }, - { - "id": "188cb73272be1c9c5a5d6812", - "name": "在线office", - "groups": [ - "self-deploy" - ], - "urls": [ - { - "id": "cba98c3c329b", - "url": "https://office.shumengya.top", - "remark": "" - } - ], - "ip_addresses": [ - "47.108.228.23" - ], - "favicon": "https://office.shumengya.top/favicon.ico", - "title": "404 Not Found", - "created_at": "2026-01-21T18:09:40.9510719+08:00", - "updated_at": "2026-03-06T13:12:54.436477+08:00" - }, - { - "id": "188cb7372655f3841e82a210", - "name": "网页魔方[CloudFlare]", - "groups": [ - "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集合", - "groups": [ - "api" - ], - "urls": [ - { - "id": "2f0596587997", - "url": "https://60s.api.shumengya.top", - "remark": "" - } - ], - "ip_addresses": [ - "47.108.228.23" - ], - "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-03-06T13:12:56.3727134+08:00" - }, - { - "id": "188cb77f231fe68c73e24b29", - "name": "萌芽主页后端API", - "groups": [ - "api" - ], - "urls": [ - { - "id": "c182709178d9", - "url": "https://nav.api.shumengya.top", - "remark": "" - } - ], - "ip_addresses": [ - "47.108.228.23" - ], - "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-03-06T13:12:57.5163074+08:00" - }, - { - "id": "188cb789a76896788f139a96", - "name": "萌芽笔记后端API", - "groups": [ - "api" - ], - "urls": [ - { - "id": "2b42ed612dd3", - "url": "https://note.api.shumengya.top/api/tree", - "remark": "" - } - ], - "ip_addresses": [ - "47.108.228.23" - ], - "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-03-06T13:12:58.307067+08:00" - }, - { - "id": "188cb7934d5d413890dea61f", - "name": "萌芽作品集后端API", - "groups": [ - "api" - ], - "urls": [ - { - "id": "d841646c7b41", - "url": "https://work.api.shumengya.top", - "remark": "" - } - ], - "ip_addresses": [ - "47.108.228.23" - ], - "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-03-06T13:12:59.0980222+08:00" - }, - { - "id": "188cb79ed2aeb85cabcf6e66", - "name": "万象口袋后端API", - "groups": [ - "api" - ], - "urls": [ - { - "id": "7c8ff0e48be4", - "url": "https://infogenie.api.shumengya.top", - "remark": "" - } - ], - "ip_addresses": [ - "47.108.228.23" - ], - "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-03-06T13:12:59.8921428+08:00" - }, - { - "id": "188cb7b7b154ddbc1dc4b14d", - "name": "萌芽短链后端API", - "groups": [ - "api" - ], - "urls": [ - { - "id": "ef0443476dd8", - "url": "https://short.api.shumengya.top", - "remark": "" - } - ], - "ip_addresses": [ - "47.108.228.23" - ], - "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-03-06T13:13:02.3005616+08:00" - }, - { - "id": "188cb7c8586dd2c4dcf4bd1e", - "name": "萌芽漂流瓶后端API", - "groups": [ - "api" - ], - "urls": [ - { - "id": "432740a25595", - "url": "https://bottle.api.shumengya.top", - "remark": "" - } - ], - "ip_addresses": [ - "47.108.228.23" - ], - "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-03-06T13:13:03.0940218+08:00" - }, - { - "id": "188cf6ed769b75e497cd2a4a", - "name": "大萌芽1Panel面板[WG]", - "groups": [ - "admin" - ], - "urls": [ - { - "id": "a3e3ff7839f1", - "url": "http://10.0.0.233:19132/smy", - "remark": "" - } - ], - "ip_addresses": [ - "10.0.0.233" - ], - "favicon": "http://10.0.0.233:19132/favicon.ico", - "title": "loading...", - "created_at": "2026-01-22T13:37:33.4073441+08:00", - "updated_at": "2026-03-06T13:13:03.5972427+08:00" - }, - { - "id": "188cf6f322f76b90c35c3484", - "name": "小萌芽1Panel面板[WG]", - "groups": [ - "admin" - ], - "urls": [ - { - "id": "76b6f9fe4f04", - "url": "http://10.0.0.100:19132/smy", - "remark": "" - } - ], - "ip_addresses": [ - "10.0.0.100" - ], - "favicon": "http://10.0.0.100:19132/favicon.ico", - "title": "loading...", - "created_at": "2026-01-22T13:37:57.7738884+08:00", - "updated_at": "2026-03-06T13:13:04.1047503+08:00" - }, - { - "id": "188cf7019e56e618827f4927", - "name": "大萌芽Portaintor面板[WG]", - "groups": [ - "admin" - ], - "urls": [ - { - "id": "093d47789bb5", - "url": "http://10.0.0.233:8484/", - "remark": "" - } - ], - "ip_addresses": [ - "10.0.0.233" - ], - "favicon": "http://10.0.0.233:8484/dd5d4c0b208895c5a7de.png", - "title": "Portainer", - "created_at": "2026-01-22T13:38:59.9732854+08:00", - "updated_at": "2026-03-06T13:13:04.6105552+08:00" - }, - { - "id": "188cf70700922fa4041f6470", - "name": "小萌芽Portaintor面板[WG]", - "groups": [ - "admin" - ], - "urls": [ - { - "id": "777c292c7696", - "url": "http://10.0.0.100:8484/", - "remark": "" - } - ], - "ip_addresses": [ - "10.0.0.100" - ], - "favicon": "http://10.0.0.100:8484/dd5d4c0b208895c5a7de.png", - "title": "Portainer", - "created_at": "2026-01-22T13:39:23.0961745+08:00", - "updated_at": "2026-03-06T13:13:05.112894+08:00" - }, - { - "id": "188cf728ed2992f06a8f9e52", - "name": "easytier面板", - "groups": [ - "admin" - ], - "urls": [ - { - "id": "0a0a59069228", - "url": "http://easytier.shumengya.top", - "remark": "" - } - ], - "ip_addresses": [ - "47.108.228.23" - ], - "favicon": "http://easytier.shumengya.top/easytier.png", - "title": "EasyTier Dashboard", - "created_at": "2026-01-22T13:41:48.7994396+08:00", - "updated_at": "2026-03-06T13:13:06.2605727+08:00" - }, - { - "id": "188cf72edea71c4cb2284c93", - "name": "wireguard面板", - "groups": [ - "admin" - ], - "urls": [ - { - "id": "170fab2239ad", - "url": "https://wireguard.shumengya.top", - "remark": "" - } - ], - "ip_addresses": [ - "47.108.228.23" - ], - "favicon": "https://wireguard.shumengya.top/favicon.ico", - "title": "502 Bad Gateway", - "created_at": "2026-01-22T13:42:14.3258123+08:00", - "updated_at": "2026-03-06T13:13:07.8283939+08:00" - }, - { - "id": "188cf746b16a83dc4d5e3d13", - "name": "萌芽Docker镜像仓库", - "groups": [ - "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": "萌芽通知", - "groups": [ - "admin" - ], - "urls": [ - { - "id": "2f580f90f979", - "url": "https://notice.shumengya.top", - "remark": "" - } - ], - "ip_addresses": [ - "47.108.228.23" - ], - "favicon": "https://notice.shumengya.top/favicon.ico", - "title": "萌芽监控面板", - "created_at": "2026-01-22T13:44:15.9437687+08:00", - "updated_at": "2026-03-06T13:13:09.4168617+08:00" - }, - { - "id": "188d00db054cf7860dff3440", - "name": "萌芽Ping", - "groups": [ - "admin" - ], - "urls": [ - { - "id": "ac85b166ccc4", - "url": "https://ping.shumengya.top", - "remark": "" - } - ], - "ip_addresses": [ - "47.108.228.23" - ], - "favicon": "https://ping.shumengya.top/favicon.ico", - "title": "萌芽Ping - 网站监控面板", - "created_at": "2026-01-22T16:39:29.31324815+08:00", - "updated_at": "2026-03-06T13:13:10.213788+08:00" - }, - { - "id": "188d00edf571226f42ec8b20", - "name": "萌芽Ping后端API", - "groups": [ - "api" - ], - "urls": [ - { - "id": "0bf0fe1bb9cc", - "url": "https://ping.api.shumengya.top", - "remark": "" - } - ], - "ip_addresses": [ - "47.108.228.23" - ], - "favicon": "https://ping.api.shumengya.top/favicon.ico", - "title": "萌芽Git仓库", - "created_at": "2026-01-22T16:40:50.651561641+08:00", - "updated_at": "2026-03-06T13:13:11.3565622+08:00" - }, - { - "id": "188e7f1a40cdc7bc3bba1817", - "name": "DNS查询[CloudFlare]", - "groups": [ - "self-deploy" - ], - "urls": [ - { - "id": "9af024b3d387", - "url": "https://cf-dns.smyhub.com/", - "remark": "" - } - ], - "ip_addresses": [ - "188.114.97.0", - "188.114.96.0", - "2a06:98c1:3121::", - "2a06:98c1:3120::" - ], - "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-03-06T13:13:12.1501803+08:00" - }, - { - "id": "188e7f27a909fe247a2fcd15", - "name": "Github文件加速[CloudFlare]", - "groups": [ - "self-deploy" - ], - "urls": [ - { - "id": "b897ef0def67", - "url": "https://gh-proxy.smyhub.com/", - "remark": "" - } - ], - "ip_addresses": [ - "188.114.97.0", - "188.114.96.0", - "2a06:98c1:3121::", - "2a06:98c1:3120::" - ], - "favicon": "https://gh-proxy.smyhub.com/favicon.ico", - "title": "GitHub 文件加速", - "created_at": "2026-01-27T13:25:11.9196401+08:00", - "updated_at": "2026-03-06T13:13:12.9476294+08:00" - }, - { - "id": "188e7f33f087667c29da05f2", - "name": "FloppyBird[CloudFlare]", - "groups": [ - "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]", - "groups": [ - "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 index b64fe0b..6f6e83f 100644 --- a/mengyaping-backend/docker-compose.yml +++ b/mengyaping-backend/docker-compose.yml @@ -1,3 +1,5 @@ +# 生产:先在 Windows 运行 build-linux-amd64.bat 生成 dist/mengyaping-backend,再 docker compose build(镜像内不再编译 Go) +# 若需在服务器上用源码构建镜像:docker build -f Dockerfile.source -t mengyaping-backend:source . version: '3.8' services: @@ -16,11 +18,13 @@ services: # 服务器配置 - SERVER_PORT=8080 - SERVER_HOST=0.0.0.0 - # 监控配置 - - MONITOR_INTERVAL=5m + # 监控默认值(首次写入 MySQL monitor_kv;周期也可在管理后台改,须为 10/20/30/60/180/360 分钟之一) + - MONITOR_INTERVAL=30m - MONITOR_TIMEOUT=10s - MONITOR_RETRY_COUNT=3 - MONITOR_HISTORY_DAYS=7 + # 若设置则覆盖数据库 monitor_kv 中的 admin_token(部署密文推荐用环境变量) + - ADMIN_TOKEN=shumengya520 networks: - mengyaping-network healthcheck: diff --git a/mengyaping-backend/go.mod b/mengyaping-backend/go.mod index 5fbb0aa..052e8a7 100644 --- a/mengyaping-backend/go.mod +++ b/mengyaping-backend/go.mod @@ -5,9 +5,13 @@ go 1.25.0 require ( github.com/gin-contrib/cors v1.7.6 github.com/gin-gonic/gin v1.11.0 + gorm.io/datatypes v1.2.4 + gorm.io/driver/mysql v1.5.7 + gorm.io/gorm v1.25.12 ) require ( + filippo.io/edwards25519 v1.1.0 // indirect 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 @@ -16,8 +20,12 @@ require ( 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/go-sql-driver/mysql v1.8.1 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-yaml v1.18.0 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // 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 diff --git a/mengyaping-backend/go.sum b/mengyaping-backend/go.sum index 7539074..286fadd 100644 --- a/mengyaping-backend/go.sum +++ b/mengyaping-backend/go.sum @@ -1,3 +1,5 @@ +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= 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= @@ -23,13 +25,34 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn 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/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= 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/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= +github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= 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/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA= +github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= +github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 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= @@ -38,6 +61,10 @@ 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/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= +github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE= +github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ= 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= @@ -89,3 +116,16 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 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= +gorm.io/datatypes v1.2.4 h1:uZmGAcK/QZ0uyfCuVg0VQY1ZmV9h1fuG0tMwKByO1z4= +gorm.io/datatypes v1.2.4/go.mod h1:f4BsLcFAX67szSv8svwLRjklArSHAvHLeE3pXAS5DZI= +gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= +gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= +gorm.io/driver/postgres v1.5.0 h1:u2FXTy14l45qc3UeCJ7QaAXZmZfDDv0YrthvmRq1l0U= +gorm.io/driver/postgres v1.5.0/go.mod h1:FUZXzO+5Uqg5zzwzv4KK49R8lvGIyscBOqYrtI1Ce9A= +gorm.io/driver/sqlite v1.4.3 h1:HBBcZSDnWi5BW3B3rwvVTc510KGkBkexlOg0QrmLUuU= +gorm.io/driver/sqlite v1.4.3/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI= +gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0= +gorm.io/driver/sqlserver v1.4.1/go.mod h1:DJ4P+MeZbc5rvY58PnmN1Lnyvb5gw5NPzGshHDnJLig= +gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8= +gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= diff --git a/mengyaping-backend/handlers/settings.go b/mengyaping-backend/handlers/settings.go new file mode 100644 index 0000000..e63b9fd --- /dev/null +++ b/mengyaping-backend/handlers/settings.go @@ -0,0 +1,87 @@ +package handlers + +import ( + "net/http" + + "github.com/gin-gonic/gin" + + "mengyaping-backend/config" + "mengyaping-backend/models" + "mengyaping-backend/services" + "mengyaping-backend/storage" +) + +// SettingsHandler 监控与系统配置(公开可读、管理端可写) +type SettingsHandler struct { + monitor *services.MonitorService +} + +// NewSettingsHandler 创建配置处理器 +func NewSettingsHandler() *SettingsHandler { + return &SettingsHandler{ + monitor: services.GetMonitorService(), + } +} + +// GetPublicMonitorSettings 公开:当前全局检测周期与可选值 +func (h *SettingsHandler) GetPublicMonitorSettings(c *gin.Context) { + cfg := config.GetConfig() + c.JSON(http.StatusOK, models.APIResponse{ + Code: 0, + Message: "success", + Data: gin.H{ + "interval_minutes": int(cfg.Monitor.Interval.Minutes()), + "allowed_interval_minutes": config.AllowedMonitorIntervalMinutes, + }, + }) +} + +// GetAdminConfig 管理端:监控相关配置(只读字段供面板展示) +func (h *SettingsHandler) GetAdminConfig(c *gin.Context) { + cfg := config.GetConfig() + c.JSON(http.StatusOK, models.APIResponse{ + Code: 0, + Message: "success", + Data: gin.H{ + "interval_minutes": int(cfg.Monitor.Interval.Minutes()), + "allowed_interval_minutes": config.AllowedMonitorIntervalMinutes, + "timeout_seconds": int(cfg.Monitor.Timeout.Seconds()), + "retry_count": cfg.Monitor.RetryCount, + "history_days": cfg.Monitor.HistoryDays, + }, + }) +} + +type putAdminMonitorConfigRequest struct { + IntervalMinutes int `json:"interval_minutes" binding:"required"` +} + +// PutAdminMonitorConfig 更新全局检测周期并持久化、应用新调度 +func (h *SettingsHandler) PutAdminMonitorConfig(c *gin.Context) { + var req putAdminMonitorConfigRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, models.APIResponse{ + Code: 400, + Message: "参数错误: " + err.Error(), + }) + return + } + + if err := storage.GetStorage().SetMonitorIntervalMinutes(req.IntervalMinutes); err != nil { + c.JSON(http.StatusBadRequest, models.APIResponse{ + Code: 400, + Message: err.Error(), + }) + return + } + + h.monitor.Reschedule() + + c.JSON(http.StatusOK, models.APIResponse{ + Code: 0, + Message: "已更新检测周期", + Data: gin.H{ + "interval_minutes": req.IntervalMinutes, + }, + }) +} diff --git a/mengyaping-backend/handlers/website.go b/mengyaping-backend/handlers/website.go index 258eb9e..5dc8cff 100644 --- a/mengyaping-backend/handlers/website.go +++ b/mengyaping-backend/handlers/website.go @@ -1,199 +1,267 @@ -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, - }) -} +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, + }) +} + +// GetGroup 获取单个分组 +func (h *WebsiteHandler) GetGroup(c *gin.Context) { + id := c.Param("id") + g := h.websiteService.GetGroup(id) + if g == nil { + c.JSON(http.StatusNotFound, models.APIResponse{ + Code: 404, + Message: "分类不存在", + }) + return + } + + c.JSON(http.StatusOK, models.APIResponse{ + Code: 0, + Message: "success", + Data: g, + }) +} + +// UpdateGroup 更新分组 +func (h *WebsiteHandler) UpdateGroup(c *gin.Context) { + id := c.Param("id") + + 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.UpdateGroup(id, req.Name) + if err != nil { + c.JSON(http.StatusBadRequest, models.APIResponse{ + Code: 400, + Message: err.Error(), + }) + return + } + + c.JSON(http.StatusOK, models.APIResponse{ + Code: 0, + Message: "更新成功", + Data: group, + }) +} + +// DeleteGroup 删除分组 +func (h *WebsiteHandler) DeleteGroup(c *gin.Context) { + id := c.Param("id") + + if err := h.websiteService.DeleteGroup(id); err != nil { + c.JSON(http.StatusBadRequest, models.APIResponse{ + Code: 400, + Message: err.Error(), + }) + return + } + + c.JSON(http.StatusOK, models.APIResponse{ + Code: 0, + Message: "删除成功", + }) +} + +// 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 index 3286ce9..cb1b66d 100644 --- a/mengyaping-backend/main.go +++ b/mengyaping-backend/main.go @@ -1,47 +1,50 @@ -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) - } -} +package main + +import ( + "fmt" + "log" + "os" + "os/signal" + "syscall" + + "mengyaping-backend/config" + "mengyaping-backend/router" + "mengyaping-backend/services" + "mengyaping-backend/storage" +) + +func main() { + config.GetConfig() + // 连接 MySQL 并从 monitor_kv 加载应用配置(须在监听与监控协程使用配置之前) + storage.GetStorage() + cfg := config.GetConfig() + if err := os.MkdirAll(cfg.DataPath, 0755); err != nil { + log.Printf("创建数据目录 %s: %v", cfg.DataPath, err) + } + + // 启动监控服务 + 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/middleware/admin.go b/mengyaping-backend/middleware/admin.go new file mode 100644 index 0000000..94bfc0c --- /dev/null +++ b/mengyaping-backend/middleware/admin.go @@ -0,0 +1,43 @@ +package middleware + +import ( + "net/http" + "strings" + + "github.com/gin-gonic/gin" + + "mengyaping-backend/models" + "mengyaping-backend/storage" +) + +// AdminAuth 校验管理员令牌:环境变量 ADMIN_TOKEN 优先,否则使用 MySQL monitor_kv 中的 admin_token(请求头 X-Admin-Token 或 Authorization: Bearer) +func AdminAuth() gin.HandlerFunc { + return func(c *gin.Context) { + expected := storage.GetStorage().EffectiveAdminToken() + if expected == "" { + c.AbortWithStatusJSON(http.StatusServiceUnavailable, models.APIResponse{ + Code: 503, + Message: "未配置管理员令牌:请在数据库 monitor_kv 写入 admin_token,或设置环境变量 ADMIN_TOKEN", + }) + return + } + + got := strings.TrimSpace(c.GetHeader("X-Admin-Token")) + if got == "" { + auth := c.GetHeader("Authorization") + const prefix = "Bearer " + if len(auth) >= len(prefix) && strings.EqualFold(auth[:len(prefix)], prefix) { + got = strings.TrimSpace(auth[len(prefix):]) + } + } + + if got == "" || got != expected { + c.AbortWithStatusJSON(http.StatusUnauthorized, models.APIResponse{ + Code: 401, + Message: "无效的管理员令牌", + }) + return + } + c.Next() + } +} diff --git a/mengyaping-backend/models/website.go b/mengyaping-backend/models/website.go index e3a281a..a5ce356 100644 --- a/mengyaping-backend/models/website.go +++ b/mengyaping-backend/models/website.go @@ -1,113 +1,117 @@ -package models - -import ( - "time" -) - -// Website 网站信息 -type Website struct { - ID string `json:"id"` - Name string `json:"name"` // 网站名称 - Groups []string `json:"groups"` // 所属分组列表(支持多分组) - Group string `json:"group,omitempty"` // 已废弃,仅用于旧数据兼容 - URLs []URLInfo `json:"urls"` // 网站访问地址列表 - IPAddresses []string `json:"ip_addresses,omitempty"` // 域名解析的IP地址 - 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"` - DailyHistory []DailyStats `json:"daily_history"` // 90天逐日统计 - Uptime24h float64 `json:"uptime_24h"` // 24小时可用率 - Uptime7d float64 `json:"uptime_7d"` // 7天可用率 - Uptime90d float64 `json:"uptime_90d"` // 90天可用率 - 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"` -} - -// DailyStats 每日统计 -type DailyStats struct { - Date time.Time `json:"date"` - 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"` - Groups []string `json:"groups" binding:"required,min=1"` - Group string `json:"group"` - URLs []string `json:"urls" binding:"required,min=1"` -} - -// UpdateWebsiteRequest 更新网站请求 -type UpdateWebsiteRequest struct { - Name string `json:"name"` - Groups []string `json:"groups"` - 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"` -} +package models + +import ( + "time" +) + +// Website 网站信息 +type Website struct { + ID string `json:"id"` + Name string `json:"name"` // 网站名称 + Groups []string `json:"groups"` // 所属分组列表(支持多分组) + Group string `json:"group,omitempty"` // 已废弃,仅用于旧数据兼容 + URLs []URLInfo `json:"urls"` // 网站访问地址列表 + IPAddresses []string `json:"ip_addresses,omitempty"` // 域名解析的IP地址 + 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"` + IsIntranet bool `json:"is_intranet"` // 主 URL 为内网 IP:不解析公网 DNS、不展示解析 IP,用内网图标 + URLStatuses []URLStatus `json:"url_statuses"` + DailyHistory []DailyStats `json:"daily_history"` // 90天逐日统计 + Uptime24h float64 `json:"uptime_24h"` // 24小时可用率 + Uptime7d float64 `json:"uptime_7d"` // 7天可用率 + Uptime90d float64 `json:"uptime_90d"` // 90天可用率 + 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"` +} + +// DailyStats 每日统计 +type DailyStats struct { + Date time.Time `json:"date"` + 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: "管理员网站"}, +} + +// DefaultIntranetFavicon 直填内网站点的列表图标(静态 SVG,不依赖公网 favicon 抓取) +const DefaultIntranetFavicon = "https://api.iconify.design/mdi/lan-connect.svg?color=%23059669" + +// CreateWebsiteRequest 创建网站请求 +type CreateWebsiteRequest struct { + Name string `json:"name" binding:"required"` + Groups []string `json:"groups" binding:"required,min=1"` + Group string `json:"group"` + URLs []string `json:"urls" binding:"required,min=1"` +} + +// UpdateWebsiteRequest 更新网站请求 +type UpdateWebsiteRequest struct { + Name string `json:"name"` + Groups []string `json:"groups"` + 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 index e6177d8..657e2e0 100644 --- a/mengyaping-backend/router/router.go +++ b/mengyaping-backend/router/router.go @@ -1,51 +1,66 @@ -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 -} +package router + +import ( + "github.com/gin-contrib/cors" + "github.com/gin-gonic/gin" + + "mengyaping-backend/handlers" + "mengyaping-backend/middleware" +) + +// 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", "X-Admin-Token"}, + ExposeHeaders: []string{"Content-Length"}, + AllowCredentials: true, + })) + + // 创建处理器 + websiteHandler := handlers.NewWebsiteHandler() + settingsHandler := handlers.NewSettingsHandler() + + // API路由组 + api := r.Group("/api") + { + // 健康检查 + api.GET("/health", func(c *gin.Context) { + c.JSON(200, gin.H{ + "status": "ok", + "message": "服务运行正常", + }) + }) + + // 公开:监控调度信息(仅展示) + api.GET("/monitor/settings", settingsHandler.GetPublicMonitorSettings) + + // 网站 / 分组 — 读操作公开 + api.GET("/websites", websiteHandler.GetWebsites) + api.GET("/websites/:id", websiteHandler.GetWebsite) + api.GET("/groups", websiteHandler.GetGroups) + api.GET("/groups/:id", websiteHandler.GetGroup) + + // 管理:写操作需 ADMIN_TOKEN + admin := api.Group("") + admin.Use(middleware.AdminAuth()) + { + admin.POST("/websites", websiteHandler.CreateWebsite) + admin.PUT("/websites/:id", websiteHandler.UpdateWebsite) + admin.DELETE("/websites/:id", websiteHandler.DeleteWebsite) + admin.POST("/websites/:id/check", websiteHandler.CheckWebsiteNow) + admin.POST("/groups", websiteHandler.AddGroup) + admin.PUT("/groups/:id", websiteHandler.UpdateGroup) + admin.DELETE("/groups/:id", websiteHandler.DeleteGroup) + + admin.GET("/admin/config", settingsHandler.GetAdminConfig) + admin.PUT("/admin/config", settingsHandler.PutAdminMonitorConfig) + } + } + + return r +} diff --git a/mengyaping-backend/schema/mysql.sql b/mengyaping-backend/schema/mysql.sql new file mode 100644 index 0000000..33139e8 --- /dev/null +++ b/mengyaping-backend/schema/mysql.sql @@ -0,0 +1,19 @@ +-- 萌芽 Ping — MySQL 8+ 表结构说明(应用启动时 GORM AutoMigrate 会自动建表;本文件便于 DBA 审阅或手工执行) +-- 字符集务必 utf8mb4 + +CREATE DATABASE IF NOT EXISTS mengyaping + DEFAULT CHARACTER SET utf8mb4 + COLLATE utf8mb4_unicode_ci; + +USE mengyaping; + +-- monitor_groups 网站分类 +-- monitor_websites 监控站点主档(ip_addresses 为 JSON 数组) +-- monitor_website_urls 每个站点下多监控 URL +-- monitor_website_groups 站点与分类多对多 +-- monitor_checks HTTP 探活历史(按保留天数定期清理) + +-- monitor_kv 键值配置:admin_token;cfg_server / cfg_monitor / cfg_data_path / cfg_database(JSON) +-- 与下列 GORM 模型一致: +-- MonitorGroup, MonitorWebsite, MonitorWebsiteURL, MonitorWebsiteGroup, +-- MonitorProbeLatest, MonitorProbeHour, MonitorProbeDay, MonitorKV diff --git a/mengyaping-backend/services/monitor.go b/mengyaping-backend/services/monitor.go index 757ceb5..dbe89fc 100644 --- a/mengyaping-backend/services/monitor.go +++ b/mengyaping-backend/services/monitor.go @@ -1,418 +1,488 @@ -package services - -import ( - "log" - "sort" - "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() - semaphore := make(chan struct{}, 3) // 最多 3 个并发检测 - - for i, website := range websites { - // 每个网站之间间隔 1 秒,把检测分散开 - if i > 0 { - time.Sleep(1 * time.Second) - } - - var wg sync.WaitGroup - 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() - } - - // 检测完毕后,逐个解析 DNS - s.resolveAllWebsiteIPs(websites) - - // 保存记录 - s.storage.SaveAll() - log.Printf("本轮检测完成,共 %d 个网站", len(websites)) -} - -// resolveAllWebsiteIPs 逐个解析所有网站域名 IP(每次都刷新) -func (s *MonitorService) resolveAllWebsiteIPs(websites []models.Website) { - for i, website := range websites { - if len(website.URLs) == 0 { - continue - } - if i > 0 { - time.Sleep(500 * time.Millisecond) - } - s.resolveWebsiteIP(website) - } -} - -// resolveWebsiteIP 解析单个网站的域名 IP -func (s *MonitorService) resolveWebsiteIP(website models.Website) { - if len(website.URLs) == 0 { - return - } - - ips, err := utils.ResolveDomainIPs(website.URLs[0].URL) - if err != nil { - log.Printf("DNS解析失败 [%s]: %v", website.Name, err) - return - } - - if len(ips) == 0 { - return - } - - w := s.storage.GetWebsite(website.ID) - if w == nil { - return - } - - w.IPAddresses = ips - w.UpdatedAt = time.Now() - s.storage.UpdateWebsite(*w) - log.Printf("DNS解析 [%s] → %v", website.Name, ips) -} - -// checkURL 检查单个URL(带重试) -func (s *MonitorService) checkURL(website models.Website, urlInfo models.URLInfo) { - cfg := config.GetConfig() - maxRetries := cfg.Monitor.RetryCount - - var result utils.CheckResult - - for attempt := 0; attempt <= maxRetries; attempt++ { - if attempt > 0 { - time.Sleep(time.Duration(attempt) * 2 * time.Second) - log.Printf("重试 [%s] %s - 第 %d 次重试", website.Name, urlInfo.URL, attempt) - } - - result = s.httpClient.CheckWebsiteStatus(urlInfo.URL) - if result.Error == nil && utils.IsSuccessStatus(result.StatusCode) { - break - } - } - - 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) - - // 仅当网站无标题时才做完整检测来获取元数据 - if record.IsUp && website.Title == "" { - fullResult := s.httpClient.CheckWebsite(urlInfo.URL) - if fullResult.Title != "" { - w := s.storage.GetWebsite(website.ID) - if w != nil { - w.Title = fullResult.Title - 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 立即检查指定网站(状态 + DNS,等待完成后保存) -func (s *MonitorService) CheckWebsiteNow(websiteID string) { - website := s.storage.GetWebsite(websiteID) - if website == nil { - return - } - - // 逐个检测该网站的所有 URL - for _, urlInfo := range website.URLs { - s.checkURL(*website, urlInfo) - } - - // 刷新 DNS - s.resolveWebsiteIP(*website) - - s.storage.SaveAll() -} - -// 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) - since90d := now.Add(-90 * 24 * time.Hour) - - var totalUptime24h, totalUptime7d float64 - var urlCount int - var allRecords90d []models.MonitorRecord - - 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++ - - records90d := s.storage.GetRecords(website.ID, urlInfo.ID, since90d) - allRecords90d = append(allRecords90d, records90d...) - } - - if urlCount > 0 { - status.Uptime24h = totalUptime24h / float64(urlCount) - status.Uptime7d = totalUptime7d / float64(urlCount) - } - - // 90 天逐日统计 - status.DailyHistory = s.aggregateByDay(allRecords90d) - if len(allRecords90d) > 0 { - upCount := 0 - for _, r := range allRecords90d { - if r.IsUp { - upCount++ - } - } - status.Uptime90d = float64(upCount) / float64(len(allRecords90d)) * 100 - } - - // 获取最后检测时间 - 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 -} - -// aggregateByDay 按天聚合记录 -func (s *MonitorService) aggregateByDay(records []models.MonitorRecord) []models.DailyStats { - dayMap := make(map[string]*models.DailyStats) - - for _, r := range records { - dayTime := time.Date(r.CheckedAt.Year(), r.CheckedAt.Month(), r.CheckedAt.Day(), 0, 0, 0, 0, r.CheckedAt.Location()) - dayKey := dayTime.Format("2006-01-02") - - if _, exists := dayMap[dayKey]; !exists { - dayMap[dayKey] = &models.DailyStats{ - Date: dayTime, - } - } - - stats := dayMap[dayKey] - stats.TotalCount++ - if r.IsUp { - stats.UpCount++ - } - stats.AvgLatency += r.Latency - } - - result := make([]models.DailyStats, 0, len(dayMap)) - for _, stats := range dayMap { - if stats.TotalCount > 0 { - stats.AvgLatency /= int64(stats.TotalCount) - stats.Uptime = float64(stats.UpCount) / float64(stats.TotalCount) * 100 - } - result = append(result, *stats) - } - - sort.Slice(result, func(i, j int) bool { - return result[i].Date.Before(result[j].Date) - }) - - 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 -} +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{} + rescheduleCh chan struct{} + running bool + mu sync.Mutex +} + +var ( + monitorService *MonitorService + monitorOnce sync.Once +) + +// GetMonitorService 获取监控服务单例 +func GetMonitorService() *MonitorService { + monitorOnce.Do(func() { + st := storage.GetStorage() + cfg := config.GetConfig() + monitorService = &MonitorService{ + httpClient: utils.NewHTTPClient(cfg.Monitor.Timeout), + storage: st, + stopCh: make(chan struct{}), + rescheduleCh: make(chan struct{}, 1), + } + }) + 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.runLoop() +} + +// Reschedule 在检测间隔配置变更后唤醒主循环以使用新周期(非阻塞) +func (s *MonitorService) Reschedule() { + select { + case s.rescheduleCh <- struct{}{}: + default: + } +} + +func (s *MonitorService) runLoop() { + go s.checkAll() + + d := config.GetConfig().Monitor.Interval + if d <= 0 { + d = 30 * time.Minute + } + timer := time.NewTimer(d) + defer timer.Stop() + + for { + select { + case <-s.stopCh: + if !timer.Stop() { + select { + case <-timer.C: + default: + } + } + log.Println("监控服务已停止") + return + case <-s.rescheduleCh: + if !timer.Stop() { + select { + case <-timer.C: + default: + } + } + d = config.GetConfig().Monitor.Interval + if d <= 0 { + d = 30 * time.Minute + } + timer.Reset(d) + case <-timer.C: + go s.checkAll() + d = config.GetConfig().Monitor.Interval + if d <= 0 { + d = 30 * time.Minute + } + timer.Reset(d) + } + } +} + +// 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() + semaphore := make(chan struct{}, 3) // 最多 3 个并发检测 + + for i, website := range websites { + // 每个网站之间间隔 1 秒,把检测分散开 + if i > 0 { + time.Sleep(1 * time.Second) + } + + var wg sync.WaitGroup + 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() + } + + // 检测完毕后,逐个解析 DNS + s.resolveAllWebsiteIPs(websites) + + // 保存记录 + s.storage.SaveAll() + log.Printf("本轮检测完成,共 %d 个网站", len(websites)) +} + +// resolveAllWebsiteIPs 逐个解析所有网站域名 IP(每次都刷新) +func (s *MonitorService) resolveAllWebsiteIPs(websites []models.Website) { + for i, website := range websites { + if len(website.URLs) == 0 { + continue + } + if i > 0 { + time.Sleep(500 * time.Millisecond) + } + s.resolveWebsiteIP(website) + } +} + +// resolveWebsiteIP 解析单个网站的域名 IP +func (s *MonitorService) resolveWebsiteIP(website models.Website) { + if len(website.URLs) == 0 { + return + } + + if utils.IsIntranetURL(website.URLs[0].URL) { + w := s.storage.GetWebsite(website.ID) + if w != nil && len(w.IPAddresses) > 0 { + w.IPAddresses = nil + w.UpdatedAt = time.Now() + s.storage.UpdateWebsite(*w) + } + return + } + + ips, err := utils.ResolveDomainIPs(website.URLs[0].URL) + if err != nil { + log.Printf("DNS解析失败 [%s]: %v", website.Name, err) + return + } + + if len(ips) == 0 { + return + } + + w := s.storage.GetWebsite(website.ID) + if w == nil { + return + } + + w.IPAddresses = ips + w.UpdatedAt = time.Now() + s.storage.UpdateWebsite(*w) + log.Printf("DNS解析 [%s] → %v", website.Name, ips) +} + +// checkURL 检查单个URL(带重试) +func (s *MonitorService) checkURL(website models.Website, urlInfo models.URLInfo) { + cfg := config.GetConfig() + maxRetries := cfg.Monitor.RetryCount + + var result utils.CheckResult + + for attempt := 0; attempt <= maxRetries; attempt++ { + if attempt > 0 { + time.Sleep(time.Duration(attempt) * 2 * time.Second) + log.Printf("重试 [%s] %s - 第 %d 次重试", website.Name, urlInfo.URL, attempt) + } + + result = s.httpClient.CheckWebsiteStatus(urlInfo.URL) + if result.Error == nil && utils.IsSuccessStatus(result.StatusCode) { + break + } + } + + 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) + + // 仅当网站无标题时才做完整检测来获取元数据 + if record.IsUp && website.Title == "" { + fullResult := s.httpClient.CheckWebsite(urlInfo.URL) + if fullResult.Title != "" { + w := s.storage.GetWebsite(website.ID) + if w != nil { + w.Title = fullResult.Title + 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 立即检查指定网站(状态 + DNS,等待完成后保存) +func (s *MonitorService) CheckWebsiteNow(websiteID string) { + website := s.storage.GetWebsite(websiteID) + if website == nil { + return + } + + // 逐个检测该网站的所有 URL + for _, urlInfo := range website.URLs { + s.checkURL(*website, urlInfo) + } + + // 刷新 DNS + s.resolveWebsiteIP(*website) + + s.storage.SaveAll() +} + +// GetWebsiteStatus 获取网站状态 +func (s *MonitorService) GetWebsiteStatus(websiteID string) *models.WebsiteStatus { + website := s.storage.GetWebsite(websiteID) + if website == nil { + return nil + } + + isIntranet := len(website.URLs) > 0 && utils.IsIntranetURL(website.URLs[0].URL) + status := &models.WebsiteStatus{ + Website: *website, + IsIntranet: isIntranet, + URLStatuses: []models.URLStatus{}, + } + + now := time.Now() + since24h := now.Add(-24 * time.Hour) + since7d := now.Add(-7 * 24 * time.Hour) + since90d := now.Add(-90 * 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) + } + + status.DailyHistory = s.storage.GetWebsiteDailyAggregates(website.ID, since90d) + if pt, ut := s.storage.GetWebsiteDayProbeTotals(website.ID, since90d); pt > 0 { + status.Uptime90d = float64(ut) / float64(pt) * 100 + } + + // 获取最后检测时间 + for _, urlStatus := range status.URLStatuses { + if urlStatus.CurrentState.CheckedAt.After(status.LastChecked) { + status.LastChecked = urlStatus.CurrentState.CheckedAt + } + } + + return status +} + +// getURLStatus 获取URL状态:每个 URL 仅 1 次读小时表 + 1 次读 latest(在内存拆 24h/7d) +func (s *MonitorService) getURLStatus(websiteID string, urlInfo models.URLInfo, since24h, since7d time.Time) models.URLStatus { + var latest *models.MonitorRecord + if rec := s.storage.GetLatestRecord(websiteID, urlInfo.ID); rec != nil { + latest = rec + } + hRows := s.storage.GetProbeHourRowsForURL(websiteID, urlInfo.ID, since7d) + return s.buildURLStatusFromHours(websiteID, urlInfo, since24h, since7d, latest, hRows) +} + +func hourlyStatFromProbeRow(h storage.MonitorProbeHour) models.HourlyStats { + st := models.HourlyStats{ + Hour: h.HourAt, + TotalCount: h.ProbeCount, + UpCount: h.UpCount, + } + if h.ProbeCount > 0 { + st.AvgLatency = h.LatencySum / int64(h.ProbeCount) + st.Uptime = float64(h.UpCount) / float64(h.ProbeCount) * 100 + } + return st +} + +func hourWindowsFromRows(rows []storage.MonitorProbeHour, since24h, since7d time.Time) ( + stats24h []models.HourlyStats, + stats7d []models.HourlyStats, + t24, u24, l24, t7, u7 int64, +) { + for _, h := range rows { + if !h.HourAt.Before(since7d) { + t7 += int64(h.ProbeCount) + u7 += int64(h.UpCount) + stats7d = append(stats7d, hourlyStatFromProbeRow(h)) + } + if !h.HourAt.Before(since24h) { + t24 += int64(h.ProbeCount) + u24 += int64(h.UpCount) + l24 += h.LatencySum + stats24h = append(stats24h, hourlyStatFromProbeRow(h)) + } + } + return stats24h, stats7d, t24, u24, l24, t7, u7 +} + +func (s *MonitorService) buildURLStatusFromHours( + websiteID string, + urlInfo models.URLInfo, + since24h, since7d time.Time, + latest *models.MonitorRecord, + hRows []storage.MonitorProbeHour, +) models.URLStatus { + urlStatus := models.URLStatus{URLInfo: urlInfo} + if latest != nil { + urlStatus.CurrentState = *latest + } + s24, s7, t24, u24, l24, t7, u7 := hourWindowsFromRows(hRows, since24h, since7d) + urlStatus.History24h = hourlyStatsToMonitorRecords(websiteID, urlInfo.ID, s24) + if t24 > 0 { + urlStatus.Uptime24h = float64(u24) / float64(t24) * 100 + urlStatus.AvgLatency = l24 / t24 + } + urlStatus.History7d = s7 + if t7 > 0 { + urlStatus.Uptime7d = float64(u7) / float64(t7) * 100 + } + return urlStatus +} + +func hourlyStatsToMonitorRecords(websiteID, urlID string, stats []models.HourlyStats) []models.MonitorRecord { + out := make([]models.MonitorRecord, 0, len(stats)) + for _, h := range stats { + isUp := h.TotalCount > 0 && h.UpCount*2 >= h.TotalCount + out = append(out, models.MonitorRecord{ + WebsiteID: websiteID, + URLID: urlID, + Latency: h.AvgLatency, + IsUp: isUp, + CheckedAt: h.Hour, + }) + } + return out +} + +// GetAllWebsiteStatuses 获取所有网站状态(批量查 probe 表,避免每个站点/URL 多次 SQL) +func (s *MonitorService) GetAllWebsiteStatuses() []models.WebsiteStatus { + websites := s.storage.GetWebsites() + if len(websites) == 0 { + return nil + } + + now := time.Now() + since24h := now.Add(-24 * time.Hour) + since7d := now.Add(-7 * 24 * time.Hour) + since90d := now.Add(-90 * 24 * time.Hour) + since90dDay := time.Date(since90d.Year(), since90d.Month(), since90d.Day(), 0, 0, 0, 0, since90d.Location()) + + latestMap := s.storage.GetProbeLatestKeyMap() + hourMap := s.storage.GroupProbeHoursSince(since7d) + dailyBySite, uptime90dBySite := s.storage.LoadAllWebsiteDayRollups(since90dDay) + + statuses := make([]models.WebsiteStatus, 0, len(websites)) + for _, w := range websites { + daily := dailyBySite[w.ID] + u90 := uptime90dBySite[w.ID] + st := s.buildWebsiteStatusBulk(w, since24h, since7d, latestMap, hourMap, daily, u90) + if st != nil { + statuses = append(statuses, *st) + } + } + return statuses +} + +func (s *MonitorService) buildWebsiteStatusBulk( + w models.Website, + since24h, since7d time.Time, + latestMap map[string]models.MonitorRecord, + hourMap map[string]map[string][]storage.MonitorProbeHour, + daily []models.DailyStats, + uptime90d float64, +) *models.WebsiteStatus { + isIntranet := len(w.URLs) > 0 && utils.IsIntranetURL(w.URLs[0].URL) + status := &models.WebsiteStatus{ + Website: w, + IsIntranet: isIntranet, + URLStatuses: []models.URLStatus{}, + } + + siteHours := hourMap[w.ID] + if siteHours == nil { + siteHours = map[string][]storage.MonitorProbeHour{} + } + + var totalUptime24h, totalUptime7d float64 + urlCount := 0 + for _, urlInfo := range w.URLs { + key := w.ID + "\x00" + urlInfo.ID + var latest *models.MonitorRecord + if rec, ok := latestMap[key]; ok { + r := rec + latest = &r + } + hRows := siteHours[urlInfo.ID] + urlSt := s.buildURLStatusFromHours(w.ID, urlInfo, since24h, since7d, latest, hRows) + status.URLStatuses = append(status.URLStatuses, urlSt) + totalUptime24h += urlSt.Uptime24h + totalUptime7d += urlSt.Uptime7d + urlCount++ + } + if urlCount > 0 { + status.Uptime24h = totalUptime24h / float64(urlCount) + status.Uptime7d = totalUptime7d / float64(urlCount) + } + if daily != nil { + status.DailyHistory = daily + } else { + status.DailyHistory = []models.DailyStats{} + } + status.Uptime90d = uptime90d + for _, urlSt := range status.URLStatuses { + if urlSt.CurrentState.CheckedAt.After(status.LastChecked) { + status.LastChecked = urlSt.CurrentState.CheckedAt + } + } + return status +} diff --git a/mengyaping-backend/services/website.go b/mengyaping-backend/services/website.go index 4ae914d..55cefab 100644 --- a/mengyaping-backend/services/website.go +++ b/mengyaping-backend/services/website.go @@ -1,142 +1,177 @@ -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) { - groups := req.Groups - if len(groups) == 0 && req.Group != "" { - groups = []string{req.Group} - } - - website := models.Website{ - ID: utils.GenerateID(), - Name: req.Name, - Groups: groups, - 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) - } - - // 创建前先解析域名 IP - if len(req.URLs) > 0 { - if ips, err := utils.ResolveDomainIPs(req.URLs[0]); err == nil { - website.IPAddresses = ips - } - } - - 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 len(req.Groups) > 0 { - website.Groups = req.Groups - } else if req.Group != "" { - website.Groups = []string{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.IPAddresses = nil // URL 变更后清空 IP,等下次检测重新解析 - } - - 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 -} +package services + +import ( + "fmt" + "strings" + "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) { + groups := req.Groups + if len(groups) == 0 && req.Group != "" { + groups = []string{req.Group} + } + + website := models.Website{ + ID: utils.GenerateID(), + Name: req.Name, + Groups: groups, + 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 len(req.URLs) > 0 { + if utils.IsIntranetURL(req.URLs[0]) { + website.Favicon = models.DefaultIntranetFavicon + website.IPAddresses = nil + } else { + if ips, err := utils.ResolveDomainIPs(req.URLs[0]); err == nil { + website.IPAddresses = ips + } + } + } + + 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 len(req.Groups) > 0 { + website.Groups = req.Groups + } else if req.Group != "" { + website.Groups = []string{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.IPAddresses = nil // URL 变更后清空 IP,等下次检测重新解析 + + if len(req.URLs) > 0 && utils.IsIntranetURL(req.URLs[0]) { + website.Favicon = models.DefaultIntranetFavicon + website.IPAddresses = nil + } + } + + 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) { + name = strings.TrimSpace(name) + if name == "" { + return nil, fmt.Errorf("分类名称不能为空") + } + + group := models.Group{ + ID: utils.GenerateShortID(), + Name: name, + } + + if err := s.storage.AddGroup(group); err != nil { + return nil, err + } + + return &group, nil +} + +// GetGroup 获取单个分组 +func (s *WebsiteService) GetGroup(id string) *models.Group { + return s.storage.GetGroupByID(id) +} + +// UpdateGroup 更新分类名称 +func (s *WebsiteService) UpdateGroup(id string, name string) (*models.Group, error) { + if err := s.storage.UpdateGroup(id, name); err != nil { + return nil, err + } + g := s.storage.GetGroupByID(id) + return g, nil +} + +// DeleteGroup 删除分类 +func (s *WebsiteService) DeleteGroup(id string) error { + return s.storage.DeleteGroup(id) +} diff --git a/mengyaping-backend/storage/app_config.go b/mengyaping-backend/storage/app_config.go new file mode 100644 index 0000000..cba8398 --- /dev/null +++ b/mengyaping-backend/storage/app_config.go @@ -0,0 +1,161 @@ +package storage + +import ( + "encoding/json" + "fmt" + "log" + "time" + + "mengyaping-backend/config" +) + +const ( + kvKeyServer = "cfg_server" + kvKeyMonitor = "cfg_monitor" + kvKeyDataPath = "cfg_data_path" + kvKeyDatabase = "cfg_database" +) + +type monitorKVPayload struct { + IntervalMinutes int `json:"interval_minutes"` + TimeoutSeconds int `json:"timeout_seconds"` + RetryCount int `json:"retry_count"` + HistoryDays int `json:"history_days"` +} + +func (s *Storage) getKVRaw(key string) string { + var row MonitorKV + s.db.Where("cfg_key = ?", key).Limit(1).Find(&row) + if row.CfgKey == "" { + return "" + } + return row.CfgValue +} + +func (s *Storage) setKVRaw(key, val string) error { + var row MonitorKV + s.db.Where("cfg_key = ?", key).Limit(1).Find(&row) + if row.CfgKey == "" { + return s.db.Create(&MonitorKV{CfgKey: key, CfgValue: val}).Error + } + row.CfgValue = val + return s.db.Save(&row).Error +} + +// loadAndSyncAppConfig 从 monitor_kv 覆盖内存配置,再写回全量(补全缺省键、统一格式) +func (s *Storage) loadAndSyncAppConfig() { + cfg := config.GetConfig() + + if v := s.getKVRaw(kvKeyServer); v != "" { + var sc config.ServerConfig + if err := json.Unmarshal([]byte(v), &sc); err != nil { + log.Printf("解析 %s: %v", kvKeyServer, err) + } else { + if sc.Port != "" { + cfg.Server.Port = sc.Port + } + if sc.Host != "" { + cfg.Server.Host = sc.Host + } + } + } + + if v := s.getKVRaw(kvKeyMonitor); v != "" { + var m monitorKVPayload + if err := json.Unmarshal([]byte(v), &m); err != nil { + log.Printf("解析 %s: %v", kvKeyMonitor, err) + } else { + if m.IntervalMinutes > 0 { + snapped := config.SnapMonitorIntervalMinutes(m.IntervalMinutes) + cfg.Monitor.Interval = time.Duration(snapped) * time.Minute + } + if m.TimeoutSeconds > 0 { + cfg.Monitor.Timeout = time.Duration(m.TimeoutSeconds) * time.Second + } + if m.RetryCount > 0 { + cfg.Monitor.RetryCount = m.RetryCount + } + if m.HistoryDays > 0 { + cfg.Monitor.HistoryDays = m.HistoryDays + } + } + } + + if v := s.getKVRaw(kvKeyDataPath); v != "" { + cfg.DataPath = v + } + + if v := s.getKVRaw(kvKeyDatabase); v != "" { + var dc config.DatabaseConfig + if err := json.Unmarshal([]byte(v), &dc); err != nil { + log.Printf("解析 %s: %v", kvKeyDatabase, err) + } else { + if dc.Host != "" { + cfg.Database.Host = dc.Host + } + if dc.Port != "" { + cfg.Database.Port = dc.Port + } + if dc.User != "" { + cfg.Database.User = dc.User + } + if dc.Password != "" { + cfg.Database.Password = dc.Password + } + if dc.Database != "" { + cfg.Database.Database = dc.Database + } + } + } + + if err := s.PersistAppConfig(); err != nil { + log.Printf("同步应用配置到 monitor_kv: %v", err) + } +} + +// PersistAppConfig 将当前内存中的 server/monitor/data_path/database 写入 monitor_kv +func (s *Storage) PersistAppConfig() error { + cfg := config.GetConfig() + + b, err := json.Marshal(cfg.Server) + if err != nil { + return err + } + if err := s.setKVRaw(kvKeyServer, string(b)); err != nil { + return err + } + + m := monitorKVPayload{ + IntervalMinutes: config.SnapMonitorIntervalMinutes(int(cfg.Monitor.Interval / time.Minute)), + TimeoutSeconds: int(cfg.Monitor.Timeout.Seconds()), + RetryCount: cfg.Monitor.RetryCount, + HistoryDays: cfg.Monitor.HistoryDays, + } + b, err = json.Marshal(m) + if err != nil { + return err + } + if err := s.setKVRaw(kvKeyMonitor, string(b)); err != nil { + return err + } + + if err := s.setKVRaw(kvKeyDataPath, cfg.DataPath); err != nil { + return err + } + + b, err = json.Marshal(cfg.Database) + if err != nil { + return err + } + return s.setKVRaw(kvKeyDatabase, string(b)) +} + +// SetMonitorIntervalMinutes 更新检测周期并写入 monitor_kv +func (s *Storage) SetMonitorIntervalMinutes(minutes int) error { + if !config.IsAllowedMonitorInterval(minutes) { + return fmt.Errorf("interval_minutes 必须是预设值之一: %v", config.AllowedMonitorIntervalMinutes) + } + cfg := config.GetConfig() + cfg.Monitor.Interval = time.Duration(minutes) * time.Minute + return s.PersistAppConfig() +} diff --git a/mengyaping-backend/storage/db_kv.go b/mengyaping-backend/storage/db_kv.go new file mode 100644 index 0000000..c737575 --- /dev/null +++ b/mengyaping-backend/storage/db_kv.go @@ -0,0 +1,14 @@ +package storage + +import "time" + +// MonitorKV 键值配置(管理员令牌等),列名避开 MySQL 保留字 key/value +type MonitorKV struct { + CfgKey string `gorm:"primaryKey;size:128;column:cfg_key"` + CfgValue string `gorm:"type:text;column:cfg_value"` + UpdatedAt time.Time `gorm:"autoUpdateTime"` +} + +func (MonitorKV) TableName() string { return "monitor_kv" } + +const kvKeyAdminToken = "admin_token" diff --git a/mengyaping-backend/storage/db_models.go b/mengyaping-backend/storage/db_models.go new file mode 100644 index 0000000..1e19400 --- /dev/null +++ b/mengyaping-backend/storage/db_models.go @@ -0,0 +1,93 @@ +package storage + +import ( + "time" + + "gorm.io/datatypes" +) + +// MonitorGroup 网站分类(避免使用 MySQL 保留字 group) +type MonitorGroup struct { + ID string `gorm:"primaryKey;size:64"` + Name string `gorm:"size:255;not null"` + SortOrder int `gorm:"default:0;index"` + CreatedAt time.Time `gorm:"autoCreateTime"` + UpdatedAt time.Time `gorm:"autoUpdateTime"` +} + +func (MonitorGroup) TableName() string { return "monitor_groups" } + +// MonitorWebsite 监控站点主表 +type MonitorWebsite struct { + ID string `gorm:"primaryKey;size:128"` + Name string `gorm:"size:255;not null;index"` + LegacyGroup string `gorm:"size:64;column:legacy_group"` // 旧版单分组,仅兼容 + Favicon string `gorm:"type:text"` + Title string `gorm:"size:512"` + IPAddresses datatypes.JSON `gorm:"type:json"` // []string + CreatedAt time.Time `gorm:"autoCreateTime"` + UpdatedAt time.Time `gorm:"autoUpdateTime"` + + URLs []MonitorWebsiteURL `gorm:"foreignKey:WebsiteID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"` + GroupLinks []MonitorWebsiteGroup `gorm:"foreignKey:WebsiteID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"` +} + +func (MonitorWebsite) TableName() string { return "monitor_websites" } + +// MonitorWebsiteURL 站点监控 URL(一对多) +type MonitorWebsiteURL struct { + ID uint64 `gorm:"primaryKey;autoIncrement"` + WebsiteID string `gorm:"size:128;not null;index:idx_url_site,priority:1"` + URLID string `gorm:"size:64;not null;index:idx_url_site,priority:2"` + URL string `gorm:"type:text;not null"` + Remark string `gorm:"size:512"` + SortOrder int `gorm:"default:0"` +} + +func (MonitorWebsiteURL) TableName() string { return "monitor_website_urls" } + +// MonitorWebsiteGroup 站点与分类多对多 +type MonitorWebsiteGroup struct { + WebsiteID string `gorm:"primaryKey;size:128"` + GroupID string `gorm:"primaryKey;size:64"` +} + +func (MonitorWebsiteGroup) TableName() string { return "monitor_website_groups" } + +// MonitorProbeLatest 每个 URL 最新一次探测结果(不逐条存历史) +type MonitorProbeLatest struct { + WebsiteID string `gorm:"primaryKey;size:128"` + URLID string `gorm:"primaryKey;size:64"` + URL string `gorm:"type:text"` + StatusCode int `gorm:"column:status_code"` + LatencyMs int64 `gorm:"column:latency_ms"` + IsUp bool `gorm:"column:is_up"` + ErrorText string `gorm:"type:text;column:error_text"` + CheckedAt time.Time `gorm:"index"` +} + +func (MonitorProbeLatest) TableName() string { return "monitor_probe_latest" } + +// MonitorProbeHour 按小时汇总(用于 24h / 7d 统计与图表),行数 ≈ URL 数 × 小时数 +type MonitorProbeHour struct { + WebsiteID string `gorm:"primaryKey;size:128"` + URLID string `gorm:"primaryKey;size:64"` + HourAt time.Time `gorm:"primaryKey;type:datetime"` // 整点 + ProbeCount int `gorm:"default:0"` + UpCount int `gorm:"default:0"` + LatencySum int64 `gorm:"default:0"` // 总延迟 ms,均值为 sum/count +} + +func (MonitorProbeHour) TableName() string { return "monitor_probe_hour" } + +// MonitorProbeDay 按自然日汇总(用于 90 天柱状图),行数 ≈ URL 数 × 天数 +type MonitorProbeDay struct { + WebsiteID string `gorm:"primaryKey;size:128"` + URLID string `gorm:"primaryKey;size:64"` + StatDate time.Time `gorm:"primaryKey;type:date"` + ProbeCount int `gorm:"default:0"` + UpCount int `gorm:"default:0"` + LatencySum int64 `gorm:"default:0"` +} + +func (MonitorProbeDay) TableName() string { return "monitor_probe_day" } diff --git a/mengyaping-backend/storage/kv.go b/mengyaping-backend/storage/kv.go new file mode 100644 index 0000000..76c52aa --- /dev/null +++ b/mengyaping-backend/storage/kv.go @@ -0,0 +1,62 @@ +package storage + +import ( + "errors" + "log" + "os" + "strings" +) + +var errEmptyAdminToken = errors.New("admin_token 不能为空") + +// EffectiveAdminToken 管理员令牌:环境变量 ADMIN_TOKEN 优先,否则读 MySQL monitor_kv +func (s *Storage) EffectiveAdminToken() string { + if v := strings.TrimSpace(os.Getenv("ADMIN_TOKEN")); v != "" { + return v + } + return strings.TrimSpace(s.adminTokenFromDB()) +} + +func (s *Storage) adminTokenFromDB() string { + var row MonitorKV + if err := s.db.Where("cfg_key = ?", kvKeyAdminToken).Limit(1).Find(&row).Error; err != nil { + return "" + } + if row.CfgKey == "" { + return "" + } + return row.CfgValue +} + +// SetAdminToken 更新数据库中的管理员令牌(需已能通过 AdminAuth) +func (s *Storage) SetAdminToken(token string) error { + token = strings.TrimSpace(token) + if token == "" { + return errEmptyAdminToken + } + var row MonitorKV + s.db.Where("cfg_key = ?", kvKeyAdminToken).Limit(1).Find(&row) + if row.CfgKey == "" { + return s.db.Create(&MonitorKV{CfgKey: kvKeyAdminToken, CfgValue: token}).Error + } + row.CfgValue = token + return s.db.Save(&row).Error +} + +func (s *Storage) seedAdminTokenKV() { + var n int64 + s.db.Model(&MonitorKV{}).Where("cfg_key = ?", kvKeyAdminToken).Count(&n) + if n > 0 { + return + } + val := strings.TrimSpace(os.Getenv("ADMIN_TOKEN")) + if val == "" { + log.Println("提示: 数据库 monitor_kv 中尚无 admin_token。可设置环境变量 ADMIN_TOKEN 后重启以自动写入,或对 monitor_kv 执行 INSERT。") + return + } + if err := s.db.Create(&MonitorKV{CfgKey: kvKeyAdminToken, CfgValue: val}).Error; err != nil { + log.Printf("写入初始 admin_token 失败: %v", err) + return + } + log.Println("已从环境变量 ADMIN_TOKEN 写入数据库 monitor_kv(后续可去掉该环境变量,改用库中令牌)。") +} diff --git a/mengyaping-backend/storage/probe.go b/mengyaping-backend/storage/probe.go new file mode 100644 index 0000000..f6a2867 --- /dev/null +++ b/mengyaping-backend/storage/probe.go @@ -0,0 +1,295 @@ +package storage + +import ( + "log" + "time" + + "gorm.io/gorm" + + "mengyaping-backend/config" + "mengyaping-backend/models" +) + +func latestToRecord(p *MonitorProbeLatest) models.MonitorRecord { + return models.MonitorRecord{ + WebsiteID: p.WebsiteID, + URLID: p.URLID, + URL: p.URL, + StatusCode: p.StatusCode, + Latency: p.LatencyMs, + IsUp: p.IsUp, + Error: p.ErrorText, + CheckedAt: p.CheckedAt, + } +} + +// AddRecord 写入最新探测 + 按小时/按天汇总(不插逐条历史) +func (s *Storage) AddRecord(record models.MonitorRecord) error { + hourAt := record.CheckedAt.Truncate(time.Hour) + statDate := time.Date(record.CheckedAt.Year(), record.CheckedAt.Month(), record.CheckedAt.Day(), + 0, 0, 0, 0, record.CheckedAt.Location()) + upDelta := 0 + if record.IsUp { + upDelta = 1 + } + + return s.db.Transaction(func(tx *gorm.DB) error { + lp := MonitorProbeLatest{ + WebsiteID: record.WebsiteID, + URLID: record.URLID, + URL: record.URL, + StatusCode: record.StatusCode, + LatencyMs: record.Latency, + IsUp: record.IsUp, + ErrorText: record.Error, + CheckedAt: record.CheckedAt, + } + if err := tx.Save(&lp).Error; err != nil { + return err + } + if err := tx.Exec(` +INSERT INTO monitor_probe_hour (website_id, url_id, hour_at, probe_count, up_count, latency_sum) +VALUES (?, ?, ?, 1, ?, ?) +ON DUPLICATE KEY UPDATE + probe_count = probe_count + 1, + up_count = up_count + VALUES(up_count), + latency_sum = latency_sum + VALUES(latency_sum) +`, record.WebsiteID, record.URLID, hourAt, upDelta, record.Latency).Error; err != nil { + return err + } + return tx.Exec(` +INSERT INTO monitor_probe_day (website_id, url_id, stat_date, probe_count, up_count, latency_sum) +VALUES (?, ?, ?, 1, ?, ?) +ON DUPLICATE KEY UPDATE + probe_count = probe_count + 1, + up_count = up_count + VALUES(up_count), + latency_sum = latency_sum + VALUES(latency_sum) +`, record.WebsiteID, record.URLID, statDate.Format("2006-01-02"), upDelta, record.Latency).Error + }) +} + +// GetLatestRecord 最新一条(无记录时返回 nil;用 Find 而非 First,避免 GORM 将「未找到」打成 record not found 日志) +func (s *Storage) GetLatestRecord(websiteID, urlID string) *models.MonitorRecord { + var p MonitorProbeLatest + if err := s.db.Where("website_id = ? AND url_id = ?", websiteID, urlID).Limit(1).Find(&p).Error; err != nil { + return nil + } + if p.WebsiteID == "" { + return nil + } + m := latestToRecord(&p) + return &m +} + +// GetRecords 兼容旧接口:按小时聚合展开为「代表点」(每探测窗口 1 条),仅用于仍依赖 slice 的逻辑;新逻辑请用汇总 API +func (s *Storage) GetRecords(websiteID, urlID string, since time.Time) []models.MonitorRecord { + rows := s.getProbeHourRows(websiteID, urlID, since) + var out []models.MonitorRecord + for _, h := range rows { + if h.ProbeCount <= 0 { + continue + } + avgLat := h.LatencySum / int64(h.ProbeCount) + isUp := h.UpCount*2 >= h.ProbeCount + out = append(out, models.MonitorRecord{ + WebsiteID: websiteID, + URLID: urlID, + StatusCode: 0, + Latency: avgLat, + IsUp: isUp, + CheckedAt: h.HourAt, + }) + } + return out +} + +func (s *Storage) getProbeHourRows(websiteID, urlID string, since time.Time) []MonitorProbeHour { + var rows []MonitorProbeHour + s.db.Where("website_id = ? AND url_id = ? AND hour_at >= ?", websiteID, urlID, since). + Order("hour_at ASC").Find(&rows) + return rows +} + +// GetProbeHourRowsForURL 某 URL 自 since 起的小时汇总行(升序) +func (s *Storage) GetProbeHourRowsForURL(websiteID, urlID string, since time.Time) []MonitorProbeHour { + return s.getProbeHourRows(websiteID, urlID, since) +} + +// GetProbeLatestKeyMap 全表 latest(每 URL 一行),key = website_id + "\x00" + url_id +func (s *Storage) GetProbeLatestKeyMap() map[string]models.MonitorRecord { + var rows []MonitorProbeLatest + if err := s.db.Find(&rows).Error; err != nil { + log.Printf("GetProbeLatestKeyMap: %v", err) + return map[string]models.MonitorRecord{} + } + m := make(map[string]models.MonitorRecord, len(rows)) + for i := range rows { + k := rows[i].WebsiteID + "\x00" + rows[i].URLID + m[k] = latestToRecord(&rows[i]) + } + return m +} + +// GroupProbeHoursSince 近窗口内全部小时汇总,按站点、URL 分组(列表接口一次查出) +func (s *Storage) GroupProbeHoursSince(since time.Time) map[string]map[string][]MonitorProbeHour { + var rows []MonitorProbeHour + if err := s.db.Where("hour_at >= ?", since).Order("website_id, url_id, hour_at").Find(&rows).Error; err != nil { + log.Printf("GroupProbeHoursSince: %v", err) + return map[string]map[string][]MonitorProbeHour{} + } + out := make(map[string]map[string][]MonitorProbeHour) + for i := range rows { + h := rows[i] + if out[h.WebsiteID] == nil { + out[h.WebsiteID] = make(map[string][]MonitorProbeHour) + } + out[h.WebsiteID][h.URLID] = append(out[h.WebsiteID][h.URLID], h) + } + return out +} + +// LoadAllWebsiteDayRollups 自 since 日起各站每日汇总 + 90d 整体可用率(单条 SQL 聚合) +func (s *Storage) LoadAllWebsiteDayRollups(since time.Time) (map[string][]models.DailyStats, map[string]float64) { + sinceDay := time.Date(since.Year(), since.Month(), since.Day(), 0, 0, 0, 0, since.Location()) + sinceStr := sinceDay.Format("2006-01-02") + type aggRow struct { + Wid string `gorm:"column:wid"` + D time.Time `gorm:"column:d"` + Pc int64 `gorm:"column:pc"` + Uc int64 `gorm:"column:uc"` + Ls int64 `gorm:"column:ls"` + } + var agg []aggRow + if err := s.db.Raw(` +SELECT website_id AS wid, stat_date AS d, SUM(probe_count) AS pc, SUM(up_count) AS uc, SUM(latency_sum) AS ls +FROM monitor_probe_day +WHERE stat_date >= ? +GROUP BY website_id, stat_date +ORDER BY website_id, stat_date +`, sinceStr).Scan(&agg).Error; err != nil { + log.Printf("LoadAllWebsiteDayRollups: %v", err) + return map[string][]models.DailyStats{}, map[string]float64{} + } + + daily := make(map[string][]models.DailyStats) + totP := make(map[string]int64) + totU := make(map[string]int64) + for _, r := range agg { + st := models.DailyStats{ + Date: r.D, + TotalCount: int(r.Pc), + UpCount: int(r.Uc), + } + if r.Pc > 0 { + st.AvgLatency = r.Ls / r.Pc + st.Uptime = float64(r.Uc) / float64(r.Pc) * 100 + } + daily[r.Wid] = append(daily[r.Wid], st) + totP[r.Wid] += r.Pc + totU[r.Wid] += r.Uc + } + uptime := make(map[string]float64) + for wid, p := range totP { + if p > 0 { + uptime[wid] = float64(totU[wid]) / float64(p) * 100 + } + } + return daily, uptime +} + +// GetProbeHourTotals 某 URL 在时间窗口内汇总(来自小时表) +func (s *Storage) GetProbeHourTotals(websiteID, urlID string, since time.Time) (probeTotal int64, upTotal int64, latencySum int64) { + type row struct { + P int64 + U int64 + L int64 + } + var r row + s.db.Model(&MonitorProbeHour{}). + Select("COALESCE(SUM(probe_count),0), COALESCE(SUM(up_count),0), COALESCE(SUM(latency_sum),0)"). + Where("website_id = ? AND url_id = ? AND hour_at >= ?", websiteID, urlID, since). + Scan(&r) + return r.P, r.U, r.L +} + +// GetHourlyStatsForURL 7 天折线用(已聚合) +func (s *Storage) GetHourlyStatsForURL(websiteID, urlID string, since time.Time) []models.HourlyStats { + rows := s.getProbeHourRows(websiteID, urlID, since) + out := make([]models.HourlyStats, 0, len(rows)) + for _, h := range rows { + st := models.HourlyStats{ + Hour: h.HourAt, + TotalCount: h.ProbeCount, + UpCount: h.UpCount, + } + if h.ProbeCount > 0 { + st.AvgLatency = h.LatencySum / int64(h.ProbeCount) + st.Uptime = float64(h.UpCount) / float64(h.ProbeCount) * 100 + } + out = append(out, st) + } + return out +} + +// GetWebsiteDailyAggregates 站点维度按日合并(多 URL 汇总到同一天) +func (s *Storage) GetWebsiteDailyAggregates(websiteID string, since time.Time) []models.DailyStats { + type aggRow struct { + D time.Time `gorm:"column:d"` + Pc int64 `gorm:"column:pc"` + Uc int64 `gorm:"column:uc"` + Ls int64 `gorm:"column:ls"` + } + var rows []aggRow + sinceDay := time.Date(since.Year(), since.Month(), since.Day(), 0, 0, 0, 0, since.Location()) + s.db.Raw(` +SELECT stat_date AS d, SUM(probe_count) AS pc, SUM(up_count) AS uc, SUM(latency_sum) AS ls +FROM monitor_probe_day +WHERE website_id = ? AND stat_date >= ? +GROUP BY stat_date +ORDER BY stat_date ASC +`, websiteID, sinceDay.Format("2006-01-02")).Scan(&rows) + + out := make([]models.DailyStats, 0, len(rows)) + for _, r := range rows { + st := models.DailyStats{ + Date: r.D, + TotalCount: int(r.Pc), + UpCount: int(r.Uc), + } + if r.Pc > 0 { + st.AvgLatency = r.Ls / r.Pc + st.Uptime = float64(r.Uc) / float64(r.Pc) * 100 + } + out = append(out, st) + } + return out +} + +// GetWebsiteDayProbeTotals 整站在日期范围内探测次数汇总(用于 90 天可用率) +func (s *Storage) GetWebsiteDayProbeTotals(websiteID string, since time.Time) (probeTotal int64, upTotal int64) { + sinceDay := time.Date(since.Year(), since.Month(), since.Day(), 0, 0, 0, 0, since.Location()) + type row struct { + P int64 + U int64 + } + var r row + s.db.Model(&MonitorProbeDay{}). + Select("COALESCE(SUM(probe_count),0), COALESCE(SUM(up_count),0)"). + Where("website_id = ? AND stat_date >= ?", websiteID, sinceDay.Format("2006-01-02")). + Scan(&r) + return r.P, r.U +} + +func (s *Storage) purgeProbeRollups() { + cfg := config.GetConfig() + now := time.Now() + dayCutoff := now.AddDate(0, 0, -cfg.Monitor.HistoryDays).Truncate(24 * time.Hour) + if err := s.db.Where("stat_date < ?", dayCutoff.Format("2006-01-02")).Delete(&MonitorProbeDay{}).Error; err != nil { + log.Printf("清理 monitor_probe_day: %v", err) + } + // 小时图保留约 10 天(覆盖 7 天曲线 + 余量) + hourCutoff := now.Add(-10 * 24 * time.Hour).Truncate(time.Hour) + if err := s.db.Where("hour_at < ?", hourCutoff).Delete(&MonitorProbeHour{}).Error; err != nil { + log.Printf("清理 monitor_probe_hour: %v", err) + } +} diff --git a/mengyaping-backend/storage/storage.go b/mengyaping-backend/storage/storage.go index d4737ac..dd4acf7 100644 --- a/mengyaping-backend/storage/storage.go +++ b/mengyaping-backend/storage/storage.go @@ -1,302 +1,365 @@ -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) - s.migrateWebsiteGroups() -} - -// migrateWebsiteGroups 将旧的单分组字段迁移到多分组数组 -func (s *Storage) migrateWebsiteGroups() { - migrated := false - for i := range s.websites { - w := &s.websites[i] - if len(w.Groups) == 0 && w.Group != "" { - w.Groups = []string{w.Group} - w.Group = "" - migrated = true - } - } - if migrated { - s.saveWebsites() - } -} - -// 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() -} +package storage + +import ( + "encoding/json" + "fmt" + "log" + "os" + "strings" + "sync" + "time" + + "gorm.io/datatypes" + "gorm.io/driver/mysql" + "gorm.io/gorm" + "gorm.io/gorm/logger" + + "mengyaping-backend/config" + "mengyaping-backend/models" +) + +// Storage 基于 MySQL 的持久化(站点 / 分类 / 检测记录) +type Storage struct { + db *gorm.DB +} + +var ( + store *Storage + once sync.Once +) + +// GetStorage 初始化数据库连接并迁移表结构 +func GetStorage() *Storage { + once.Do(func() { + cfg := config.GetConfig() + dsn := cfg.DatabaseDSN() + + gormLog := logger.Default.LogMode(logger.Warn) + if os.Getenv("DB_DEBUG") == "1" { + gormLog = logger.Default.LogMode(logger.Info) + } + + db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{ + Logger: gormLog, + }) + if err != nil { + log.Fatalf("MySQL 连接失败: %v(请检查 DB_* / DB_DSN 环境变量)", err) + } + sqlDB, err := db.DB() + if err != nil { + log.Fatalf("MySQL sqlDB: %v", err) + } + sqlDB.SetMaxIdleConns(8) + sqlDB.SetMaxOpenConns(40) + sqlDB.SetConnMaxLifetime(time.Hour) + + store = &Storage{db: db} + if err := store.migrate(); err != nil { + log.Fatalf("数据库迁移失败: %v", err) + } + store.seedAdminTokenKV() + store.loadAndSyncAppConfig() + store.seedDefaultGroups() + log.Println("MySQL 存储已就绪: " + cfg.Database.Database + "@" + cfg.Database.Host) + }) + return store +} + +func (s *Storage) migrate() error { + err := s.db.AutoMigrate( + &MonitorGroup{}, + &MonitorWebsite{}, + &MonitorWebsiteURL{}, + &MonitorWebsiteGroup{}, + &MonitorProbeLatest{}, + &MonitorProbeHour{}, + &MonitorProbeDay{}, + &MonitorKV{}, + ) + if err != nil { + return err + } + // 旧版逐条 monitor_checks 表体积极大,已弃用 + if s.db.Migrator().HasTable("monitor_checks") { + if err := s.db.Migrator().DropTable("monitor_checks"); err != nil { + log.Printf("删除旧表 monitor_checks: %v(可手工 DROP)", err) + } else { + log.Println("已删除旧表 monitor_checks") + } + } + return nil +} + +func (s *Storage) seedDefaultGroups() { + var n int64 + s.db.Model(&MonitorGroup{}).Count(&n) + if n > 0 { + return + } + order := 0 + for _, g := range models.DefaultGroups { + order++ + s.db.Create(&MonitorGroup{ + ID: g.ID, + Name: g.Name, + SortOrder: order, + }) + } + log.Println("已写入默认分类 monitor_groups") +} + +func ipsToJSON(ips []string) []byte { + if ips == nil { + ips = []string{} + } + b, err := json.Marshal(ips) + if err != nil { + return []byte("[]") + } + return b +} + +func jsonToIPs(raw []byte) []string { + if len(raw) == 0 { + return nil + } + var ips []string + if err := json.Unmarshal(raw, &ips); err != nil { + return nil + } + return ips +} + +func rowToWebsite(w *MonitorWebsite) models.Website { + groups := make([]string, 0, len(w.GroupLinks)) + for _, l := range w.GroupLinks { + groups = append(groups, l.GroupID) + } + if len(groups) == 0 && w.LegacyGroup != "" { + groups = []string{w.LegacyGroup} + } + urls := make([]models.URLInfo, 0, len(w.URLs)) + for _, u := range w.URLs { + urls = append(urls, models.URLInfo{ + ID: u.URLID, + URL: u.URL, + Remark: u.Remark, + }) + } + return models.Website{ + ID: w.ID, + Name: w.Name, + Groups: groups, + Group: "", + URLs: urls, + IPAddresses: jsonToIPs(w.IPAddresses), + Favicon: w.Favicon, + Title: w.Title, + CreatedAt: w.CreatedAt, + UpdatedAt: w.UpdatedAt, + } +} + +func websiteToRow(w *models.Website) *MonitorWebsite { + return &MonitorWebsite{ + ID: w.ID, + Name: w.Name, + LegacyGroup: "", + Favicon: w.Favicon, + Title: w.Title, + IPAddresses: datatypes.JSON(ipsToJSON(w.IPAddresses)), + CreatedAt: w.CreatedAt, + UpdatedAt: w.UpdatedAt, + } +} + +func (s *Storage) listWebsiteModels() ([]models.Website, error) { + var rows []MonitorWebsite + if err := s.db.Preload("URLs", func(db *gorm.DB) *gorm.DB { + return db.Order("sort_order ASC, id ASC") + }).Preload("GroupLinks").Order("created_at ASC").Find(&rows).Error; err != nil { + return nil, err + } + out := make([]models.Website, len(rows)) + for i := range rows { + out[i] = rowToWebsite(&rows[i]) + } + return out, nil +} + +// GetWebsites 获取所有网站 +func (s *Storage) GetWebsites() []models.Website { + list, err := s.listWebsiteModels() + if err != nil { + log.Printf("GetWebsites: %v", err) + return nil + } + return list +} + +// GetWebsite 获取单个网站 +func (s *Storage) GetWebsite(id string) *models.Website { + var w MonitorWebsite + if err := s.db.Preload("URLs", func(db *gorm.DB) *gorm.DB { + return db.Order("sort_order ASC, id ASC") + }).Preload("GroupLinks").First(&w, "id = ?", id).Error; err != nil { + return nil + } + m := rowToWebsite(&w) + return &m +} + +func (s *Storage) persistWebsiteFull(w models.Website) error { + return s.db.Transaction(func(tx *gorm.DB) error { + row := websiteToRow(&w) + if err := tx.Save(row).Error; err != nil { + return err + } + if err := tx.Where("website_id = ?", w.ID).Delete(&MonitorWebsiteURL{}).Error; err != nil { + return err + } + if err := tx.Where("website_id = ?", w.ID).Delete(&MonitorWebsiteGroup{}).Error; err != nil { + return err + } + for i, u := range w.URLs { + if err := tx.Create(&MonitorWebsiteURL{ + WebsiteID: w.ID, + URLID: u.ID, + URL: u.URL, + Remark: u.Remark, + SortOrder: i, + }).Error; err != nil { + return err + } + } + for _, gid := range w.Groups { + if err := tx.Create(&MonitorWebsiteGroup{WebsiteID: w.ID, GroupID: gid}).Error; err != nil { + return err + } + } + return nil + }) +} + +// AddWebsite 添加网站 +func (s *Storage) AddWebsite(website models.Website) error { + return s.persistWebsiteFull(website) +} + +// UpdateWebsite 更新网站 +func (s *Storage) UpdateWebsite(website models.Website) error { + return s.persistWebsiteFull(website) +} + +// DeleteWebsite 删除网站 +func (s *Storage) DeleteWebsite(id string) error { + return s.db.Transaction(func(tx *gorm.DB) error { + _ = tx.Where("website_id = ?", id).Delete(&MonitorProbeLatest{}) + _ = tx.Where("website_id = ?", id).Delete(&MonitorProbeHour{}) + _ = tx.Where("website_id = ?", id).Delete(&MonitorProbeDay{}) + _ = tx.Where("website_id = ?", id).Delete(&MonitorWebsiteURL{}) + _ = tx.Where("website_id = ?", id).Delete(&MonitorWebsiteGroup{}) + return tx.Where("id = ?", id).Delete(&MonitorWebsite{}).Error + }) +} + +// GetGroups 获取所有分组 +func (s *Storage) GetGroups() []models.Group { + var rows []MonitorGroup + s.db.Order("sort_order ASC, id ASC").Find(&rows) + out := make([]models.Group, len(rows)) + for i, r := range rows { + out[i] = models.Group{ID: r.ID, Name: r.Name} + } + return out +} + +// AddGroup 添加分组 +func (s *Storage) AddGroup(group models.Group) error { + var n int64 + s.db.Model(&MonitorGroup{}).Count(&n) + return s.db.Create(&MonitorGroup{ + ID: group.ID, + Name: group.Name, + SortOrder: int(n) + 1, + }).Error +} + +// GetGroupByID 按 ID 获取分组(无则 nil) +func (s *Storage) GetGroupByID(id string) *models.Group { + var r MonitorGroup + if err := s.db.First(&r, "id = ?", id).Error; err != nil { + return nil + } + g := models.Group{ID: r.ID, Name: r.Name} + return &g +} + +// UpdateGroup 更新分组显示名称(ID 不变) +func (s *Storage) UpdateGroup(id, name string) error { + name = strings.TrimSpace(name) + if name == "" { + return fmt.Errorf("分类名称不能为空") + } + res := s.db.Model(&MonitorGroup{}).Where("id = ?", id).Update("name", name) + if res.Error != nil { + return res.Error + } + if res.RowsAffected == 0 { + return fmt.Errorf("分类不存在") + } + return nil +} + +// DeleteGroup 删除分组;从所有网站中移除该分类;若网站无剩余分类则归入删除后列表中的第一个分类。至少保留一个分类。 +func (s *Storage) DeleteGroup(id string) error { + var total int64 + s.db.Model(&MonitorGroup{}).Count(&total) + if total <= 1 { + return fmt.Errorf("至少保留一个分类") + } + + var remain []MonitorGroup + s.db.Where("id != ?", id).Order("sort_order ASC, id ASC").Find(&remain) + if len(remain) == 0 { + return fmt.Errorf("至少保留一个分类") + } + fallbackID := remain[0].ID + + var sites []MonitorWebsite + if err := s.db.Preload("GroupLinks").Find(&sites).Error; err != nil { + return err + } + + return s.db.Transaction(func(tx *gorm.DB) error { + for i := range sites { + next := make([]string, 0) + for _, l := range sites[i].GroupLinks { + if l.GroupID != id { + next = append(next, l.GroupID) + } + } + if len(next) == 0 { + next = []string{fallbackID} + } + if err := tx.Where("website_id = ?", sites[i].ID).Delete(&MonitorWebsiteGroup{}).Error; err != nil { + return err + } + for _, gid := range next { + if err := tx.Create(&MonitorWebsiteGroup{WebsiteID: sites[i].ID, GroupID: gid}).Error; err != nil { + return err + } + } + } + if err := tx.Delete(&MonitorGroup{}, "id = ?", id).Error; err != nil { + return err + } + return nil + }) +} + +// SaveAll 周期结束时清理过期汇总分区(日表按 history_days,小时表保留约 10 天) +func (s *Storage) SaveAll() error { + s.purgeProbeRollups() + return nil +} diff --git a/mengyaping-backend/utils/intranet.go b/mengyaping-backend/utils/intranet.go new file mode 100644 index 0000000..2fcc6b2 --- /dev/null +++ b/mengyaping-backend/utils/intranet.go @@ -0,0 +1,37 @@ +package utils + +import ( + "net" + "net/url" +) + +// IsIntranetURL 判断监控地址的主机是否为内网 / 本机 IP(直填 IP 的虚拟局域网站点)。 +// 仅识别主机名为 IPv4/IPv6 字面量的情况;公网域名不在此列。 +func IsIntranetURL(raw string) bool { + u, err := url.Parse(raw) + if err != nil { + return false + } + host := u.Hostname() + if host == "" { + return false + } + ip := net.ParseIP(host) + if ip == nil { + return false + } + return isIntranetIP(ip) +} + +func isIntranetIP(ip net.IP) bool { + if ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast() { + return true + } + // Go 1.17+ IsPrivate 已含 RFC1918、CGNAT 100.64/10、ULA 等;再兜底部分实现差异 + if v4 := ip.To4(); v4 != nil { + if v4[0] == 100 && v4[1] >= 64 && v4[1] <= 127 { + return true + } + } + return false +} diff --git a/mengyaping-backend/utils/intranet_test.go b/mengyaping-backend/utils/intranet_test.go new file mode 100644 index 0000000..3259192 --- /dev/null +++ b/mengyaping-backend/utils/intranet_test.go @@ -0,0 +1,24 @@ +package utils + +import "testing" + +func TestIsIntranetURL(t *testing.T) { + cases := []struct { + raw string + want bool + }{ + {"http://10.0.0.233:8080/path", true}, + {"https://192.168.1.1/", true}, + {"http://127.0.0.1:3000", true}, + {"http://[fd00::1]/", true}, + {"https://1.1.1.1/", false}, + {"https://example.com/", false}, + {"http://nas.local:8080", false}, + } + for _, tc := range cases { + got := IsIntranetURL(tc.raw) + if got != tc.want { + t.Fatalf("%q: got %v want %v", tc.raw, got, tc.want) + } + } +} diff --git a/mengyaping-frontend/package-lock.json b/mengyaping-frontend/package-lock.json index a3edba7..531d6b3 100644 --- a/mengyaping-frontend/package-lock.json +++ b/mengyaping-frontend/package-lock.json @@ -10,7 +10,8 @@ "dependencies": { "@tailwindcss/vite": "^4.1.18", "react": "^19.2.0", - "react-dom": "^19.2.0" + "react-dom": "^19.2.0", + "react-router-dom": "^7.14.0" }, "devDependencies": { "@eslint/js": "^9.39.1", @@ -58,7 +59,6 @@ "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", @@ -1633,7 +1633,6 @@ "integrity": "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -1675,7 +1674,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1818,7 +1816,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -1915,6 +1912,19 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2061,7 +2071,6 @@ "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", @@ -3030,7 +3039,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -3057,7 +3065,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -3099,7 +3106,6 @@ "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" } @@ -3126,6 +3132,44 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.14.0.tgz", + "integrity": "sha512-m/xR9N4LQLmAS0ZhkY2nkPA1N7gQ5TUVa5n8TgANuDTARbn1gt+zLPXEm7W0XDTbrQ2AJSJKhoa6yx1D8BcpxQ==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.14.0.tgz", + "integrity": "sha512-2G3ajSVSZMEtmTjIklRWlNvo8wICEpLihfD/0YMDxbWK2UyP5EGfnoIn9AIQGnF3G/FX0MRbHXdFcD+rL1ZreQ==", + "license": "MIT", + "dependencies": { + "react-router": "7.14.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -3196,6 +3240,12 @@ "semver": "bin/semver.js" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -3348,7 +3398,6 @@ "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", @@ -3470,7 +3519,6 @@ "integrity": "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/mengyaping-frontend/package.json b/mengyaping-frontend/package.json index a10477c..8852d80 100644 --- a/mengyaping-frontend/package.json +++ b/mengyaping-frontend/package.json @@ -14,7 +14,8 @@ "dependencies": { "@tailwindcss/vite": "^4.1.18", "react": "^19.2.0", - "react-dom": "^19.2.0" + "react-dom": "^19.2.0", + "react-router-dom": "^7.14.0" }, "devDependencies": { "@eslint/js": "^9.39.1", diff --git a/mengyaping-frontend/src/App.jsx b/mengyaping-frontend/src/App.jsx index 9a8cf16..157ab0e 100644 --- a/mengyaping-frontend/src/App.jsx +++ b/mengyaping-frontend/src/App.jsx @@ -1,7 +1,18 @@ -import Dashboard from './pages/Dashboard' +import { Routes, Route, Navigate } from 'react-router-dom' +import RandomBackdrop from './components/RandomBackdrop' +import PublicDashboard from './pages/PublicDashboard' +import AdminPanel from './pages/AdminPanel' function App() { - return + return ( + + + } /> + } /> + } /> + + + ) } export default App diff --git a/mengyaping-frontend/src/components/AdminGroupsPanel.jsx b/mengyaping-frontend/src/components/AdminGroupsPanel.jsx new file mode 100644 index 0000000..fff4157 --- /dev/null +++ b/mengyaping-frontend/src/components/AdminGroupsPanel.jsx @@ -0,0 +1,179 @@ +import { useState, useCallback, useEffect } from 'react'; +import { getGroups, addGroup, updateGroup, deleteGroup } from '../services/api'; +import { GROUPS_CHANGED_EVENT } from '../constants/events'; + +function notifyGroupsChanged() { + window.dispatchEvent(new CustomEvent(GROUPS_CHANGED_EVENT)); +} + +export default function AdminGroupsPanel() { + const [groups, setGroups] = useState(null); + const [loading, setLoading] = useState(true); + const [err, setErr] = useState(''); + const [newName, setNewName] = useState(''); + const [adding, setAdding] = useState(false); + + const [editingId, setEditingId] = useState(null); + const [editName, setEditName] = useState(''); + const [saveBusy, setSaveBusy] = useState(false); + + const load = useCallback(async () => { + setLoading(true); + setErr(''); + try { + const data = await getGroups(); + setGroups(data || []); + } catch (e) { + setErr(e.message || '加载分类失败'); + } finally { + setLoading(false); + } + }, []); + + useEffect(() => { + load(); + }, [load]); + + async function handleAdd(e) { + e.preventDefault(); + const name = newName.trim(); + if (!name) return; + setAdding(true); + setErr(''); + try { + await addGroup(name); + setNewName(''); + await load(); + notifyGroupsChanged(); + } catch (e) { + setErr(e.message || '添加失败'); + } finally { + setAdding(false); + } + } + + function startEdit(g) { + setEditingId(g.id); + setEditName(g.name); + } + + function cancelEdit() { + setEditingId(null); + setEditName(''); + } + + async function handleSaveEdit(e) { + e.preventDefault(); + if (!editingId) return; + const name = editName.trim(); + if (!name) return; + setSaveBusy(true); + setErr(''); + try { + await updateGroup(editingId, { name }); + cancelEdit(); + await load(); + notifyGroupsChanged(); + } catch (e) { + setErr(e.message || '保存失败'); + } finally { + setSaveBusy(false); + } + } + + async function handleDelete(g) { + if (!confirm(`确定删除分类「${g.name}」?\n使用该分类的站点将自动归入其他分类。`)) { + return; + } + setErr(''); + try { + await deleteGroup(g.id); + await load(); + notifyGroupsChanged(); + } catch (e) { + setErr(e.message || '删除失败'); + } + } + + return ( +
+

网站分类

+

+ 增删改分类后,列表与筛选会同步更新;删除分类时,原归属站点会合并到剩余的第一个分类。 +

+ +
+ setNewName(e.target.value)} + placeholder="新分类名称" + className="flex-1 px-3 py-2 rounded-lg border border-gray-200 text-sm focus:ring-2 focus:ring-emerald-500 outline-none" + /> + +
+ + {loading && !groups ? ( +

加载中…

+ ) : ( + + )} + + {err ?

{err}

: null} +
+ ); +} diff --git a/mengyaping-frontend/src/components/RandomBackdrop.jsx b/mengyaping-frontend/src/components/RandomBackdrop.jsx new file mode 100644 index 0000000..b05100f --- /dev/null +++ b/mengyaping-frontend/src/components/RandomBackdrop.jsx @@ -0,0 +1,57 @@ +import { useState, useEffect } from 'react'; + +const RANDOM_BG_API = + 'https://randbg.api.smyhub.com/api/random?format=json'; + +/** 高斯模糊强度:10%(相对参考最大模糊半径,便于以后微调) */ +const BLUR_PERCENT = 10; +const BLUR_REFERENCE_PX = 24; + +export default function RandomBackdrop({ children }) { + const [imageUrl, setImageUrl] = useState(null); + + useEffect(() => { + let cancelled = false; + (async () => { + try { + const res = await fetch(RANDOM_BG_API); + const data = await res.json(); + const url = data?.url; + if (!cancelled && typeof url === 'string' && url.length > 0) { + setImageUrl(url); + } + } catch { + /* 失败时仅用遮罩渐变,不阻塞页面 */ + } + })(); + return () => { + cancelled = true; + }; + }, []); + + const blurPx = (BLUR_REFERENCE_PX * BLUR_PERCENT) / 100; + + return ( +
+
+ {imageUrl ? ( +
+ ) : null} + {/* 浅色罩层:保证前景可读,并保留原站绿色系氛围 */} +
+
+ +
{children}
+
+ ); +} diff --git a/mengyaping-frontend/src/components/StatsCard.jsx b/mengyaping-frontend/src/components/StatsCard.jsx index 7503970..1a64a36 100644 --- a/mengyaping-frontend/src/components/StatsCard.jsx +++ b/mengyaping-frontend/src/components/StatsCard.jsx @@ -133,12 +133,6 @@ export default function StatsCard({ websites }) {
-
- - - - 每5分钟检测一次 -
); diff --git a/mengyaping-frontend/src/components/WebsiteCard.jsx b/mengyaping-frontend/src/components/WebsiteCard.jsx index 3f463ca..001a62f 100644 --- a/mengyaping-frontend/src/components/WebsiteCard.jsx +++ b/mengyaping-frontend/src/components/WebsiteCard.jsx @@ -3,6 +3,10 @@ import { formatLatency, formatUptime, formatTime, getStatusColor, getUptimeColor const HISTORY_DAYS = 90; +const INTRANET_FALLBACK_ICON = + 'https://api.iconify.design/mdi/lan-connect.svg?color=%23059669'; +const FAVICON_PROXY_BASE = 'https://favicon.smyhub.com/api/favicon?url='; + function getBarColor(bar) { if (bar.uptime === null) return 'bg-gray-200'; if (bar.uptime >= 99) return 'bg-emerald-400'; @@ -19,7 +23,7 @@ function getBarHoverColor(bar) { return 'hover:bg-red-600'; } -export default function WebsiteCard({ website, onRefresh, onEdit, onDelete }) { +export default function WebsiteCard({ website, onRefresh, onEdit, onDelete, readOnly = false }) { const [expanded, setExpanded] = useState(false); const [hoveredBar, setHoveredBar] = useState(null); @@ -28,6 +32,10 @@ export default function WebsiteCard({ website, onRefresh, onEdit, onDelete }) { 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; + const isIntranet = website.is_intranet === true; + const cardIconSrc = isIntranet + ? (website.website?.favicon || INTRANET_FALLBACK_ICON) + : (primaryUrl ? `${FAVICON_PROXY_BASE}${encodeURIComponent(primaryUrl)}` : null); const dailyBars = useMemo(() => { const bars = []; @@ -81,9 +89,9 @@ export default function WebsiteCard({ website, onRefresh, onEdit, onDelete }) {
- {primaryUrl ? ( + {cardIconSrc ? ( { @@ -93,8 +101,8 @@ export default function WebsiteCard({ website, onRefresh, onEdit, onDelete }) { /> ) : null} {website.website?.name?.[0] || '?'} @@ -172,7 +180,7 @@ export default function WebsiteCard({ website, onRefresh, onEdit, onDelete }) {
{/* IP 地址(IPv4 / IPv6 分开显示) */} - {website.website?.ip_addresses?.length > 0 && (() => { + {!isIntranet && website.website?.ip_addresses?.length > 0 && (() => { const ipv4 = website.website.ip_addresses.filter(ip => !ip.includes(':')); const ipv6 = website.website.ip_addresses.filter(ip => ip.includes(':')); return (ipv4.length > 0 || ipv6.length > 0) && ( @@ -222,42 +230,47 @@ export default function WebsiteCard({ website, onRefresh, onEdit, onDelete }) { ))}
- {/* 操作按钮 */} -
+ {/* 操作按钮(仅管理后台) */} +
{formatTime(website.last_checked)} -
- - - -
+ {!readOnly && ( +
+ + + +
+ )}
)} diff --git a/mengyaping-frontend/src/constants/events.js b/mengyaping-frontend/src/constants/events.js new file mode 100644 index 0000000..e256d81 --- /dev/null +++ b/mengyaping-frontend/src/constants/events.js @@ -0,0 +1,2 @@ +/** 分类增删改后广播,便于监控页立即刷新分组与站点 */ +export const GROUPS_CHANGED_EVENT = 'mengyaping:groups-changed'; diff --git a/mengyaping-frontend/src/main.jsx b/mengyaping-frontend/src/main.jsx index fba4204..4233fff 100644 --- a/mengyaping-frontend/src/main.jsx +++ b/mengyaping-frontend/src/main.jsx @@ -1,5 +1,6 @@ import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' +import { BrowserRouter } from 'react-router-dom' import './index.css' import App from './App.jsx' @@ -13,6 +14,8 @@ if (import.meta.env.PROD && 'serviceWorker' in navigator) { createRoot(document.getElementById('root')).render( - + + + , ) diff --git a/mengyaping-frontend/src/pages/AdminPanel.jsx b/mengyaping-frontend/src/pages/AdminPanel.jsx new file mode 100644 index 0000000..aaed5bf --- /dev/null +++ b/mengyaping-frontend/src/pages/AdminPanel.jsx @@ -0,0 +1,206 @@ +import { useState, useCallback, useEffect } from 'react'; +import { Link } from 'react-router-dom'; +import Dashboard from './Dashboard'; +import { + verifyAdminToken, + setStoredAdminToken, + getStoredAdminToken, + getAdminConfig, + updateAdminMonitorConfig, +} from '../services/api'; +import AdminGroupsPanel from '../components/AdminGroupsPanel'; + +const INTERVAL_OPTIONS = [ + { minutes: 10, label: '10 分钟' }, + { minutes: 20, label: '20 分钟' }, + { minutes: 30, label: '30 分钟' }, + { minutes: 60, label: '1 小时' }, + { minutes: 180, label: '3 小时' }, + { minutes: 360, label: '6 小时' }, +]; + +function AdminScheduleToolbar({ onLogout }) { + const [intervalMinutes, setIntervalMinutes] = useState(30); + const [loading, setLoading] = useState(true); + const [err, setErr] = useState(''); + const [saving, setSaving] = useState(false); + + const load = useCallback(async () => { + setLoading(true); + setErr(''); + try { + const c = await getAdminConfig(); + setIntervalMinutes(c.interval_minutes); + } catch (e) { + setErr(e.message || '加载配置失败'); + } finally { + setLoading(false); + } + }, []); + + useEffect(() => { + load(); + }, [load]); + + async function handleSave(e) { + e.preventDefault(); + setSaving(true); + setErr(''); + try { + await updateAdminMonitorConfig({ interval_minutes: intervalMinutes }); + await load(); + } catch (e) { + setErr(e.message || '保存失败'); + } finally { + setSaving(false); + } + } + + return ( +
+
+
+

全局检测周期

+

+ 所有站点共用同一轮询间隔,保存后立即调整后台定时器(仍会先跑完上一轮逻辑)。 +

+
+
+ + + 公开页 + +
+
+ + {loading ? ( +

加载中…

+ ) : ( +
+ + +
+ )} + {err ?

{err}

: null} +
+ ); +} + +export default function AdminPanel() { + const [authed, setAuthed] = useState(() => !!getStoredAdminToken()); + const [tokenInput, setTokenInput] = useState(''); + const [loginErr, setLoginErr] = useState(''); + const [loggingIn, setLoggingIn] = useState(false); + + async function handleLogin(e) { + e.preventDefault(); + const t = tokenInput.trim(); + if (!t) { + setLoginErr('请输入管理员令牌'); + return; + } + setLoggingIn(true); + setLoginErr(''); + try { + await verifyAdminToken(t); + setStoredAdminToken(t); + setAuthed(true); + setTokenInput(''); + } catch (err) { + setLoginErr(err.message || '登录失败'); + } finally { + setLoggingIn(false); + } + } + + function handleLogout() { + setStoredAdminToken(null); + setAuthed(false); + } + + if (!authed) { + return ( +
+
+
+
+ +
+
+

萌芽Ping · 管理后台

+

请输入与服务端 ADMIN_TOKEN 一致的口令

+
+
+
+ + {loginErr ?

{loginErr}

: null} + +
+

+ + ← 返回公开页 + +

+
+
+ ); + } + + return ( + + +
+ +
+ + } + /> + ); +} diff --git a/mengyaping-frontend/src/pages/Dashboard.jsx b/mengyaping-frontend/src/pages/Dashboard.jsx index 29cef3c..142321d 100644 --- a/mengyaping-frontend/src/pages/Dashboard.jsx +++ b/mengyaping-frontend/src/pages/Dashboard.jsx @@ -1,23 +1,107 @@ -import { useState, useCallback, useMemo } from 'react'; +import { useState, useCallback, useMemo, useRef, useEffect } from 'react'; +import { Link, useNavigate } from 'react-router-dom'; 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'; +import { + getWebsites, + deleteWebsite, + checkWebsiteNow, + getGroups, + getMonitorSettings, + verifyAdminToken, + setStoredAdminToken, +} from '../services/api'; +import { GROUPS_CHANGED_EVENT } from '../constants/events'; + +const LOGO_TAP_WINDOW_MS = 2000; +const LOGO_TAPS_TO_OPEN = 5; + +export default function Dashboard({ readOnly = false, topSlot = null }) { + const navigate = useNavigate(); + const logoTapCountRef = useRef(0); + const logoTapResetTimerRef = useRef(null); -export default function Dashboard() { const [modalOpen, setModalOpen] = useState(false); const [editData, setEditData] = useState(null); const [selectedGroup, setSelectedGroup] = useState('all'); const [searchTerm, setSearchTerm] = useState(''); + const [adminGateOpen, setAdminGateOpen] = useState(false); + const [adminGateToken, setAdminGateToken] = useState(''); + const [adminGateErr, setAdminGateErr] = useState(''); + const [adminGateBusy, setAdminGateBusy] = useState(false); + + useEffect(() => { + return () => { + const t = logoTapResetTimerRef.current; + if (t != null) { + clearTimeout(t); + } + }; + }, []); + + const handleLogoTap = () => { + if (!readOnly) return; + if (logoTapResetTimerRef.current != null) { + clearTimeout(logoTapResetTimerRef.current); + } + logoTapCountRef.current += 1; + if (logoTapCountRef.current >= LOGO_TAPS_TO_OPEN) { + logoTapCountRef.current = 0; + logoTapResetTimerRef.current = null; + setAdminGateOpen(true); + setAdminGateErr(''); + setAdminGateToken(''); + return; + } + logoTapResetTimerRef.current = setTimeout(() => { + logoTapCountRef.current = 0; + logoTapResetTimerRef.current = null; + }, LOGO_TAP_WINDOW_MS); + }; + + const handleAdminGateSubmit = async (e) => { + e.preventDefault(); + const t = adminGateToken.trim(); + if (!t) { + setAdminGateErr('请输入令牌'); + return; + } + setAdminGateBusy(true); + setAdminGateErr(''); + try { + await verifyAdminToken(t); + setStoredAdminToken(t); + setAdminGateOpen(false); + navigate('/admin'); + } catch (err) { + setAdminGateErr(err.message || '验证失败'); + } finally { + setAdminGateBusy(false); + } + }; + // 获取网站数据 const fetchData = useCallback(() => getWebsites(), []); const { data: websites, loading, error, refresh } = useAutoRefresh(fetchData, 30000); // 获取分组数据 const fetchGroups = useCallback(() => getGroups(), []); - const { data: groups } = useAutoRefresh(fetchGroups, 60000); + const { data: groups, refresh: refreshGroups } = useAutoRefresh(fetchGroups, 60000); + + const fetchMonitorSettings = useCallback(() => getMonitorSettings(), []); + const { data: monitorSettings } = useAutoRefresh(fetchMonitorSettings, 120000); + + useEffect(() => { + const onGroupsChanged = () => { + refreshGroups(); + refresh(); + }; + window.addEventListener(GROUPS_CHANGED_EVENT, onGroupsChanged); + return () => window.removeEventListener(GROUPS_CHANGED_EVENT, onGroupsChanged); + }, [refreshGroups, refresh]); // 处理添加网站 const handleAdd = () => { @@ -102,44 +186,67 @@ export default function Dashboard() { }; return ( -
+
{/* 顶部导航 */}
{/* Logo */}
-
+
+

萌芽Ping

-

网站监控面板

+ {monitorSettings?.interval_minutes ? ( +

+ 检测周期:每 {monitorSettings.interval_minutes} 分钟 +

+ ) : null}
- - {/* 添加按钮 */} - + +
+ {readOnly ? null : ( + <> + + 公开页 + + + + )} +
{/* 主内容区 */}
+ {topSlot ?
{topSlot}
: null} + {/* 统计概览 */}
@@ -225,14 +332,18 @@ export default function Dashboard() {

{searchTerm ? '没有找到匹配的网站' : '还没有添加任何监控网站'}

- {!searchTerm && ( + {!searchTerm && !readOnly && ( )} + {!searchTerm && readOnly && ( +

站点由管理员在「管理后台」维护

+ )}
) : (
@@ -250,9 +361,10 @@ export default function Dashboard() { ))}
@@ -269,12 +381,61 @@ export default function Dashboard() { {/* 添加/编辑弹窗 */} - setModalOpen(false)} - onSuccess={refresh} - editData={editData} - /> + {!readOnly && ( + setModalOpen(false)} + onSuccess={refresh} + editData={editData} + /> + )} + + {adminGateOpen && ( +
setAdminGateOpen(false)} + > +
e.stopPropagation()} + > +

+ 管理员验证 +

+

请输入与服务端配置一致的管理员令牌

+
+ setAdminGateToken(e.target.value)} + className="w-full px-3 py-2 rounded-lg border border-gray-200 text-sm focus:ring-2 focus:ring-emerald-500 outline-none" + placeholder="admin_token" + /> + {adminGateErr ?

{adminGateErr}

: null} +
+ + +
+
+
+
+ )}
); } diff --git a/mengyaping-frontend/src/pages/PublicDashboard.jsx b/mengyaping-frontend/src/pages/PublicDashboard.jsx new file mode 100644 index 0000000..c79ffd3 --- /dev/null +++ b/mengyaping-frontend/src/pages/PublicDashboard.jsx @@ -0,0 +1,5 @@ +import Dashboard from './Dashboard'; + +export default function PublicDashboard() { + return ; +} diff --git a/mengyaping-frontend/src/services/api.js b/mengyaping-frontend/src/services/api.js index 6ff0981..e0dbc0c 100644 --- a/mengyaping-frontend/src/services/api.js +++ b/mengyaping-frontend/src/services/api.js @@ -1,10 +1,29 @@ // API服务 // 根据环境变量判断使用哪个 API 地址 -const API_BASE = import.meta.env.PROD +const API_BASE = import.meta.env.PROD ? 'https://ping.api.shumengya.top/api' // 生产环境 : 'http://localhost:8080/api'; // 开发环境 -// 通用请求方法 +const ADMIN_TOKEN_KEY = 'mengyaping_admin_token'; + +export function getStoredAdminToken() { + return localStorage.getItem(ADMIN_TOKEN_KEY); +} + +export function setStoredAdminToken(token) { + if (token) { + localStorage.setItem(ADMIN_TOKEN_KEY, token); + } else { + localStorage.removeItem(ADMIN_TOKEN_KEY); + } +} + +async function parseJsonResponse(response) { + const data = await response.json(); + return data; +} + +// 通用请求(公开读接口) async function request(url, options = {}) { const response = await fetch(`${API_BASE}${url}`, { headers: { @@ -13,16 +32,62 @@ async function request(url, options = {}) { }, ...options, }); - - const data = await response.json(); - + + const data = await parseJsonResponse(response); + if (data.code !== 0) { throw new Error(data.message || '请求失败'); } - + return data.data; } +async function adminRequest(url, options = {}) { + const token = getStoredAdminToken(); + if (!token) { + throw new Error('请先登录管理后台'); + } + return request(url, { + ...options, + headers: { + ...options.headers, + 'X-Admin-Token': token, + }, + }); +} + +/** 校验管理员令牌(登录前调用,不落库) */ +export async function verifyAdminToken(token) { + const response = await fetch(`${API_BASE}/admin/config`, { + headers: { + 'Content-Type': 'application/json', + 'X-Admin-Token': token, + }, + }); + const data = await parseJsonResponse(response); + if (data.code !== 0) { + throw new Error(data.message || '令牌无效'); + } + return data.data; +} + +// 公开:全局检测周期 +export async function getMonitorSettings() { + return request('/monitor/settings'); +} + +// 管理端:完整监控配置 +export async function getAdminConfig() { + return adminRequest('/admin/config'); +} + +export async function updateAdminMonitorConfig(payload) { + return adminRequest('/admin/config', { + method: 'PUT', + body: JSON.stringify(payload), + }); +} + // 获取所有网站状态 export async function getWebsites() { return request('/websites'); @@ -35,7 +100,7 @@ export async function getWebsite(id) { // 创建网站 export async function createWebsite(data) { - return request('/websites', { + return adminRequest('/websites', { method: 'POST', body: JSON.stringify(data), }); @@ -43,7 +108,7 @@ export async function createWebsite(data) { // 更新网站 export async function updateWebsite(id, data) { - return request(`/websites/${id}`, { + return adminRequest(`/websites/${id}`, { method: 'PUT', body: JSON.stringify(data), }); @@ -51,14 +116,14 @@ export async function updateWebsite(id, data) { // 删除网站 export async function deleteWebsite(id) { - return request(`/websites/${id}`, { + return adminRequest(`/websites/${id}`, { method: 'DELETE', }); } // 立即检测网站 export async function checkWebsiteNow(id) { - return request(`/websites/${id}/check`, { + return adminRequest(`/websites/${id}/check`, { method: 'POST', }); } @@ -68,10 +133,30 @@ export async function getGroups() { return request('/groups'); } +// 获取单个分组 +export async function getGroup(id) { + return request(`/groups/${id}`); +} + // 添加分组 export async function addGroup(name) { - return request('/groups', { + return adminRequest('/groups', { method: 'POST', body: JSON.stringify({ name }), }); } + +// 更新分组 +export async function updateGroup(id, payload) { + return adminRequest(`/groups/${id}`, { + method: 'PUT', + body: JSON.stringify(payload), + }); +} + +// 删除分组 +export async function deleteGroup(id) { + return adminRequest(`/groups/${id}`, { + method: 'DELETE', + }); +}