| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- package admin
- import (
- "encoding/json"
- "net/http"
- "strconv"
- "strings"
- "time"
- "github.com/Wei-Shaw/sub2api/internal/pkg/response"
- "github.com/Wei-Shaw/sub2api/internal/service"
- "github.com/gin-gonic/gin"
- "golang.org/x/sync/errgroup"
- )
- var opsDashboardSnapshotV2Cache = newSnapshotCache(30 * time.Second)
- type opsDashboardSnapshotV2Response struct {
- GeneratedAt string `json:"generated_at"`
- Overview *service.OpsDashboardOverview `json:"overview"`
- ThroughputTrend *service.OpsThroughputTrendResponse `json:"throughput_trend"`
- ErrorTrend *service.OpsErrorTrendResponse `json:"error_trend"`
- }
- type opsDashboardSnapshotV2CacheKey struct {
- StartTime string `json:"start_time"`
- EndTime string `json:"end_time"`
- Platform string `json:"platform"`
- GroupID *int64 `json:"group_id"`
- QueryMode service.OpsQueryMode `json:"mode"`
- BucketSecond int `json:"bucket_second"`
- }
- // GetDashboardSnapshotV2 returns ops dashboard core snapshot in one request.
- // GET /api/v1/admin/ops/dashboard/snapshot-v2
- func (h *OpsHandler) GetDashboardSnapshotV2(c *gin.Context) {
- if h.opsService == nil {
- response.Error(c, http.StatusServiceUnavailable, "Ops service not available")
- return
- }
- if err := h.opsService.RequireMonitoringEnabled(c.Request.Context()); err != nil {
- response.ErrorFrom(c, err)
- return
- }
- startTime, endTime, err := parseOpsTimeRange(c, "1h")
- if err != nil {
- response.BadRequest(c, err.Error())
- return
- }
- filter := &service.OpsDashboardFilter{
- StartTime: startTime,
- EndTime: endTime,
- Platform: strings.TrimSpace(c.Query("platform")),
- QueryMode: parseOpsQueryMode(c),
- }
- if v := strings.TrimSpace(c.Query("group_id")); v != "" {
- id, err := strconv.ParseInt(v, 10, 64)
- if err != nil || id <= 0 {
- response.BadRequest(c, "Invalid group_id")
- return
- }
- filter.GroupID = &id
- }
- bucketSeconds := pickThroughputBucketSeconds(endTime.Sub(startTime))
- keyRaw, _ := json.Marshal(opsDashboardSnapshotV2CacheKey{
- StartTime: startTime.UTC().Format(time.RFC3339),
- EndTime: endTime.UTC().Format(time.RFC3339),
- Platform: filter.Platform,
- GroupID: filter.GroupID,
- QueryMode: filter.QueryMode,
- BucketSecond: bucketSeconds,
- })
- cacheKey := string(keyRaw)
- if cached, ok := opsDashboardSnapshotV2Cache.Get(cacheKey); ok {
- if cached.ETag != "" {
- c.Header("ETag", cached.ETag)
- c.Header("Vary", "If-None-Match")
- if ifNoneMatchMatched(c.GetHeader("If-None-Match"), cached.ETag) {
- c.Status(http.StatusNotModified)
- return
- }
- }
- c.Header("X-Snapshot-Cache", "hit")
- response.Success(c, cached.Payload)
- return
- }
- var (
- overview *service.OpsDashboardOverview
- trend *service.OpsThroughputTrendResponse
- errTrend *service.OpsErrorTrendResponse
- )
- g, gctx := errgroup.WithContext(c.Request.Context())
- g.Go(func() error {
- f := *filter
- result, err := h.opsService.GetDashboardOverview(gctx, &f)
- if err != nil {
- return err
- }
- overview = result
- return nil
- })
- g.Go(func() error {
- f := *filter
- result, err := h.opsService.GetThroughputTrend(gctx, &f, bucketSeconds)
- if err != nil {
- return err
- }
- trend = result
- return nil
- })
- g.Go(func() error {
- f := *filter
- result, err := h.opsService.GetErrorTrend(gctx, &f, bucketSeconds)
- if err != nil {
- return err
- }
- errTrend = result
- return nil
- })
- if err := g.Wait(); err != nil {
- response.ErrorFrom(c, err)
- return
- }
- resp := &opsDashboardSnapshotV2Response{
- GeneratedAt: time.Now().UTC().Format(time.RFC3339),
- Overview: overview,
- ThroughputTrend: trend,
- ErrorTrend: errTrend,
- }
- cached := opsDashboardSnapshotV2Cache.Set(cacheKey, resp)
- if cached.ETag != "" {
- c.Header("ETag", cached.ETag)
- c.Header("Vary", "If-None-Match")
- }
- c.Header("X-Snapshot-Cache", "miss")
- response.Success(c, resp)
- }
|