| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169 |
- package controller
- import (
- "context"
- "encoding/json"
- "errors"
- "fmt"
- "net/http"
- "one-api/common"
- "strings"
- "time"
- "github.com/gin-gonic/gin"
- "golang.org/x/sync/errgroup"
- )
- type UptimeKumaMonitor struct {
- ID int `json:"id"`
- Name string `json:"name"`
- Type string `json:"type"`
- }
- type UptimeKumaGroup struct {
- ID int `json:"id"`
- Name string `json:"name"`
- Weight int `json:"weight"`
- MonitorList []UptimeKumaMonitor `json:"monitorList"`
- }
- type UptimeKumaHeartbeat struct {
- Status int `json:"status"`
- Time string `json:"time"`
- Msg string `json:"msg"`
- Ping *float64 `json:"ping"`
- }
- type UptimeKumaStatusResponse struct {
- PublicGroupList []UptimeKumaGroup `json:"publicGroupList"`
- }
- type UptimeKumaHeartbeatResponse struct {
- HeartbeatList map[string][]UptimeKumaHeartbeat `json:"heartbeatList"`
- UptimeList map[string]float64 `json:"uptimeList"`
- }
- type MonitorStatus struct {
- Name string `json:"name"`
- Uptime float64 `json:"uptime"`
- Status int `json:"status"`
- }
- var (
- ErrUpstreamNon200 = errors.New("upstream non-200")
- ErrTimeout = errors.New("context deadline exceeded")
- )
- func getAndDecode(ctx context.Context, client *http.Client, url string, dest interface{}) error {
- req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
- if err != nil {
- return err
- }
- resp, err := client.Do(req)
- if err != nil {
- if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {
- return ErrTimeout
- }
- return err
- }
- defer resp.Body.Close()
- if resp.StatusCode != http.StatusOK {
- return ErrUpstreamNon200
- }
- return json.NewDecoder(resp.Body).Decode(dest)
- }
- func GetUptimeKumaStatus(c *gin.Context) {
- common.OptionMapRWMutex.RLock()
- uptimeKumaUrl := common.OptionMap["UptimeKumaUrl"]
- slug := common.OptionMap["UptimeKumaSlug"]
- common.OptionMapRWMutex.RUnlock()
- if uptimeKumaUrl == "" || slug == "" {
- c.JSON(http.StatusOK, gin.H{
- "success": true,
- "message": "",
- "data": []MonitorStatus{},
- })
- return
- }
- uptimeKumaUrl = strings.TrimSuffix(uptimeKumaUrl, "/")
- ctx, cancel := context.WithTimeout(c.Request.Context(), 10*time.Second)
- defer cancel()
- client := &http.Client{}
- statusPageUrl := fmt.Sprintf("%s/api/status-page/%s", uptimeKumaUrl, slug)
- heartbeatUrl := fmt.Sprintf("%s/api/status-page/heartbeat/%s", uptimeKumaUrl, slug)
- var (
- statusData UptimeKumaStatusResponse
- heartbeatData UptimeKumaHeartbeatResponse
- )
- g, gCtx := errgroup.WithContext(ctx)
- g.Go(func() error {
- return getAndDecode(gCtx, client, statusPageUrl, &statusData)
- })
- g.Go(func() error {
- return getAndDecode(gCtx, client, heartbeatUrl, &heartbeatData)
- })
- if err := g.Wait(); err != nil {
- switch err {
- case ErrUpstreamNon200:
- c.JSON(http.StatusBadRequest, gin.H{
- "success": false,
- "message": "上游接口出现问题",
- })
- case ErrTimeout:
- c.JSON(http.StatusRequestTimeout, gin.H{
- "success": false,
- "message": "请求上游接口超时",
- })
- default:
- c.JSON(http.StatusBadRequest, gin.H{
- "success": false,
- "message": err.Error(),
- })
- }
- return
- }
- var monitors []MonitorStatus
- for _, group := range statusData.PublicGroupList {
- for _, monitor := range group.MonitorList {
- monitorStatus := MonitorStatus{
- Name: monitor.Name,
- Uptime: 0.0,
- Status: 0,
- }
- uptimeKey := fmt.Sprintf("%d_24", monitor.ID)
- if uptime, exists := heartbeatData.UptimeList[uptimeKey]; exists {
- monitorStatus.Uptime = uptime
- }
- heartbeatKey := fmt.Sprintf("%d", monitor.ID)
- if heartbeats, exists := heartbeatData.HeartbeatList[heartbeatKey]; exists && len(heartbeats) > 0 {
- latestHeartbeat := heartbeats[0]
- monitorStatus.Status = latestHeartbeat.Status
- }
- monitors = append(monitors, monitorStatus)
- }
- }
- c.JSON(http.StatusOK, gin.H{
- "success": true,
- "message": "",
- "data": monitors,
- })
- }
|