package api import ( "math" "net/http" "os" "runtime" "time" "github.com/gin-gonic/gin" ) // 构建信息由编译参数注入: // go build -ldflags "-X oci-portal/internal/api.buildVersion=v1.0.0 -X oci-portal/internal/api.buildTime=2026-07-09T12:00Z" var ( buildVersion = "dev" buildTime = "" // startedAt 为进程启动时刻,运行时长与 CPU 均值的计算基准 startedAt = time.Now() // aboutDB 由 main 启动时注入,存储指标据此定位数据库文件 aboutDB struct{ driver, path string } ) // SetAboutRuntime 注入数据库形态,「关于」页存储指标展示用;启动时调用一次。 func SetAboutRuntime(dbDriver, dbPath string) { aboutDB.driver, aboutDB.path = dbDriver, dbPath } // about 返回构建与运行环境信息,「设置 · 关于」页展示。 // // @Summary 返回构建、运行时长与资源占用信息,「设置 · 关于」页展示 // @Tags 设置 // @Success 200 {object} aboutResponse // @Security BearerAuth // @Router /api/v1/about [get] func about(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "version": buildVersion, "buildTime": buildTime, "goVersion": runtime.Version(), "platform": runtime.GOOS + "/" + runtime.GOARCH, "startedAt": startedAt.UTC().Format(time.RFC3339), "uptimeSeconds": int64(time.Since(startedAt).Seconds()), "resources": resourcesSnapshot(), }) } // resourcesSnapshot 采集进程资源占用:CPU 自启动均值、内存、goroutine 与存储。 func resourcesSnapshot() gin.H { var ms runtime.MemStats runtime.ReadMemStats(&ms) pct := 0.0 if up := time.Since(startedAt).Seconds(); up > 0 { // 多核并行时均值可超 100%,口径为「占单核百分比」 pct = processCPUTime().Seconds() / up * 100 } return gin.H{ "cpuAvgPercent": math.Round(pct*100) / 100, "numCpu": runtime.NumCPU(), "goroutines": runtime.NumGoroutine(), "memHeapBytes": ms.HeapAlloc, "memSysBytes": ms.Sys, "dbEngine": aboutDB.driver, "dbBytes": dbFileBytes(aboutDB.driver, aboutDB.path), } } // dbFileBytes 统计数据库磁盘占用:sqlite 累加主文件与 -wal / -shm, // 外部数据库(mysql / postgres)不可从本机度量,返回 -1 表示不可用。 func dbFileBytes(driver, path string) int64 { if driver != "sqlite" || path == "" { return -1 } var total int64 for _, p := range []string{path, path + "-wal", path + "-shm"} { if fi, err := os.Stat(p); err == nil { total += fi.Size() } } return total }