Openstatus www.openstatus.dev
6
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 155 lines 4.0 kB view raw
1package handlers 2 3import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "time" 8 9 "github.com/cenkalti/backoff/v4" 10 "github.com/gin-gonic/gin" 11 "github.com/openstatushq/openstatus/apps/checker/checker" 12 "github.com/openstatushq/openstatus/apps/checker/request" 13 "github.com/rs/zerolog/log" 14) 15 16type PingResponse struct { 17 Body string `json:"body,omitempty"` 18 Headers string `json:"headers,omitempty"` 19 Region string `json:"region"` 20 Timing string `json:"timing,omitempty"` 21 RequestId int64 `json:"requestId,omitempty"` 22 WorkspaceId int64 `json:"workspaceId,omitempty"` 23 Latency int64 `json:"latency"` 24 Timestamp int64 `json:"timestamp"` 25 StatusCode int `json:"statusCode,omitempty"` 26} 27 28type Response struct { 29 Headers map[string]string `json:"headers,omitempty"` 30 Error string `json:"error,omitempty"` 31 Body string `json:"body,omitempty"` 32 Region string `json:"region"` 33 Tags []string `json:"tags,omitempty"` 34 RequestId int64 `json:"requestId,omitempty"` 35 WorkspaceId int64 `json:"workspaceId,omitempty"` 36 Latency int64 `json:"latency"` 37 Timestamp int64 `json:"timestamp"` 38 Timing checker.Timing `json:"timing"` 39 Status int `json:"status,omitempty"` 40} 41 42func (h Handler) PingRegionHandler(c *gin.Context) { 43 ctx := c.Request.Context() 44 45 dataSourceName := "check_response_http__v0" 46 region := c.Param("region") 47 48 if region == "" { 49 c.String(http.StatusBadRequest, "region is required") 50 51 return 52 } 53 54 fmt.Printf("Start of /ping/%s\n", region) 55 56 if c.GetHeader("Authorization") != fmt.Sprintf("Basic %s", h.Secret) { 57 c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"}) 58 59 return 60 } 61 62 if h.CloudProvider == "fly" { 63 if region != h.Region { 64 c.Header("fly-replay", fmt.Sprintf("region=%s", region)) 65 c.String(http.StatusAccepted, "Forwarding request to %s", region) 66 67 return 68 } 69 } 70 71 // We need a new client for each request to avoid connection reuse. 72 requestClient := &http.Client{ 73 Timeout: 45 * time.Second, 74 } 75 76 defer requestClient.CloseIdleConnections() 77 78 var req request.PingRequest 79 if err := c.ShouldBindJSON(&req); err != nil { 80 log.Ctx(ctx).Error().Err(err).Msg("failed to decode checker request") 81 c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"}) 82 83 return 84 } 85 86 var res checker.Response 87 88 op := func() error { 89 90 headers := make([]struct { 91 Key string `json:"key"` 92 Value string `json:"value"` 93 }, 0) 94 95 for key, value := range req.Headers { 96 headers = append(headers, struct { 97 Key string `json:"key"` 98 Value string `json:"value"` 99 }{Key: key, Value: value}) 100 } 101 102 input := request.HttpCheckerRequest{ 103 Headers: headers, 104 URL: req.URL, 105 Method: req.Method, 106 Body: req.Body, 107 } 108 109 r, err := checker.Http(c.Request.Context(), requestClient, input) 110 111 if err != nil { 112 return fmt.Errorf("unable to ping: %w", err) 113 } 114 115 timingAsString, err := json.Marshal(r.Timing) 116 if err != nil { 117 return fmt.Errorf("error while parsing timing data %s: %w", req.URL, err) 118 } 119 120 headersAsString, err := json.Marshal(r.Headers) 121 if err != nil { 122 return nil 123 } 124 125 tbData := PingResponse{ 126 RequestId: req.RequestId, 127 WorkspaceId: req.WorkspaceId, 128 StatusCode: r.Status, 129 Latency: r.Latency, 130 Body: r.Body, 131 Headers: string(headersAsString), 132 Timestamp: r.Timestamp, 133 Timing: string(timingAsString), 134 Region: h.Region, 135 } 136 137 res = r 138 res.Region = h.Region 139 140 if tbData.RequestId != 0 { 141 if err := h.TbClient.SendEvent(ctx, tbData, dataSourceName); err != nil { 142 log.Ctx(ctx).Error().Err(err).Msg("failed to send event to tinybird") 143 } 144 } 145 146 return nil 147 } 148 if err := backoff.Retry(op, backoff.WithMaxRetries(backoff.NewExponentialBackOff(), 3)); err != nil { 149 c.JSON(http.StatusOK, gin.H{"message": "url not reachable"}) 150 151 return 152 } 153 154 c.JSON(http.StatusOK, res) 155}