diff --git a/README.md b/README.md
index c2ff37feb..3999c62ba 100644
--- a/README.md
+++ b/README.md
@@ -44,6 +44,7 @@ Have any feedback or questions? [Create a discussion](https://github.com/TwiN/ga
- [Configuration](#configuration)
- [Conditions](#conditions)
- [Placeholders](#placeholders)
+ - [Severity](#severity)
- [Functions](#functions)
- [Storage](#storage)
- [Client configuration](#client-configuration)
@@ -279,7 +280,6 @@ Here are some examples of conditions you can use:
| `[CERTIFICATE_EXPIRATION] > 48h` | Certificate expiration is more than 48h away | 49h, 50h, 123h | 1h, 24h, ... |
| `[DOMAIN_EXPIRATION] > 720h` | The domain must expire in more than 720h | 4000h | 1h, 24h, ... |
-
#### Placeholders
| Placeholder | Description | Example of resolved value |
|:---------------------------|:------------------------------------------------------------------------------------------|:---------------------------------------------|
@@ -292,6 +292,16 @@ Here are some examples of conditions you can use:
| `[DOMAIN_EXPIRATION]` | Resolves into the duration before the domain expires (valid units are "s", "m", "h".) | `24h`, `48h`, `1234h56m78s` |
| `[DNS_RCODE]` | Resolves into the DNS status of the response | `NOERROR` |
+#### Severity
+
+For failure case you can specify index of severity for each condition. Example of usage: `Low :: [STATUS] == 200`. For case, when severity index is omitted, critical severity will be used.
+
+| Available Severity statuses |
+|:----------------------------|
+|`Low` |
+|`Medium` |
+|`High` |
+|`Critical` |
#### Functions
| Function | Description | Example |
diff --git a/alerting/provider/pagerduty/pagerduty.go b/alerting/provider/pagerduty/pagerduty.go
index ea13d904d..b14caf254 100644
--- a/alerting/provider/pagerduty/pagerduty.go
+++ b/alerting/provider/pagerduty/pagerduty.go
@@ -119,12 +119,28 @@ func (provider *AlertProvider) buildRequestBody(endpoint *core.Endpoint, alert *
Payload: Payload{
Summary: message,
Source: "Gatus",
- Severity: "critical",
+ Severity: provider.pagerDutySeverity(result),
},
})
return body
}
+// Returns PagerDuty severity based on result severity represented as a string
+func (provider *AlertProvider) pagerDutySeverity(result *core.Result) string {
+ switch severity := result.Severity; {
+ case severity.Critical:
+ return "critical"
+ case severity.High:
+ return "error"
+ case severity.Medium:
+ return "warning"
+ case severity.Low:
+ return "info"
+ default:
+ return "critical"
+ }
+}
+
// getIntegrationKeyForGroup returns the appropriate pagerduty integration key for a given group
func (provider *AlertProvider) getIntegrationKeyForGroup(group string) string {
if provider.Overrides != nil {
diff --git a/alerting/provider/pagerduty/pagerduty_test.go b/alerting/provider/pagerduty/pagerduty_test.go
index 2c7ab8d22..a39563941 100644
--- a/alerting/provider/pagerduty/pagerduty_test.go
+++ b/alerting/provider/pagerduty/pagerduty_test.go
@@ -173,6 +173,26 @@ func TestAlertProvider_buildRequestBody(t *testing.T) {
}
}
+func TestAlertProvider_pagerDutySeverity(t *testing.T) {
+ alertProvider := new(AlertProvider)
+ scenarios := []struct {
+ result *core.Result
+ actualSeverityString, expectedSeverityString string
+ }{
+ {result: &core.Result{Severity: core.Severity{}}, actualSeverityString: "critical", expectedSeverityString: "critical"},
+ {result: &core.Result{Severity: core.Severity{Low: true, Medium: true, High: true, Critical: true}}, actualSeverityString: "critical", expectedSeverityString: "critical"},
+ {result: &core.Result{Severity: core.Severity{Low: true, Medium: true, High: true}}, actualSeverityString: "error", expectedSeverityString: "error"},
+ {result: &core.Result{Severity: core.Severity{Low: true, Medium: true}}, actualSeverityString: "warning", expectedSeverityString: "warning"},
+ {result: &core.Result{Severity: core.Severity{Low: true}}, actualSeverityString: "info", expectedSeverityString: "info"},
+ }
+
+ for _, scenario := range scenarios {
+ if alertProvider.pagerDutySeverity(scenario.result) != scenario.expectedSeverityString {
+ t.Errorf("expected %v, got %v", scenario.expectedSeverityString, scenario.actualSeverityString)
+ }
+ }
+}
+
func TestAlertProvider_getIntegrationKeyForGroup(t *testing.T) {
scenarios := []struct {
Name string
diff --git a/core/condition.go b/core/condition.go
index f5a26cd8f..153527c9f 100644
--- a/core/condition.go
+++ b/core/condition.go
@@ -3,6 +3,7 @@ package core
import (
"errors"
"fmt"
+ "regexp"
"strconv"
"strings"
"time"
@@ -106,7 +107,8 @@ func (c Condition) Validate() error {
// evaluate the Condition with the Result of the health check
func (c Condition) evaluate(result *Result, dontResolveFailedConditions bool) bool {
- condition := string(c)
+ severityStatus := None
+ severityByCondition, condition := c.sanitizeSeverityCondition()
success := false
conditionToDisplay := condition
if strings.Contains(condition, " == ") {
@@ -151,11 +153,36 @@ func (c Condition) evaluate(result *Result, dontResolveFailedConditions bool) bo
}
if !success {
//log.Printf("[Condition][evaluate] Condition '%s' did not succeed because '%s' is false", condition, condition)
+ severityStatus = severityByCondition
}
- result.ConditionResults = append(result.ConditionResults, &ConditionResult{Condition: conditionToDisplay, Success: success})
+ result.ConditionResults = append(result.ConditionResults, &ConditionResult{Condition: conditionToDisplay, Success: success, SeverityStatus: severityStatus})
return success
}
+// Extracts severity status from condition.
+// Returns separated SeverityStatus and condition
+func (c Condition) sanitizeSeverityCondition() (SeverityStatus, string) {
+ severityStatus, complexCondition := Critical, string(c)
+ regex, _ := regexp.Compile(`(Low|Medium|High|Critical) :: (.+)`)
+ matchedStringsSlice := regex.FindStringSubmatch(complexCondition)
+ if len(matchedStringsSlice) == 0 {
+ return severityStatus, complexCondition
+ }
+
+ switch foundSeverityStatus := matchedStringsSlice[1]; {
+ case foundSeverityStatus == "Low":
+ severityStatus = Low
+ case foundSeverityStatus == "Medium":
+ severityStatus = Medium
+ case foundSeverityStatus == "High":
+ severityStatus = High
+ case foundSeverityStatus == "Critical":
+ severityStatus = Critical
+ }
+
+ return severityStatus, matchedStringsSlice[2]
+}
+
// hasBodyPlaceholder checks whether the condition has a BodyPlaceholder
// Used for determining whether the response body should be read or not
func (c Condition) hasBodyPlaceholder() bool {
diff --git a/core/condition_result.go b/core/condition_result.go
index d8bdc1e97..e18e66ce6 100644
--- a/core/condition_result.go
+++ b/core/condition_result.go
@@ -7,4 +7,7 @@ type ConditionResult struct {
// Success whether the condition was met (successful) or not (failed)
Success bool `json:"success"`
+
+ // Severity of condition, evaluated during failure case only
+ SeverityStatus SeverityStatus `json:"-"`
}
diff --git a/core/condition_test.go b/core/condition_test.go
index e4daf2377..5bd64fe15 100644
--- a/core/condition_test.go
+++ b/core/condition_test.go
@@ -62,230 +62,263 @@ func TestCondition_evaluate(t *testing.T) {
DontResolveFailedConditions bool
ExpectedSuccess bool
ExpectedOutput string
+ ExpectedSeverity SeverityStatus
}{
{
- Name: "ip",
- Condition: Condition("[IP] == 127.0.0.1"),
- Result: &Result{IP: "127.0.0.1"},
- ExpectedSuccess: true,
- ExpectedOutput: "[IP] == 127.0.0.1",
+ Name: "ip",
+ Condition: Condition("[IP] == 127.0.0.1"),
+ Result: &Result{IP: "127.0.0.1"},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[IP] == 127.0.0.1",
+ ExpectedSeverity: None,
},
{
- Name: "status",
- Condition: Condition("[STATUS] == 200"),
- Result: &Result{HTTPStatus: 200},
- ExpectedSuccess: true,
- ExpectedOutput: "[STATUS] == 200",
+ Name: "status",
+ Condition: Condition("[STATUS] == 200"),
+ Result: &Result{HTTPStatus: 200},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[STATUS] == 200",
+ ExpectedSeverity: None,
},
{
- Name: "status-failure",
- Condition: Condition("[STATUS] == 200"),
- Result: &Result{HTTPStatus: 500},
- ExpectedSuccess: false,
- ExpectedOutput: "[STATUS] (500) == 200",
+ Name: "status-failure",
+ Condition: Condition("[STATUS] == 200"),
+ Result: &Result{HTTPStatus: 500},
+ ExpectedSuccess: false,
+ ExpectedOutput: "[STATUS] (500) == 200",
+ ExpectedSeverity: Critical,
},
{
- Name: "status-using-less-than",
- Condition: Condition("[STATUS] < 300"),
- Result: &Result{HTTPStatus: 201},
- ExpectedSuccess: true,
- ExpectedOutput: "[STATUS] < 300",
+ Name: "status-failure",
+ Condition: Condition("Low :: [STATUS] == 200"),
+ Result: &Result{HTTPStatus: 500},
+ ExpectedSuccess: false,
+ ExpectedOutput: "[STATUS] (500) == 200",
+ ExpectedSeverity: Low,
},
{
- Name: "status-using-less-than-failure",
- Condition: Condition("[STATUS] < 300"),
- Result: &Result{HTTPStatus: 404},
- ExpectedSuccess: false,
- ExpectedOutput: "[STATUS] (404) < 300",
+ Name: "status-failure",
+ Condition: Condition("Medium :: [STATUS] == 200"),
+ Result: &Result{HTTPStatus: 500},
+ ExpectedSuccess: false,
+ ExpectedOutput: "[STATUS] (500) == 200",
+ ExpectedSeverity: Medium,
},
{
- Name: "response-time-using-less-than",
- Condition: Condition("[RESPONSE_TIME] < 500"),
- Result: &Result{Duration: 50 * time.Millisecond},
- ExpectedSuccess: true,
- ExpectedOutput: "[RESPONSE_TIME] < 500",
+ Name: "status-failure",
+ Condition: Condition("High :: [STATUS] == 200"),
+ Result: &Result{HTTPStatus: 500},
+ ExpectedSuccess: false,
+ ExpectedOutput: "[STATUS] (500) == 200",
+ ExpectedSeverity: High,
},
{
- Name: "response-time-using-less-than-with-duration",
- Condition: Condition("[RESPONSE_TIME] < 1s"),
- Result: &Result{Duration: 50 * time.Millisecond},
- ExpectedSuccess: true,
- ExpectedOutput: "[RESPONSE_TIME] < 1s",
+ Name: "status-failure",
+ Condition: Condition("Critical :: [STATUS] == 200"),
+ Result: &Result{HTTPStatus: 500},
+ ExpectedSuccess: false,
+ ExpectedOutput: "[STATUS] (500) == 200",
+ ExpectedSeverity: Critical,
},
{
- Name: "response-time-using-less-than-invalid",
- Condition: Condition("[RESPONSE_TIME] < potato"),
- Result: &Result{Duration: 50 * time.Millisecond},
- ExpectedSuccess: false,
- ExpectedOutput: "[RESPONSE_TIME] (50) < potato (0)", // Non-numerical values automatically resolve to 0
+ Name: "status-using-less-than",
+ Condition: Condition("[STATUS] < 300"),
+ Result: &Result{HTTPStatus: 201},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[STATUS] < 300",
+ ExpectedSeverity: None,
},
{
- Name: "response-time-using-greater-than",
- Condition: Condition("[RESPONSE_TIME] > 500"),
- Result: &Result{Duration: 750 * time.Millisecond},
- ExpectedSuccess: true,
- ExpectedOutput: "[RESPONSE_TIME] > 500",
+ Name: "status-using-less-than-failure",
+ Condition: Condition("[STATUS] < 300"),
+ Result: &Result{HTTPStatus: 404},
+ ExpectedSuccess: false,
+ ExpectedOutput: "[STATUS] (404) < 300",
+ ExpectedSeverity: Critical,
},
{
- Name: "response-time-using-greater-than-with-duration",
- Condition: Condition("[RESPONSE_TIME] > 1s"),
- Result: &Result{Duration: 2 * time.Second},
- ExpectedSuccess: true,
- ExpectedOutput: "[RESPONSE_TIME] > 1s",
+ Name: "response-time-using-less-than",
+ Condition: Condition("[RESPONSE_TIME] < 500"),
+ Result: &Result{Duration: 50 * time.Millisecond},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[RESPONSE_TIME] < 500",
+ ExpectedSeverity: None,
},
{
- Name: "response-time-using-greater-than-or-equal-to-equal",
- Condition: Condition("[RESPONSE_TIME] >= 500"),
- Result: &Result{Duration: 500 * time.Millisecond},
- ExpectedSuccess: true,
- ExpectedOutput: "[RESPONSE_TIME] >= 500",
+ Name: "response-time-using-less-than-with-duration",
+ Condition: Condition("[RESPONSE_TIME] < 1s"),
+ Result: &Result{Duration: 50 * time.Millisecond},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[RESPONSE_TIME] < 1s",
+ ExpectedSeverity: None,
},
{
- Name: "response-time-using-greater-than-or-equal-to-greater",
- Condition: Condition("[RESPONSE_TIME] >= 500"),
- Result: &Result{Duration: 499 * time.Millisecond},
- ExpectedSuccess: false,
- ExpectedOutput: "[RESPONSE_TIME] (499) >= 500",
+ Name: "response-time-using-less-than-invalid",
+ Condition: Condition("[RESPONSE_TIME] < potato"),
+ Result: &Result{Duration: 50 * time.Millisecond},
+ ExpectedSuccess: false,
+ ExpectedOutput: "[RESPONSE_TIME] (50) < potato (0)", // Non-numerical values automatically resolve to 0
+ ExpectedSeverity: Critical,
},
{
- Name: "response-time-using-greater-than-or-equal-to-failure",
- Condition: Condition("[RESPONSE_TIME] >= 500"),
- Result: &Result{Duration: 499 * time.Millisecond},
- ExpectedSuccess: false,
- ExpectedOutput: "[RESPONSE_TIME] (499) >= 500",
+ Name: "response-time-using-greater-than",
+ Condition: Condition("[RESPONSE_TIME] > 500"),
+ Result: &Result{Duration: 750 * time.Millisecond},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[RESPONSE_TIME] > 500",
+ ExpectedSeverity: None,
},
{
- Name: "response-time-using-less-than-or-equal-to-equal",
- Condition: Condition("[RESPONSE_TIME] <= 500"),
- Result: &Result{Duration: 500 * time.Millisecond},
- ExpectedSuccess: true,
- ExpectedOutput: "[RESPONSE_TIME] <= 500",
+ Name: "response-time-using-greater-than-with-duration",
+ Condition: Condition("[RESPONSE_TIME] > 1s"),
+ Result: &Result{Duration: 2 * time.Second},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[RESPONSE_TIME] > 1s",
+ ExpectedSeverity: None,
},
{
- Name: "response-time-using-less-than-or-equal-to-less",
- Condition: Condition("[RESPONSE_TIME] <= 500"),
- Result: &Result{Duration: 25 * time.Millisecond},
- ExpectedSuccess: true,
- ExpectedOutput: "[RESPONSE_TIME] <= 500",
+ Name: "response-time-using-greater-than-or-equal-to-equal",
+ Condition: Condition("[RESPONSE_TIME] >= 500"),
+ Result: &Result{Duration: 500 * time.Millisecond},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[RESPONSE_TIME] >= 500",
+ ExpectedSeverity: None,
},
{
- Name: "response-time-using-less-than-or-equal-to-failure",
- Condition: Condition("[RESPONSE_TIME] <= 500"),
- Result: &Result{Duration: 750 * time.Millisecond},
- ExpectedSuccess: false,
- ExpectedOutput: "[RESPONSE_TIME] (750) <= 500",
+ Name: "response-time-using-greater-than-or-equal-to-greater",
+ Condition: Condition("[RESPONSE_TIME] >= 500"),
+ Result: &Result{Duration: 499 * time.Millisecond},
+ ExpectedSuccess: false,
+ ExpectedOutput: "[RESPONSE_TIME] (499) >= 500",
+ ExpectedSeverity: Critical,
},
{
- Name: "body",
- Condition: Condition("[BODY] == test"),
- Result: &Result{Body: []byte("test")},
- ExpectedSuccess: true,
- ExpectedOutput: "[BODY] == test",
+ Name: "response-time-using-greater-than-or-equal-to-failure",
+ Condition: Condition("[RESPONSE_TIME] >= 500"),
+ Result: &Result{Duration: 499 * time.Millisecond},
+ ExpectedSuccess: false,
+ ExpectedOutput: "[RESPONSE_TIME] (499) >= 500",
+ ExpectedSeverity: Critical,
},
{
- Name: "body-numerical-equal",
- Condition: Condition("[BODY] == 123"),
- Result: &Result{Body: []byte("123")},
- ExpectedSuccess: true,
- ExpectedOutput: "[BODY] == 123",
+ Name: "response-time-using-less-than-or-equal-to-equal",
+ Condition: Condition("[RESPONSE_TIME] <= 500"),
+ Result: &Result{Duration: 500 * time.Millisecond},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[RESPONSE_TIME] <= 500",
+ ExpectedSeverity: None,
},
{
- Name: "body-numerical-less-than",
- Condition: Condition("[BODY] < 124"),
- Result: &Result{Body: []byte("123")},
- ExpectedSuccess: true,
- ExpectedOutput: "[BODY] < 124",
+ Name: "response-time-using-less-than-or-equal-to-less",
+ Condition: Condition("[RESPONSE_TIME] <= 500"),
+ Result: &Result{Duration: 25 * time.Millisecond},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[RESPONSE_TIME] <= 500",
+ ExpectedSeverity: None,
},
{
- Name: "body-numerical-greater-than",
- Condition: Condition("[BODY] > 122"),
- Result: &Result{Body: []byte("123")},
- ExpectedSuccess: true,
- ExpectedOutput: "[BODY] > 122",
+ Name: "response-time-using-less-than-or-equal-to-failure",
+ Condition: Condition("[RESPONSE_TIME] <= 500"),
+ Result: &Result{Duration: 750 * time.Millisecond},
+ ExpectedSuccess: false,
+ ExpectedOutput: "[RESPONSE_TIME] (750) <= 500",
+ ExpectedSeverity: Critical,
},
{
- Name: "body-numerical-greater-than-failure",
- Condition: Condition("[BODY] > 123"),
- Result: &Result{Body: []byte("100")},
- ExpectedSuccess: false,
- ExpectedOutput: "[BODY] (100) > 123",
+ Name: "body",
+ Condition: Condition("[BODY] == test"),
+ Result: &Result{Body: []byte("test")},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[BODY] == test",
+ ExpectedSeverity: None,
},
{
- Name: "body-jsonpath",
- Condition: Condition("[BODY].status == UP"),
- Result: &Result{Body: []byte("{\"status\":\"UP\"}")},
- ExpectedSuccess: true,
- ExpectedOutput: "[BODY].status == UP",
+ Name: "body-numerical-equal",
+ Condition: Condition("[BODY] == 123"),
+ Result: &Result{Body: []byte("123")},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[BODY] == 123",
+ ExpectedSeverity: None,
},
{
- Name: "body-jsonpath-complex",
- Condition: Condition("[BODY].data.name == john"),
- Result: &Result{Body: []byte("{\"data\": {\"id\": 1, \"name\": \"john\"}}")},
- ExpectedSuccess: true,
- ExpectedOutput: "[BODY].data.name == john",
+ Name: "body-numerical-less-than",
+ Condition: Condition("[BODY] < 124"),
+ Result: &Result{Body: []byte("123")},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[BODY] < 124",
+ ExpectedSeverity: None,
},
{
- Name: "body-jsonpath-complex-invalid",
- Condition: Condition("[BODY].data.name == john"),
- Result: &Result{Body: []byte("{\"data\": {\"id\": 1}}")},
- ExpectedSuccess: false,
- ExpectedOutput: "[BODY].data.name (INVALID) == john",
+ Name: "body-numerical-greater-than",
+ Condition: Condition("[BODY] > 122"),
+ Result: &Result{Body: []byte("123")},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[BODY] > 122",
+ ExpectedSeverity: None,
},
{
- Name: "body-jsonpath-complex-len-invalid",
- Condition: Condition("len([BODY].data.name) == john"),
- Result: &Result{Body: []byte("{\"data\": {\"id\": 1}}")},
- ExpectedSuccess: false,
- ExpectedOutput: "len([BODY].data.name) (INVALID) == john",
+ Name: "body-numerical-greater-than-failure",
+ Condition: Condition("[BODY] > 123"),
+ Result: &Result{Body: []byte("100")},
+ ExpectedSuccess: false,
+ ExpectedOutput: "[BODY] (100) > 123",
+ ExpectedSeverity: Critical,
},
{
- Name: "body-jsonpath-double-placeholder",
- Condition: Condition("[BODY].user.firstName != [BODY].user.lastName"),
- Result: &Result{Body: []byte("{\"user\": {\"firstName\": \"john\", \"lastName\": \"doe\"}}")},
- ExpectedSuccess: true,
- ExpectedOutput: "[BODY].user.firstName != [BODY].user.lastName",
+ Name: "body-jsonpath",
+ Condition: Condition("[BODY].status == UP"),
+ Result: &Result{Body: []byte("{\"status\":\"UP\"}")},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[BODY].status == UP",
+ ExpectedSeverity: None,
},
{
- Name: "body-jsonpath-double-placeholder-failure",
- Condition: Condition("[BODY].user.firstName == [BODY].user.lastName"),
- Result: &Result{Body: []byte("{\"user\": {\"firstName\": \"john\", \"lastName\": \"doe\"}}")},
- ExpectedSuccess: false,
- ExpectedOutput: "[BODY].user.firstName (john) == [BODY].user.lastName (doe)",
+ Name: "body-jsonpath-complex",
+ Condition: Condition("[BODY].data.name == john"),
+ Result: &Result{Body: []byte("{\"data\": {\"id\": 1, \"name\": \"john\"}}")},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[BODY].data.name == john",
+ ExpectedSeverity: None,
},
{
- Name: "body-jsonpath-when-body-is-array",
- Condition: Condition("[BODY][0].id == 1"),
- Result: &Result{Body: []byte("[{\"id\": 1}, {\"id\": 2}]")},
- ExpectedSuccess: true,
- ExpectedOutput: "[BODY][0].id == 1",
+ Name: "body-jsonpath-complex-invalid",
+ Condition: Condition("[BODY].data.name == john"),
+ Result: &Result{Body: []byte("{\"data\": {\"id\": 1}}")},
+ ExpectedSuccess: false,
+ ExpectedOutput: "[BODY].data.name (INVALID) == john",
+ ExpectedSeverity: Critical,
},
{
- Name: "body-jsonpath-when-body-is-array-but-actual-body-is-not",
- Condition: Condition("[BODY][0].name == test"),
- Result: &Result{Body: []byte("{\"statusCode\": 500, \"message\": \"Internal Server Error\"}")},
- ExpectedSuccess: false,
- ExpectedOutput: "[BODY][0].name (INVALID) == test",
+ Name: "body-jsonpath-complex-len-invalid",
+ Condition: Condition("len([BODY].data.name) == john"),
+ Result: &Result{Body: []byte("{\"data\": {\"id\": 1}}")},
+ ExpectedSuccess: false,
+ ExpectedOutput: "len([BODY].data.name) (INVALID) == john",
+ ExpectedSeverity: Critical,
},
{
- Name: "body-jsonpath-complex-int",
- Condition: Condition("[BODY].data.id == 1"),
- Result: &Result{Body: []byte("{\"data\": {\"id\": 1}}")},
- ExpectedSuccess: true,
- ExpectedOutput: "[BODY].data.id == 1",
+ Name: "body-jsonpath-double-placeholder",
+ Condition: Condition("[BODY].user.firstName != [BODY].user.lastName"),
+ Result: &Result{Body: []byte("{\"user\": {\"firstName\": \"john\", \"lastName\": \"doe\"}}")},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[BODY].user.firstName != [BODY].user.lastName",
+ ExpectedSeverity: None,
},
{
- Name: "body-jsonpath-complex-array-int",
- Condition: Condition("[BODY].data[1].id == 2"),
- Result: &Result{Body: []byte("{\"data\": [{\"id\": 1}, {\"id\": 2}, {\"id\": 3}]}")},
- ExpectedSuccess: true,
- ExpectedOutput: "[BODY].data[1].id == 2",
+ Name: "body-jsonpath-double-placeholder-failure",
+ Condition: Condition("[BODY].user.firstName == [BODY].user.lastName"),
+ Result: &Result{Body: []byte("{\"user\": {\"firstName\": \"john\", \"lastName\": \"doe\"}}")},
+ ExpectedSuccess: false,
+ ExpectedOutput: "[BODY].user.firstName (john) == [BODY].user.lastName (doe)",
+ ExpectedSeverity: Critical,
},
{
- Name: "body-jsonpath-complex-int-using-greater-than",
- Condition: Condition("[BODY].data.id > 0"),
- Result: &Result{Body: []byte("{\"data\": {\"id\": 1}}")},
- ExpectedSuccess: true,
- ExpectedOutput: "[BODY].data.id > 0",
+ Name: "body-jsonpath-when-body-is-array",
+ Condition: Condition("[BODY][0].id == 1"),
+ Result: &Result{Body: []byte("[{\"id\": 1}, {\"id\": 2}]")},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[BODY][0].id == 1",
+ ExpectedSeverity: None,
},
{
Name: "body-jsonpath-hexadecimal-int-using-greater-than",
@@ -400,297 +433,370 @@ func TestCondition_evaluate(t *testing.T) {
ExpectedOutput: "[BODY].data.id (1) > 5",
},
{
- Name: "body-jsonpath-float-using-greater-than-issue433", // As of v5.3.1, Gatus will convert a float to an int. We're losing precision, but it's better than just returning 0
- Condition: Condition("[BODY].balance > 100"),
- Result: &Result{Body: []byte(`{"balance": "123.40000000000005"}`)},
- ExpectedSuccess: true,
- ExpectedOutput: "[BODY].balance > 100",
+ Name: "body-jsonpath-complex-int",
+ Condition: Condition("[BODY].data.id == 1"),
+ Result: &Result{Body: []byte("{\"data\": {\"id\": 1}}")},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[BODY].data.id == 1",
+ ExpectedSeverity: None,
},
{
- Name: "body-jsonpath-complex-int-using-less-than",
- Condition: Condition("[BODY].data.id < 5"),
- Result: &Result{Body: []byte("{\"data\": {\"id\": 2}}")},
- ExpectedSuccess: true,
- ExpectedOutput: "[BODY].data.id < 5",
+ Name: "body-jsonpath-complex-array-int",
+ Condition: Condition("[BODY].data[1].id == 2"),
+ Result: &Result{Body: []byte("{\"data\": [{\"id\": 1}, {\"id\": 2}, {\"id\": 3}]}")},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[BODY].data[1].id == 2",
+ ExpectedSeverity: None,
},
{
- Name: "body-jsonpath-complex-int-using-less-than-failure",
- Condition: Condition("[BODY].data.id < 5"),
- Result: &Result{Body: []byte("{\"data\": {\"id\": 10}}")},
- ExpectedSuccess: false,
- ExpectedOutput: "[BODY].data.id (10) < 5",
+ Name: "body-jsonpath-complex-int-using-greater-than",
+ Condition: Condition("[BODY].data.id > 0"),
+ Result: &Result{Body: []byte("{\"data\": {\"id\": 1}}")},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[BODY].data.id > 0",
+ ExpectedSeverity: None,
},
{
- Name: "connected",
- Condition: Condition("[CONNECTED] == true"),
- Result: &Result{Connected: true},
- ExpectedSuccess: true,
- ExpectedOutput: "[CONNECTED] == true",
+ Name: "body-jsonpath-complex-int-using-greater-than-failure",
+ Condition: Condition("[BODY].data.id > 5"),
+ Result: &Result{Body: []byte("{\"data\": {\"id\": 1}}")},
+ ExpectedSuccess: false,
+ ExpectedOutput: "[BODY].data.id (1) > 5",
+ ExpectedSeverity: Critical,
},
{
- Name: "connected-failure",
- Condition: Condition("[CONNECTED] == true"),
- Result: &Result{Connected: false},
- ExpectedSuccess: false,
- ExpectedOutput: "[CONNECTED] (false) == true",
+ Name: "body-jsonpath-float-using-greater-than-issue433", // As of v5.3.1, Gatus will convert a float to an int. We're losing precision, but it's better than just returning 0
+ Condition: Condition("[BODY].balance > 100"),
+ Result: &Result{Body: []byte(`{"balance": "123.40000000000005"}`)},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[BODY].balance > 100",
+ ExpectedSeverity: None,
},
{
- Name: "certificate-expiration-not-set",
- Condition: Condition("[CERTIFICATE_EXPIRATION] == 0"),
- Result: &Result{},
- ExpectedSuccess: true,
- ExpectedOutput: "[CERTIFICATE_EXPIRATION] == 0",
+ Name: "body-jsonpath-complex-int-using-less-than",
+ Condition: Condition("[BODY].data.id < 5"),
+ Result: &Result{Body: []byte("{\"data\": {\"id\": 2}}")},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[BODY].data.id < 5",
+ ExpectedSeverity: None,
},
{
- Name: "certificate-expiration-greater-than-numerical",
- Condition: Condition("[CERTIFICATE_EXPIRATION] > " + strconv.FormatInt((time.Hour*24*28).Milliseconds(), 10)),
- Result: &Result{CertificateExpiration: time.Hour * 24 * 60},
- ExpectedSuccess: true,
- ExpectedOutput: "[CERTIFICATE_EXPIRATION] > 2419200000",
+ Name: "body-jsonpath-complex-int-using-less-than-failure",
+ Condition: Condition("[BODY].data.id < 5"),
+ Result: &Result{Body: []byte("{\"data\": {\"id\": 10}}")},
+ ExpectedSuccess: false,
+ ExpectedOutput: "[BODY].data.id (10) < 5",
+ ExpectedSeverity: Critical,
},
{
- Name: "certificate-expiration-greater-than-numerical-failure",
- Condition: Condition("[CERTIFICATE_EXPIRATION] > " + strconv.FormatInt((time.Hour*24*28).Milliseconds(), 10)),
- Result: &Result{CertificateExpiration: time.Hour * 24 * 14},
- ExpectedSuccess: false,
- ExpectedOutput: "[CERTIFICATE_EXPIRATION] (1209600000) > 2419200000",
+ Name: "connected",
+ Condition: Condition("[CONNECTED] == true"),
+ Result: &Result{Connected: true},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[CONNECTED] == true",
+ ExpectedSeverity: None,
},
{
- Name: "certificate-expiration-greater-than-duration",
- Condition: Condition("[CERTIFICATE_EXPIRATION] > 12h"),
- Result: &Result{CertificateExpiration: 24 * time.Hour},
- ExpectedSuccess: true,
- ExpectedOutput: "[CERTIFICATE_EXPIRATION] > 12h",
+ Name: "connected-failure",
+ Condition: Condition("[CONNECTED] == true"),
+ Result: &Result{Connected: false},
+ ExpectedSuccess: false,
+ ExpectedOutput: "[CONNECTED] (false) == true",
+ ExpectedSeverity: Critical,
},
{
- Name: "certificate-expiration-greater-than-duration",
- Condition: Condition("[CERTIFICATE_EXPIRATION] > 48h"),
- Result: &Result{CertificateExpiration: 24 * time.Hour},
- ExpectedSuccess: false,
- ExpectedOutput: "[CERTIFICATE_EXPIRATION] (86400000) > 48h (172800000)",
+ Name: "certificate-expiration-not-set",
+ Condition: Condition("[CERTIFICATE_EXPIRATION] == 0"),
+ Result: &Result{},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[CERTIFICATE_EXPIRATION] == 0",
+ ExpectedSeverity: None,
},
{
- Name: "no-placeholders",
- Condition: Condition("1 == 2"),
- Result: &Result{},
- ExpectedSuccess: false,
- ExpectedOutput: "1 == 2",
+ Name: "certificate-expiration-greater-than-numerical",
+ Condition: Condition("[CERTIFICATE_EXPIRATION] > " + strconv.FormatInt((time.Hour*24*28).Milliseconds(), 10)),
+ Result: &Result{CertificateExpiration: time.Hour * 24 * 60},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[CERTIFICATE_EXPIRATION] > 2419200000",
+ ExpectedSeverity: None,
+ },
+ {
+ Name: "certificate-expiration-greater-than-numerical-failure",
+ Condition: Condition("[CERTIFICATE_EXPIRATION] > " + strconv.FormatInt((time.Hour*24*28).Milliseconds(), 10)),
+ Result: &Result{CertificateExpiration: time.Hour * 24 * 14},
+ ExpectedSuccess: false,
+ ExpectedOutput: "[CERTIFICATE_EXPIRATION] (1209600000) > 2419200000",
+ ExpectedSeverity: Critical,
+ },
+ {
+ Name: "certificate-expiration-greater-than-duration",
+ Condition: Condition("[CERTIFICATE_EXPIRATION] > 12h"),
+ Result: &Result{CertificateExpiration: 24 * time.Hour},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[CERTIFICATE_EXPIRATION] > 12h",
+ ExpectedSeverity: None,
+ },
+ {
+ Name: "certificate-expiration-greater-than-duration",
+ Condition: Condition("[CERTIFICATE_EXPIRATION] > 48h"),
+ Result: &Result{CertificateExpiration: 24 * time.Hour},
+ ExpectedSuccess: false,
+ ExpectedOutput: "[CERTIFICATE_EXPIRATION] (86400000) > 48h (172800000)",
+ ExpectedSeverity: Critical,
+ },
+ {
+ Name: "no-placeholders",
+ Condition: Condition("1 == 2"),
+ Result: &Result{},
+ ExpectedSuccess: false,
+ ExpectedOutput: "1 == 2",
+ ExpectedSeverity: Critical,
},
///////////////
// Functions //
///////////////
// len
{
- Name: "len-body-jsonpath-complex",
- Condition: Condition("len([BODY].data.name) == 4"),
- Result: &Result{Body: []byte("{\"data\": {\"name\": \"john\"}}")},
- ExpectedSuccess: true,
- ExpectedOutput: "len([BODY].data.name) == 4",
+ Name: "len-body-jsonpath-complex",
+ Condition: Condition("len([BODY].data.name) == 4"),
+ Result: &Result{Body: []byte("{\"data\": {\"name\": \"john\"}}")},
+ ExpectedSuccess: true,
+ ExpectedOutput: "len([BODY].data.name) == 4",
+ ExpectedSeverity: None,
},
{
- Name: "len-body-array",
- Condition: Condition("len([BODY]) == 3"),
- Result: &Result{Body: []byte("[{\"id\": 1}, {\"id\": 2}, {\"id\": 3}]")},
- ExpectedSuccess: true,
- ExpectedOutput: "len([BODY]) == 3",
+ Name: "len-body-array",
+ Condition: Condition("len([BODY]) == 3"),
+ Result: &Result{Body: []byte("[{\"id\": 1}, {\"id\": 2}, {\"id\": 3}]")},
+ ExpectedSuccess: true,
+ ExpectedOutput: "len([BODY]) == 3",
+ ExpectedSeverity: None,
},
{
- Name: "len-body-keyed-array",
- Condition: Condition("len([BODY].data) == 3"),
- Result: &Result{Body: []byte("{\"data\": [{\"id\": 1}, {\"id\": 2}, {\"id\": 3}]}")},
- ExpectedSuccess: true,
- ExpectedOutput: "len([BODY].data) == 3",
+ Name: "len-body-keyed-array",
+ Condition: Condition("len([BODY].data) == 3"),
+ Result: &Result{Body: []byte("{\"data\": [{\"id\": 1}, {\"id\": 2}, {\"id\": 3}]}")},
+ ExpectedSuccess: true,
+ ExpectedOutput: "len([BODY].data) == 3",
+ ExpectedSeverity: None,
},
{
- Name: "len-body-array-invalid",
- Condition: Condition("len([BODY].data) == 8"),
- Result: &Result{Body: []byte("{\"name\": \"john.doe\"}")},
- ExpectedSuccess: false,
- ExpectedOutput: "len([BODY].data) (INVALID) == 8",
+ Name: "len-body-array-invalid",
+ Condition: Condition("len([BODY].data) == 8"),
+ Result: &Result{Body: []byte("{\"name\": \"john.doe\"}")},
+ ExpectedSuccess: false,
+ ExpectedOutput: "len([BODY].data) (INVALID) == 8",
+ ExpectedSeverity: Critical,
},
{
- Name: "len-body-string",
- Condition: Condition("len([BODY]) == 8"),
- Result: &Result{Body: []byte("john.doe")},
- ExpectedSuccess: true,
- ExpectedOutput: "len([BODY]) == 8",
+ Name: "len-body-string",
+ Condition: Condition("len([BODY]) == 8"),
+ Result: &Result{Body: []byte("john.doe")},
+ ExpectedSuccess: true,
+ ExpectedOutput: "len([BODY]) == 8",
+ ExpectedSeverity: None,
},
{
- Name: "len-body-keyed-string",
- Condition: Condition("len([BODY].name) == 8"),
- Result: &Result{Body: []byte("{\"name\": \"john.doe\"}")},
- ExpectedSuccess: true,
- ExpectedOutput: "len([BODY].name) == 8",
+ Name: "len-body-keyed-string",
+ Condition: Condition("len([BODY].name) == 8"),
+ Result: &Result{Body: []byte("{\"name\": \"john.doe\"}")},
+ ExpectedSuccess: true,
+ ExpectedOutput: "len([BODY].name) == 8",
+ ExpectedSeverity: None,
},
{
- Name: "len-body-keyed-int",
- Condition: Condition("len([BODY].age) == 2"),
- Result: &Result{Body: []byte(`{"age":18}`)},
- ExpectedSuccess: true,
- ExpectedOutput: "len([BODY].age) == 2",
+ Name: "len-body-keyed-int",
+ Condition: Condition("len([BODY].age) == 2"),
+ Result: &Result{Body: []byte(`{"age":18}`)},
+ ExpectedSuccess: true,
+ ExpectedOutput: "len([BODY].age) == 2",
+ ExpectedSeverity: None,
},
{
- Name: "len-body-keyed-bool",
- Condition: Condition("len([BODY].adult) == 4"),
- Result: &Result{Body: []byte(`{"adult":true}`)},
- ExpectedSuccess: true,
- ExpectedOutput: "len([BODY].adult) == 4",
+ Name: "len-body-keyed-bool",
+ Condition: Condition("len([BODY].adult) == 4"),
+ Result: &Result{Body: []byte(`{"adult":true}`)},
+ ExpectedSuccess: true,
+ ExpectedOutput: "len([BODY].adult) == 4",
+ ExpectedSeverity: None,
},
{
- Name: "len-body-object-inside-array",
- Condition: Condition("len([BODY][0]) == 23"),
- Result: &Result{Body: []byte(`[{"age":18,"adult":true}]`)},
- ExpectedSuccess: true,
- ExpectedOutput: "len([BODY][0]) == 23",
+ Name: "len-body-object-inside-array",
+ Condition: Condition("len([BODY][0]) == 23"),
+ Result: &Result{Body: []byte(`[{"age":18,"adult":true}]`)},
+ ExpectedSuccess: true,
+ ExpectedOutput: "len([BODY][0]) == 23",
+ ExpectedSeverity: None,
},
{
- Name: "len-body-object-keyed-int-inside-array",
- Condition: Condition("len([BODY][0].age) == 2"),
- Result: &Result{Body: []byte(`[{"age":18,"adult":true}]`)},
- ExpectedSuccess: true,
- ExpectedOutput: "len([BODY][0].age) == 2",
+ Name: "len-body-object-keyed-int-inside-array",
+ Condition: Condition("len([BODY][0].age) == 2"),
+ Result: &Result{Body: []byte(`[{"age":18,"adult":true}]`)},
+ ExpectedSuccess: true,
+ ExpectedOutput: "len([BODY][0].age) == 2",
+ ExpectedSeverity: None,
},
{
- Name: "len-body-keyed-bool-inside-array",
- Condition: Condition("len([BODY][0].adult) == 4"),
- Result: &Result{Body: []byte(`[{"age":18,"adult":true}]`)},
- ExpectedSuccess: true,
- ExpectedOutput: "len([BODY][0].adult) == 4",
+ Name: "len-body-keyed-bool-inside-array",
+ Condition: Condition("len([BODY][0].adult) == 4"),
+ Result: &Result{Body: []byte(`[{"age":18,"adult":true}]`)},
+ ExpectedSuccess: true,
+ ExpectedOutput: "len([BODY][0].adult) == 4",
+ ExpectedSeverity: None,
},
{
- Name: "len-body-object",
- Condition: Condition("len([BODY]) == 20"),
- Result: &Result{Body: []byte("{\"name\": \"john.doe\"}")},
- ExpectedSuccess: true,
- ExpectedOutput: "len([BODY]) == 20",
+ Name: "len-body-object",
+ Condition: Condition("len([BODY]) == 20"),
+ Result: &Result{Body: []byte("{\"name\": \"john.doe\"}")},
+ ExpectedSuccess: true,
+ ExpectedOutput: "len([BODY]) == 20",
+ ExpectedSeverity: None,
},
// pat
{
- Name: "pat-body-1",
- Condition: Condition("[BODY] == pat(*john*)"),
- Result: &Result{Body: []byte("{\"name\": \"john.doe\"}")},
- ExpectedSuccess: true,
- ExpectedOutput: "[BODY] == pat(*john*)",
+ Name: "pat-body-1",
+ Condition: Condition("[BODY] == pat(*john*)"),
+ Result: &Result{Body: []byte("{\"name\": \"john.doe\"}")},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[BODY] == pat(*john*)",
+ ExpectedSeverity: None,
},
{
- Name: "pat-body-2",
- Condition: Condition("[BODY].name == pat(john*)"),
- Result: &Result{Body: []byte("{\"name\": \"john.doe\"}")},
- ExpectedSuccess: true,
- ExpectedOutput: "[BODY].name == pat(john*)",
+ Name: "pat-body-2",
+ Condition: Condition("[BODY].name == pat(john*)"),
+ Result: &Result{Body: []byte("{\"name\": \"john.doe\"}")},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[BODY].name == pat(john*)",
+ ExpectedSeverity: None,
},
{
- Name: "pat-body-failure",
- Condition: Condition("[BODY].name == pat(bob*)"),
- Result: &Result{Body: []byte("{\"name\": \"john.doe\"}")},
- ExpectedSuccess: false,
- ExpectedOutput: "[BODY].name (john.doe) == pat(bob*)",
+ Name: "pat-body-failure",
+ Condition: Condition("[BODY].name == pat(bob*)"),
+ Result: &Result{Body: []byte("{\"name\": \"john.doe\"}")},
+ ExpectedSuccess: false,
+ ExpectedOutput: "[BODY].name (john.doe) == pat(bob*)",
+ ExpectedSeverity: Critical,
},
{
- Name: "pat-body-html",
- Condition: Condition("[BODY] == pat(*
john.doe
*)"),
- Result: &Result{Body: []byte(`john.doe
`)},
- ExpectedSuccess: true,
- ExpectedOutput: "[BODY] == pat(*john.doe
*)",
+ Name: "pat-body-html",
+ Condition: Condition("[BODY] == pat(*john.doe
*)"),
+ Result: &Result{Body: []byte(`john.doe
`)},
+ ExpectedSuccess: true,
+ ExpectedOutput: "[BODY] == pat(*john.doe
*)",
+ ExpectedSeverity: None,
},
{
- Name: "pat-body-html-failure",
- Condition: Condition("[BODY] == pat(*john.doe
*)"),
- Result: &Result{Body: []byte(`jane.doe
`)},
- ExpectedSuccess: false,
- ExpectedOutput: "[BODY] (john.doe*)",
+ Name: "pat-body-html-failure",
+ Condition: Condition("[BODY] == pat(*john.doe
*)"),
+ Result: &Result{Body: []byte(`jane.doe
`)},
+ ExpectedSuccess: false,
+ ExpectedOutput: "[BODY] (john.doe*)",
+ ExpectedSeverity: Critical,
},
{
- Name: "pat-body-html-failure-alt",
- Condition: Condition("pat(*john.doe
*) == [BODY]"),
- Result: &Result{Body: []byte(`jane.doe
`)},
- ExpectedSuccess: false,
- ExpectedOutput: "pat(*john.doe
*) == [BODY] (john.doe*) == [BODY]"),
+ Result: &Result{Body: []byte(`jane.doe
`)},
+ ExpectedSuccess: false,
+ ExpectedOutput: "pat(*john.doe
*) == [BODY] ( 100h", Success: false},
@@ -147,9 +153,11 @@ func TestEndpoint(t *testing.T) {
ClientConfig: &client.Config{Timeout: time.Millisecond},
},
ExpectedResult: &Result{
- Success: false,
- Connected: false,
- Hostname: "", // Because Endpoint.UIConfig.HideHostname is true, this should be empty.
+ Success: false,
+ Connected: false,
+ Severity: Severity{Critical: true},
+ SeverityStatus: Critical,
+ Hostname: "", // Because Endpoint.UIConfig.HideHostname is true, this should be empty.
ConditionResults: []*ConditionResult{
{Condition: "[CONNECTED] (false) == true", Success: false},
},
@@ -170,9 +178,11 @@ func TestEndpoint(t *testing.T) {
ClientConfig: &client.Config{Timeout: time.Millisecond},
},
ExpectedResult: &Result{
- Success: false,
- Connected: false,
- Hostname: "twin.sh",
+ Success: false,
+ Connected: false,
+ Severity: Severity{Critical: true},
+ SeverityStatus: Critical,
+ Hostname: "twin.sh",
ConditionResults: []*ConditionResult{
{Condition: "[CONNECTED] (false) == true", Success: false},
},
diff --git a/core/result.go b/core/result.go
index ddbfad2e8..e5d670d29 100644
--- a/core/result.go
+++ b/core/result.go
@@ -4,6 +4,23 @@ import (
"time"
)
+// Severity type
+type Severity struct {
+ Low, Medium, High, Critical bool
+}
+
+// Severity status type
+type SeverityStatus int
+
+// Severity status enums
+const (
+ None SeverityStatus = iota
+ Low
+ Medium
+ High
+ Critical
+)
+
// Result of the evaluation of a Endpoint
type Result struct {
// HTTPStatus is the HTTP response status code
@@ -49,6 +66,12 @@ type Result struct {
// Note that this field is not persisted in the storage.
// It is used for health evaluation as well as debugging purposes.
Body []byte `json:"-"`
+
+ // Total severity counted from all ConditionResults
+ Severity Severity `json:"-"`
+
+ // Result serverity status (detrmined by Result.Severity)
+ SeverityStatus SeverityStatus `json:"severity_status"`
}
// AddError adds an error to the result's list of errors.
@@ -62,3 +85,20 @@ func (r *Result) AddError(error string) {
}
r.Errors = append(r.Errors, error)
}
+
+// Determines severity status by highest result severity priority found.
+// Returns SeverityStatus.
+func (r *Result) determineSeverityStatus() SeverityStatus {
+ switch severity := r.Severity; {
+ case severity.Critical:
+ return Critical
+ case severity.High:
+ return High
+ case severity.Medium:
+ return Medium
+ case severity.Low:
+ return Low
+ default:
+ return Critical
+ }
+}
diff --git a/core/result_test.go b/core/result_test.go
index 899ec7f98..ee398ba32 100644
--- a/core/result_test.go
+++ b/core/result_test.go
@@ -19,3 +19,25 @@ func TestResult_AddError(t *testing.T) {
t.Error("should've had 2 error")
}
}
+
+func TestResult_determineSeverityStatus(t *testing.T) {
+ scenarios := []struct {
+ result *Result
+ actualSeverityStatus, expectedSeverityStatus SeverityStatus
+ }{
+ {result: &Result{Severity: Severity{}}, actualSeverityStatus: Critical, expectedSeverityStatus: SeverityStatus(4)},
+ {result: &Result{Severity: Severity{Low: true}}, actualSeverityStatus: Low, expectedSeverityStatus: SeverityStatus(1)},
+ {result: &Result{Severity: Severity{Medium: true}}, actualSeverityStatus: Medium, expectedSeverityStatus: SeverityStatus(2)},
+ {result: &Result{Severity: Severity{Low: true, Medium: true}}, actualSeverityStatus: Medium, expectedSeverityStatus: SeverityStatus(2)},
+ {result: &Result{Severity: Severity{High: true}}, actualSeverityStatus: High, expectedSeverityStatus: SeverityStatus(3)},
+ {result: &Result{Severity: Severity{Low: true, Medium: true, High: true}}, actualSeverityStatus: High, expectedSeverityStatus: SeverityStatus(3)},
+ {result: &Result{Severity: Severity{Critical: true}}, actualSeverityStatus: Critical, expectedSeverityStatus: SeverityStatus(4)},
+ {result: &Result{Severity: Severity{Low: true, Medium: true, High: true, Critical: true}}, actualSeverityStatus: Critical, expectedSeverityStatus: SeverityStatus(4)},
+ }
+
+ for _, scenario := range scenarios {
+ if scenario.result.determineSeverityStatus() != scenario.expectedSeverityStatus {
+ t.Errorf("expected %v, got %v", scenario.expectedSeverityStatus, scenario.actualSeverityStatus)
+ }
+ }
+}
diff --git a/storage/store/sql/specific_postgres.go b/storage/store/sql/specific_postgres.go
index ea00e8397..04233c503 100644
--- a/storage/store/sql/specific_postgres.go
+++ b/storage/store/sql/specific_postgres.go
@@ -32,6 +32,7 @@ func (s *Store) createPostgresSchema() error {
errors TEXT NOT NULL,
connected BOOLEAN NOT NULL,
status BIGINT NOT NULL,
+ severity_status INTEGER NOT NULL,
dns_rcode TEXT NOT NULL,
certificate_expiration BIGINT NOT NULL,
domain_expiration BIGINT NOT NULL,
@@ -67,6 +68,10 @@ func (s *Store) createPostgresSchema() error {
)
`)
// Silent table modifications
- _, _ = s.db.Exec(`ALTER TABLE endpoint_results ADD IF NOT EXISTS domain_expiration BIGINT NOT NULL DEFAULT 0`)
+ _, _ = s.db.Exec(`
+ ALTER TABLE endpoint_results
+ ADD IF NOT EXISTS domain_expiration BIGINT NOT NULL DEFAULT 0,
+ ADD IF NOT EXISTS severity_status INTEGER NOT NULL DEFAULT 0;
+ `)
return err
}
diff --git a/storage/store/sql/specific_sqlite.go b/storage/store/sql/specific_sqlite.go
index 66b6eff2b..0c5eab247 100644
--- a/storage/store/sql/specific_sqlite.go
+++ b/storage/store/sql/specific_sqlite.go
@@ -32,9 +32,10 @@ func (s *Store) createSQLiteSchema() error {
errors TEXT NOT NULL,
connected INTEGER NOT NULL,
status INTEGER NOT NULL,
+ severity_status INTEGER NOT NULL,
dns_rcode TEXT NOT NULL,
certificate_expiration INTEGER NOT NULL,
- domain_expiration INTEGER NOT NULL,
+ domain_expiration INTEGER NOT NULL,
hostname TEXT NOT NULL,
ip TEXT NOT NULL,
duration INTEGER NOT NULL,
@@ -67,6 +68,9 @@ func (s *Store) createSQLiteSchema() error {
)
`)
// Silent table modifications TODO: Remove this
- _, _ = s.db.Exec(`ALTER TABLE endpoint_results ADD domain_expiration INTEGER NOT NULL DEFAULT 0`)
+ _, _ = s.db.Exec(`
+ ALTER TABLE endpoint_results ADD domain_expiration INTEGER NOT NULL DEFAULT 0;
+ ALTER TABLE endpoint_results ADD severity_status INTEGER NOT NULL DEFAULT 0;
+ `)
return err
}
diff --git a/storage/store/sql/sql.go b/storage/store/sql/sql.go
index 8f520f99e..77e9cb3de 100644
--- a/storage/store/sql/sql.go
+++ b/storage/store/sql/sql.go
@@ -439,8 +439,8 @@ func (s *Store) insertEndpointResult(tx *sql.Tx, endpointID int64, result *core.
var endpointResultID int64
err := tx.QueryRow(
`
- INSERT INTO endpoint_results (endpoint_id, success, errors, connected, status, dns_rcode, certificate_expiration, domain_expiration, hostname, ip, duration, timestamp)
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
+ INSERT INTO endpoint_results (endpoint_id, success, errors, connected, status, severity_status, dns_rcode, certificate_expiration, domain_expiration, hostname, ip, duration, timestamp)
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
RETURNING endpoint_result_id
`,
endpointID,
@@ -448,6 +448,7 @@ func (s *Store) insertEndpointResult(tx *sql.Tx, endpointID int64, result *core.
strings.Join(result.Errors, arraySeparator),
result.Connected,
result.HTTPStatus,
+ result.SeverityStatus,
result.DNSRCode,
result.CertificateExpiration,
result.DomainExpiration,
@@ -591,7 +592,7 @@ func (s *Store) getEndpointEventsByEndpointID(tx *sql.Tx, endpointID int64, page
func (s *Store) getEndpointResultsByEndpointID(tx *sql.Tx, endpointID int64, page, pageSize int) (results []*core.Result, err error) {
rows, err := tx.Query(
`
- SELECT endpoint_result_id, success, errors, connected, status, dns_rcode, certificate_expiration, domain_expiration, hostname, ip, duration, timestamp
+ SELECT endpoint_result_id, success, errors, connected, status, severity_status, dns_rcode, certificate_expiration, domain_expiration, hostname, ip, duration, timestamp
FROM endpoint_results
WHERE endpoint_id = $1
ORDER BY endpoint_result_id DESC -- Normally, we'd sort by timestamp, but sorting by endpoint_result_id is faster
@@ -609,7 +610,7 @@ func (s *Store) getEndpointResultsByEndpointID(tx *sql.Tx, endpointID int64, pag
result := &core.Result{}
var id int64
var joinedErrors string
- err = rows.Scan(&id, &result.Success, &joinedErrors, &result.Connected, &result.HTTPStatus, &result.DNSRCode, &result.CertificateExpiration, &result.DomainExpiration, &result.Hostname, &result.IP, &result.Duration, &result.Timestamp)
+ err = rows.Scan(&id, &result.Success, &joinedErrors, &result.Connected, &result.HTTPStatus, &result.SeverityStatus, &result.DNSRCode, &result.CertificateExpiration, &result.DomainExpiration, &result.Hostname, &result.IP, &result.Duration, &result.Timestamp)
if err != nil {
log.Printf("[sql][getEndpointResultsByEndpointID] Silently failed to retrieve endpoint result for endpointID=%d: %s", endpointID, err.Error())
err = nil
diff --git a/web/app/src/components/Endpoint.vue b/web/app/src/components/Endpoint.vue
index f1f41e054..35d456347 100644
--- a/web/app/src/components/Endpoint.vue
+++ b/web/app/src/components/Endpoint.vue
@@ -29,6 +29,9 @@
+
+
+
@@ -172,12 +175,17 @@ export default {
content: "✓";
}
+.status.status-warning::after {
+ content: "!";
+}
+
.status.status-failure::after {
content: "X";
}
@media screen and (max-width: 600px) {
.status.status-success::after,
+ .status.status-warning::after,
.status.status-failure::after {
content: " ";
white-space: pre;
diff --git a/web/static/css/app.css b/web/static/css/app.css
index 7954af7ac..3766a5183 100644
--- a/web/static/css/app.css
+++ b/web/static/css/app.css
@@ -2,4 +2,4 @@
/*
! tailwindcss v3.1.8 | MIT License | https://tailwindcss.com
-*/*,:after,:before{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:after,:before{--tw-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:Consolas,Monaco,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::-webkit-backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.invisible{visibility:hidden}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.top-2{top:.5rem}.left-2{left:.5rem}.bottom-2{bottom:.5rem}.float-right{float:right}.mx-auto{margin-left:auto;margin-right:auto}.my-auto{margin-top:auto;margin-bottom:auto}.my-4{margin-top:1rem;margin-bottom:1rem}.my-24{margin-top:6rem;margin-bottom:6rem}.mb-2{margin-bottom:.5rem}.mt-4{margin-top:1rem}.mb-5{margin-bottom:1.25rem}.mt-3{margin-top:.75rem}.mr-2{margin-right:.5rem}.mb-4{margin-bottom:1rem}.mt-12{margin-top:3rem}.mt-6{margin-top:1.5rem}.mb-10{margin-bottom:2.5rem}.mb-1{margin-bottom:.25rem}.mt-2{margin-top:.5rem}.mt-1{margin-top:.25rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.hidden{display:none}.h-64{height:16rem}.h-6{height:1.5rem}.w-64{width:16rem}.w-3\/4{width:75%}.w-1\/4{width:25%}.w-full{width:100%}.w-1\/2{width:50%}.w-7{width:1.75rem}.w-3{width:.75rem}.w-4{width:1rem}.w-8{width:2rem}.max-w-md{max-width:28rem}.max-w-lg{max-width:32rem}.flex-1{flex:1 1 0%}@-webkit-keyframes spin{to{transform:rotate(1turn)}}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{-webkit-animation:spin 1s linear infinite;animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(229 231 235/var(--tw-divide-opacity))}.overflow-x-hidden{overflow-x:hidden}.rounded-sm{border-radius:.125rem}.rounded-md{border-radius:.375rem}.rounded-none{border-radius:0}.rounded{border-radius:.25rem}.rounded-xl{border-radius:.75rem}.rounded-full{border-radius:9999px}.border{border-width:1px}.border-l{border-left-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-green-800{--tw-border-opacity:1;border-color:rgb(22 101 52/var(--tw-border-opacity))}.border-gray-400{--tw-border-opacity:1;border-color:rgb(156 163 175/var(--tw-border-opacity))}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.bg-green-700{--tw-bg-opacity:1;background-color:rgb(21 128 61/var(--tw-bg-opacity))}.bg-red-600{--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.from-green-600{--tw-gradient-from:#16a34a;--tw-gradient-to:rgba(22,163,74,0);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.to-green-700{--tw-gradient-to:#15803d}.object-scale-down{-o-object-fit:scale-down;object-fit:scale-down}.p-5{padding:1.25rem}.p-1{padding:.25rem}.p-3{padding:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-2{padding-left:.5rem;padding-right:.5rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-0{padding-top:0;padding-bottom:0}.py-7{padding-top:1.75rem;padding-bottom:1.75rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-0{padding-left:0;padding-right:0}.pb-12{padding-bottom:3rem}.pt-12{padding-top:3rem}.pt-2{padding-top:.5rem}.pb-2{padding-bottom:.5rem}.pb-0\.5{padding-bottom:.125rem}.pb-0{padding-bottom:0}.pl-12{padding-left:3rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:Consolas,Monaco,Courier New,monospace}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-2xl{font-size:1.5rem;line-height:2rem}.font-light{font-weight:300}.font-medium{font-weight:500}.font-extrabold{font-weight:800}.font-bold{font-weight:700}.leading-6{line-height:1.5rem}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.text-green-600{--tw-text-opacity:1;color:rgb(22 163 74/var(--tw-text-opacity))}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity))}.opacity-60{opacity:.6}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow,.shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.transition{transition-property:color,background-color,border-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-text-decoration-color,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-text-decoration-color,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.bg-success{background-color:#28a745}html{height:100%}body{padding-top:20px;padding-bottom:20px;min-height:100vh}body,html{background-color:#f7f9fb}#global{margin-top:0!important}#global,#results{max-width:1280px}@media screen and (max-width:1279px){body{padding-top:0;padding-bottom:0}#global{min-height:100vh}}@media (hover:hover) and (pointer:fine){.hover\:scale-110:hover{--tw-scale-x:1.1;--tw-scale-y:1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:bg-gray-100:hover{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}.hover\:bg-gray-200:hover{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.hover\:from-green-700:hover{--tw-gradient-from:#15803d;--tw-gradient-to:rgba(21,128,61,0);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.hover\:to-green-800:hover{--tw-gradient-to:#166534}.hover\:text-gray-500:hover{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.hover\:text-blue-800:hover{--tw-text-opacity:1;color:rgb(30 64 175/var(--tw-text-opacity))}.hover\:underline:hover{-webkit-text-decoration-line:underline;text-decoration-line:underline}}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.dark .dark\:divide-gray-600>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(75 85 99/var(--tw-divide-opacity))}.dark .dark\:border-gray-500{--tw-border-opacity:1;border-color:rgb(107 114 128/var(--tw-border-opacity))}.dark .dark\:bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}.dark .dark\:bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}.dark .dark\:bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}.dark .dark\:text-gray-200{--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity))}.dark .dark\:text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity))}.dark .dark\:text-gray-100{--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity))}@media (hover:hover) and (pointer:fine){.dark .dark\:hover\:bg-gray-700:hover{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}.dark .dark\:hover\:bg-gray-600:hover{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity))}.dark .dark\:hover\:text-gray-400:hover{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.dark .dark\:hover\:text-blue-400:hover{--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity))}.dark .dark\:hover\:text-gray-500:hover{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}}@media (min-width:640px){.sm\:px-10{padding-left:2.5rem;padding-right:2.5rem}.sm\:text-lg{font-size:1.125rem;line-height:1.75rem}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width:1024px){.lg\:text-4xl{font-size:2.25rem;line-height:2.5rem}}@media (min-width:1280px){.xl\:my-5{margin-top:1.25rem;margin-bottom:1.25rem}.xl\:rounded{border-radius:.25rem}.xl\:border{border-width:1px}.xl\:px-24{padding-left:6rem;padding-right:6rem}.xl\:pb-5{padding-bottom:1.25rem}.xl\:text-5xl{font-size:3rem;line-height:1}.xl\:text-3xl{font-size:1.875rem;line-height:2.25rem}.xl\:shadow-xl{--tw-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}}#settings{position:fixed;left:10px;bottom:10px}#settings select:focus{box-shadow:none}.endpoint:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.endpoint:last-child{border-bottom-left-radius:3px;border-bottom-right-radius:3px;border-bottom-width:3px}.status-over-time{overflow:auto}.status-over-time>span:not(:first-child){margin-left:2px}.status{cursor:pointer;transition:all .5s ease-in-out;overflow-x:hidden;color:#fff;width:5%;font-size:75%;font-weight:700;text-align:center}.status:hover{opacity:.7;transition:opacity .1s ease-in-out;color:#000}.status-time-ago{color:#6a737d;opacity:.5;margin-top:5px}.status.status-success:after{content:"✓"}.status.status-failure:after{content:"X"}@media screen and (max-width:600px){.status.status-failure:after,.status.status-success:after{content:" ";white-space:pre}}.endpoint-group{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none}.endpoint-group h5:hover{color:#1b1e21}.endpoint-group-content>div:first-child{border-top-left-radius:0;border-top-right-radius:0}.endpoint[data-v-dd19f9b4]{border-radius:3px;border-bottom-width:3px}
\ No newline at end of file
+*/*,:after,:before{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:after,:before{--tw-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:Consolas,Monaco,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::-webkit-backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.invisible{visibility:hidden}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.top-2{top:.5rem}.left-2{left:.5rem}.bottom-2{bottom:.5rem}.float-right{float:right}.mx-auto{margin-left:auto;margin-right:auto}.my-auto{margin-top:auto;margin-bottom:auto}.my-4{margin-top:1rem;margin-bottom:1rem}.my-24{margin-top:6rem;margin-bottom:6rem}.mb-2{margin-bottom:.5rem}.mt-4{margin-top:1rem}.mb-5{margin-bottom:1.25rem}.mt-3{margin-top:.75rem}.mr-2{margin-right:.5rem}.mb-4{margin-bottom:1rem}.mt-12{margin-top:3rem}.mt-6{margin-top:1.5rem}.mb-10{margin-bottom:2.5rem}.mb-1{margin-bottom:.25rem}.mt-2{margin-top:.5rem}.mt-1{margin-top:.25rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.hidden{display:none}.h-64{height:16rem}.h-6{height:1.5rem}.w-64{width:16rem}.w-3\/4{width:75%}.w-1\/4{width:25%}.w-full{width:100%}.w-1\/2{width:50%}.w-7{width:1.75rem}.w-3{width:.75rem}.w-4{width:1rem}.w-8{width:2rem}.max-w-md{max-width:28rem}.max-w-lg{max-width:32rem}.flex-1{flex:1 1 0%}@-webkit-keyframes spin{to{transform:rotate(1turn)}}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{-webkit-animation:spin 1s linear infinite;animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(229 231 235/var(--tw-divide-opacity))}.overflow-x-hidden{overflow-x:hidden}.rounded-sm{border-radius:.125rem}.rounded-md{border-radius:.375rem}.rounded-none{border-radius:0}.rounded{border-radius:.25rem}.rounded-xl{border-radius:.75rem}.rounded-full{border-radius:9999px}.border{border-width:1px}.border-l{border-left-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-green-800{--tw-border-opacity:1;border-color:rgb(22 101 52/var(--tw-border-opacity))}.border-gray-400{--tw-border-opacity:1;border-color:rgb(156 163 175/var(--tw-border-opacity))}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.bg-green-700{--tw-bg-opacity:1;background-color:rgb(21 128 61/var(--tw-bg-opacity))}.bg-cyan-600{--tw-bg-opacity:1;background-color:rgb(8 145 178/var(--tw-bg-opacity))}.bg-yellow-600{--tw-bg-opacity:1;background-color:rgb(202 138 4/var(--tw-bg-opacity))}.bg-orange-600{--tw-bg-opacity:1;background-color:rgb(234 88 12/var(--tw-bg-opacity))}.bg-red-600{--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.from-green-600{--tw-gradient-from:#16a34a;--tw-gradient-to:rgba(22,163,74,0);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.to-green-700{--tw-gradient-to:#15803d}.object-scale-down{-o-object-fit:scale-down;object-fit:scale-down}.p-5{padding:1.25rem}.p-1{padding:.25rem}.p-3{padding:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-2{padding-left:.5rem;padding-right:.5rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-0{padding-top:0;padding-bottom:0}.py-7{padding-top:1.75rem;padding-bottom:1.75rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-0{padding-left:0;padding-right:0}.pb-12{padding-bottom:3rem}.pt-12{padding-top:3rem}.pt-2{padding-top:.5rem}.pb-2{padding-bottom:.5rem}.pb-0\.5{padding-bottom:.125rem}.pb-0{padding-bottom:0}.pl-12{padding-left:3rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:Consolas,Monaco,Courier New,monospace}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-2xl{font-size:1.5rem;line-height:2rem}.font-light{font-weight:300}.font-medium{font-weight:500}.font-extrabold{font-weight:800}.font-bold{font-weight:700}.leading-6{line-height:1.5rem}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.text-green-600{--tw-text-opacity:1;color:rgb(22 163 74/var(--tw-text-opacity))}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity))}.opacity-60{opacity:.6}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow,.shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.transition{transition-property:color,background-color,border-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-text-decoration-color,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-text-decoration-color,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.bg-success{background-color:#28a745}html{height:100%}body{padding-top:20px;padding-bottom:20px;min-height:100vh}body,html{background-color:#f7f9fb}#global{margin-top:0!important}#global,#results{max-width:1280px}@media screen and (max-width:1279px){body{padding-top:0;padding-bottom:0}#global{min-height:100vh}}@media (hover:hover) and (pointer:fine){.hover\:scale-110:hover{--tw-scale-x:1.1;--tw-scale-y:1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:bg-gray-100:hover{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}.hover\:bg-gray-200:hover{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.hover\:from-green-700:hover{--tw-gradient-from:#15803d;--tw-gradient-to:rgba(21,128,61,0);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.hover\:to-green-800:hover{--tw-gradient-to:#166534}.hover\:text-gray-500:hover{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.hover\:text-blue-800:hover{--tw-text-opacity:1;color:rgb(30 64 175/var(--tw-text-opacity))}.hover\:underline:hover{-webkit-text-decoration-line:underline;text-decoration-line:underline}}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.dark .dark\:divide-gray-600>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(75 85 99/var(--tw-divide-opacity))}.dark .dark\:border-gray-500{--tw-border-opacity:1;border-color:rgb(107 114 128/var(--tw-border-opacity))}.dark .dark\:bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}.dark .dark\:bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}.dark .dark\:bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}.dark .dark\:text-gray-200{--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity))}.dark .dark\:text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity))}.dark .dark\:text-gray-100{--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity))}@media (hover:hover) and (pointer:fine){.dark .dark\:hover\:bg-gray-700:hover{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}.dark .dark\:hover\:bg-gray-600:hover{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity))}.dark .dark\:hover\:text-gray-400:hover{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.dark .dark\:hover\:text-blue-400:hover{--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity))}.dark .dark\:hover\:text-gray-500:hover{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}}@media (min-width:640px){.sm\:px-10{padding-left:2.5rem;padding-right:2.5rem}.sm\:text-lg{font-size:1.125rem;line-height:1.75rem}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width:1024px){.lg\:text-4xl{font-size:2.25rem;line-height:2.5rem}}@media (min-width:1280px){.xl\:my-5{margin-top:1.25rem;margin-bottom:1.25rem}.xl\:rounded{border-radius:.25rem}.xl\:border{border-width:1px}.xl\:px-24{padding-left:6rem;padding-right:6rem}.xl\:pb-5{padding-bottom:1.25rem}.xl\:text-5xl{font-size:3rem;line-height:1}.xl\:text-3xl{font-size:1.875rem;line-height:2.25rem}.xl\:shadow-xl{--tw-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}}#settings{position:fixed;left:10px;bottom:10px}#settings select:focus{box-shadow:none}.endpoint:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.endpoint:last-child{border-bottom-left-radius:3px;border-bottom-right-radius:3px;border-bottom-width:3px}.status-over-time{overflow:auto}.status-over-time>span:not(:first-child){margin-left:2px}.status{cursor:pointer;transition:all .5s ease-in-out;overflow-x:hidden;color:#fff;width:5%;font-size:75%;font-weight:700;text-align:center}.status:hover{opacity:.7;transition:opacity .1s ease-in-out;color:#000}.status-time-ago{color:#6a737d;opacity:.5;margin-top:5px}.status.status-success:after{content:"✓"}.status.status-warning:after{content:"!"}.status.status-failure:after{content:"X"}@media screen and (max-width:600px){.status.status-failure:after,.status.status-success:after,.status.status-warning:after{content:" ";white-space:pre}}.endpoint-group{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none}.endpoint-group h5:hover{color:#1b1e21}.endpoint-group-content>div:first-child{border-top-left-radius:0;border-top-right-radius:0}.endpoint[data-v-dd19f9b4]{border-radius:3px;border-bottom-width:3px}
\ No newline at end of file
diff --git a/web/static/js/app.js b/web/static/js/app.js
index db94fcd69..bdc67d226 100644
--- a/web/static/js/app.js
+++ b/web/static/js/app.js
@@ -1 +1 @@
-(function(){"use strict";var e={4647:function(e,t,s){s.d(t,{L:function(){return us}});s(7727);var n=s(9963),o=s(6252),a=s(3577),r=s.p+"img/logo.svg";const i={class:"mb-2"},l={class:"flex flex-wrap"},d={class:"w-3/4 text-left my-auto"},g={class:"text-3xl xl:text-5xl lg:text-4xl font-light"},h={class:"w-1/4 flex justify-end"},u=["src"],c={key:1,src:r,alt:"Gatus",class:"object-scale-down",style:{"max-width":"100px","min-width":"50px","min-height":"50px"}},p={key:0,class:"flex flex-wrap"},m=["href"],v={key:2,class:"mx-auto max-w-md pt-12"},f=(0,o._)("img",{src:r,alt:"Gatus",class:"mx-auto",style:{"max-width":"160px","min-width":"50px","min-height":"50px"}},null,-1),w=(0,o._)("h2",{class:"mt-4 text-center text-4xl font-extrabold text-gray-800 dark:text-gray-200"}," Gatus ",-1),x={class:"py-7 px-4 rounded-sm sm:px-10"},y={key:0,class:"text-red-500 text-center mb-5"},k={class:"text-sm"},T={key:0,class:"text-red-500"},b={key:1,class:"text-red-500"},R=["href"];function _(e,t,s,n,r,_){const S=(0,o.up)("Loading"),D=(0,o.up)("router-view"),I=(0,o.up)("Tooltip"),A=(0,o.up)("Social");return(0,o.wg)(),(0,o.iD)(o.HY,null,[r.retrievedConfig?((0,o.wg)(),(0,o.iD)("div",{key:1,class:(0,a.C_)([r.config&&r.config.oidc&&!r.config.authenticated?"hidden":"","container container-xs relative mx-auto xl:rounded xl:border xl:shadow-xl xl:my-5 p-5 pb-12 xl:pb-5 text-left dark:bg-gray-800 dark:text-gray-200 dark:border-gray-500"]),id:"global"},[(0,o._)("div",i,[(0,o._)("div",l,[(0,o._)("div",d,[(0,o._)("div",g,(0,a.zw)(_.header),1)]),(0,o._)("div",h,[((0,o.wg)(),(0,o.j4)((0,o.LL)(_.link?"a":"div"),{href:_.link,target:"_blank",style:{width:"100px"}},{default:(0,o.w5)((()=>[_.logo?((0,o.wg)(),(0,o.iD)("img",{key:0,src:_.logo,alt:"Gatus",class:"object-scale-down",style:{"max-width":"100px","min-width":"50px","min-height":"50px"}},null,8,u)):((0,o.wg)(),(0,o.iD)("img",c))])),_:1},8,["href"]))])]),_.buttons?((0,o.wg)(),(0,o.iD)("div",p,[((0,o.wg)(!0),(0,o.iD)(o.HY,null,(0,o.Ko)(_.buttons,(e=>((0,o.wg)(),(0,o.iD)("a",{key:e.name,href:e.link,target:"_blank",class:"px-2 py-0.5 font-medium select-none text-gray-600 hover:text-gray-500 dark:text-gray-300 dark:hover:text-gray-400 hover:underline"},(0,a.zw)(e.name),9,m)))),128))])):(0,o.kq)("",!0)]),(0,o.Wm)(D,{onShowTooltip:_.showTooltip},null,8,["onShowTooltip"])],2)):((0,o.wg)(),(0,o.j4)(S,{key:0,class:"h-64 w-64 px-4"})),r.config&&r.config.oidc&&!r.config.authenticated?((0,o.wg)(),(0,o.iD)("div",v,[f,w,(0,o._)("div",x,[e.$route&&e.$route.query.error?((0,o.wg)(),(0,o.iD)("div",y,[(0,o._)("div",k,["access_denied"===e.$route.query.error?((0,o.wg)(),(0,o.iD)("span",T,"You do not have access to this status page")):((0,o.wg)(),(0,o.iD)("span",b,(0,a.zw)(e.$route.query.error),1))])])):(0,o.kq)("",!0),(0,o._)("div",null,[(0,o._)("a",{href:`${r.SERVER_URL}/oidc/login`,class:"max-w-lg mx-auto w-full flex justify-center py-3 px-4 border border-green-800 rounded-md shadow-lg text-sm text-white bg-green-700 bg-gradient-to-r from-green-600 to-green-700 hover:from-green-700 hover:to-green-800"}," Login with OIDC ",8,R)])])])):(0,o.kq)("",!0),(0,o.Wm)(I,{result:r.tooltip.result,event:r.tooltip.event},null,8,["result","event"]),(0,o.Wm)(A)],64)}const S=e=>((0,o.dD)("data-v-a4b3d200"),e=e(),(0,o.Cn)(),e),D={id:"social"},I=S((()=>(0,o._)("a",{href:"https://github.com/TwiN/gatus",target:"_blank",title:"Gatus on GitHub"},[(0,o._)("svg",{xmlns:"http://www.w3.org/2000/svg",width:"32",height:"32",viewBox:"0 0 16 16",class:"hover:scale-110"},[(0,o._)("path",{fill:"gray",d:"M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"})])],-1))),A=[I];function C(e,t,s,n,a,r){return(0,o.wg)(),(0,o.iD)("div",D,A)}var $={name:"Social"},P=s(3744);const E=(0,P.Z)($,[["render",C],["__scopeId","data-v-a4b3d200"]]);var H=E;const L=(0,o._)("div",{class:"tooltip-title"},"Timestamp:",-1),U={id:"tooltip-timestamp"},W=(0,o._)("div",{class:"tooltip-title"},"Response time:",-1),M={id:"tooltip-response-time"},O=(0,o._)("div",{class:"tooltip-title"},"Conditions:",-1),j={id:"tooltip-conditions"},q=(0,o._)("br",null,null,-1),B={key:0,id:"tooltip-errors-container"},z=(0,o._)("div",{class:"tooltip-title"},"Errors:",-1),Y={id:"tooltip-errors"},N=(0,o._)("br",null,null,-1);function Z(e,t,s,n,r,i){return(0,o.wg)(),(0,o.iD)("div",{id:"tooltip",ref:"tooltip",class:(0,a.C_)(r.hidden?"invisible":""),style:(0,a.j5)("top:"+r.top+"px; left:"+r.left+"px")},[s.result?(0,o.WI)(e.$slots,"default",{key:0},(()=>[L,(0,o._)("code",U,(0,a.zw)(e.prettifyTimestamp(s.result.timestamp)),1),W,(0,o._)("code",M,(0,a.zw)((s.result.duration/1e6).toFixed(0))+"ms",1),O,(0,o._)("code",j,[((0,o.wg)(!0),(0,o.iD)(o.HY,null,(0,o.Ko)(s.result.conditionResults,(t=>(0,o.WI)(e.$slots,"default",{key:t},(()=>[(0,o.Uk)((0,a.zw)(t.success?"✓":"X")+" ~ "+(0,a.zw)(t.condition),1),q])))),128))]),s.result.errors&&s.result.errors.length?((0,o.wg)(),(0,o.iD)("div",B,[z,(0,o._)("code",Y,[((0,o.wg)(!0),(0,o.iD)(o.HY,null,(0,o.Ko)(s.result.errors,(t=>(0,o.WI)(e.$slots,"default",{key:t},(()=>[(0,o.Uk)(" - "+(0,a.zw)(t),1),N])))),128))])])):(0,o.kq)("",!0)])):(0,o.kq)("",!0)],6)}s(5306);const G={methods:{generatePrettyTimeAgo(e){let t=(new Date).getTime()-new Date(e).getTime();if(t<500)return"now";if(t>2592e5){let e=(t/864e5).toFixed(0);return e+" day"+("1"!==e?"s":"")+" ago"}if(t>36e5){let e=(t/36e5).toFixed(0);return e+" hour"+("1"!==e?"s":"")+" ago"}if(t>6e4){let e=(t/6e4).toFixed(0);return e+" minute"+("1"!==e?"s":"")+" ago"}let s=(t/1e3).toFixed(0);return s+" second"+("1"!==s?"s":"")+" ago"},generatePrettyTimeDifference(e,t){let s=Math.ceil((new Date(e)-new Date(t))/1e3/60);return s+(1===s?" minute":" minutes")},prettifyTimestamp(e){let t=new Date(e),s=t.getFullYear(),n=(t.getMonth()+1<10?"0":"")+(t.getMonth()+1),o=(t.getDate()<10?"0":"")+t.getDate(),a=(t.getHours()<10?"0":"")+t.getHours(),r=(t.getMinutes()<10?"0":"")+t.getMinutes(),i=(t.getSeconds()<10?"0":"")+t.getSeconds();return s+"-"+n+"-"+o+" "+a+":"+r+":"+i}}};var F={name:"Endpoints",props:{event:Event,result:Object},mixins:[G],methods:{htmlEntities(e){return String(e).replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")},reposition(){if(this.event&&this.event.type)if("mouseenter"===this.event.type){let e=this.event.target.getBoundingClientRect().y+30,t=this.event.target.getBoundingClientRect().x,s=this.$refs.tooltip.getBoundingClientRect();t+window.scrollX+s.width+50>document.body.getBoundingClientRect().width&&(t=this.event.target.getBoundingClientRect().x-s.width+this.event.target.getBoundingClientRect().width,t<0&&(t+=-t)),e+window.scrollY+s.height+50>document.body.getBoundingClientRect().height&&e>=0&&(e=this.event.target.getBoundingClientRect().y-(s.height+10),e<0&&(e=this.event.target.getBoundingClientRect().y+30)),this.top=e,this.left=t}else"mouseleave"===this.event.type&&(this.hidden=!0)}},watch:{event:function(e){e&&e.type&&("mouseenter"===e.type?this.hidden=!1:"mouseleave"===e.type&&(this.hidden=!0))}},updated(){this.reposition()},created(){this.reposition()},data(){return{hidden:!0,top:0,left:0}}};const K=(0,P.Z)(F,[["render",Z]]);var V=K;const J={class:"flex justify-center items-center mx-auto"},X=(0,o._)("img",{class:(0,a.C_)("animate-spin opacity-60 rounded-full"),src:r,alt:"Gatus logo"},null,-1),Q=[X];function ee(e,t,s,n,a,r){return(0,o.wg)(),(0,o.iD)("div",J,Q)}var te={};const se=(0,P.Z)(te,[["render",ee]]);var ne=se,oe={name:"App",components:{Loading:ne,Social:H,Tooltip:V},methods:{fetchConfig(){fetch(`${us}/api/v1/config`,{credentials:"include"}).then((e=>{this.retrievedConfig=!0,200===e.status&&e.json().then((e=>{this.config=e}))}))},showTooltip(e,t){this.tooltip={result:e,event:t}}},computed:{logo(){return window.config&&window.config.logo&&"{{ .Logo }}"!==window.config.logo?window.config.logo:""},header(){return window.config&&window.config.header&&"{{ .Header }}"!==window.config.header?window.config.header:"Health Status"},link(){return window.config&&window.config.link&&"{{ .Link }}"!==window.config.link?window.config.link:null},buttons(){return window.config&&window.config.buttons?window.config.buttons:[]}},data(){return{error:"",retrievedConfig:!1,config:{oidc:!1,authenticated:!0},tooltip:{},SERVER_URL:us}},created(){this.fetchConfig()}};const ae=(0,P.Z)(oe,[["render",_]]);var re=ae,ie=s(2119);function le(e,t,s,a,r,i){const l=(0,o.up)("Loading"),d=(0,o.up)("Endpoints"),g=(0,o.up)("Pagination"),h=(0,o.up)("Settings");return(0,o.wg)(),(0,o.iD)(o.HY,null,[r.retrievedData?(0,o.kq)("",!0):((0,o.wg)(),(0,o.j4)(l,{key:0,class:"h-64 w-64 px-4 my-24"})),(0,o.WI)(e.$slots,"default",{},(()=>[(0,o.wy)((0,o.Wm)(d,{endpointStatuses:r.endpointStatuses,showStatusOnHover:!0,onShowTooltip:i.showTooltip,onToggleShowAverageResponseTime:i.toggleShowAverageResponseTime,showAverageResponseTime:r.showAverageResponseTime},null,8,["endpointStatuses","onShowTooltip","onToggleShowAverageResponseTime","showAverageResponseTime"]),[[n.F8,r.retrievedData]]),(0,o.wy)((0,o.Wm)(g,{onPage:i.changePage},null,8,["onPage"]),[[n.F8,r.retrievedData]])])),(0,o.Wm)(h,{onRefreshData:i.fetchData},null,8,["onRefreshData"])],64)}s(3948);const de={id:"settings",class:"flex bg-gray-200 border-gray-300 rounded border shadow dark:text-gray-200 dark:bg-gray-800 dark:border-gray-500"},ge={class:"text-xs text-gray-600 rounded-xl py-1.5 px-1.5 dark:text-gray-200"},he=["selected"],ue=["selected"],ce=["selected"],pe=["selected"],me=["selected"],ve=["selected"];function fe(e,t,s,n,a,r){const i=(0,o.up)("ArrowPathIcon"),l=(0,o.up)("SunIcon"),d=(0,o.up)("MoonIcon");return(0,o.wg)(),(0,o.iD)("div",de,[(0,o._)("div",ge,[(0,o.Wm)(i,{class:"w-3"})]),(0,o._)("select",{class:"text-center text-gray-500 text-xs dark:text-gray-200 dark:bg-gray-800 border-r border-l border-gray-300 dark:border-gray-500",id:"refresh-rate",ref:"refreshInterval",onChange:t[0]||(t[0]=(...e)=>r.handleChangeRefreshInterval&&r.handleChangeRefreshInterval(...e))},[(0,o._)("option",{value:"10",selected:10===a.refreshInterval},"10s",8,he),(0,o._)("option",{value:"30",selected:30===a.refreshInterval},"30s",8,ue),(0,o._)("option",{value:"60",selected:60===a.refreshInterval},"1m",8,ce),(0,o._)("option",{value:"120",selected:120===a.refreshInterval},"2m",8,pe),(0,o._)("option",{value:"300",selected:300===a.refreshInterval},"5m",8,me),(0,o._)("option",{value:"600",selected:600===a.refreshInterval},"10m",8,ve)],544),(0,o._)("button",{onClick:t[1]||(t[1]=(...e)=>r.toggleDarkMode&&r.toggleDarkMode(...e)),class:"text-xs p-1"},[a.darkMode?(0,o.WI)(e.$slots,"default",{key:0},(()=>[(0,o.Wm)(l,{class:"w-4"})])):(0,o.WI)(e.$slots,"default",{key:1},(()=>[(0,o.Wm)(d,{class:"w-4 text-gray-500"})]))])])}var we=s(6758),xe=s(4913),ye=s(7886),ke={name:"Settings",components:{ArrowPathIcon:ye.Z,MoonIcon:we.Z,SunIcon:xe.Z},props:{},methods:{setRefreshInterval(e){sessionStorage.setItem("gatus:refresh-interval",e);let t=this;this.refreshIntervalHandler=setInterval((function(){t.refreshData()}),1e3*e)},refreshData(){this.$emit("refreshData")},handleChangeRefreshInterval(){this.refreshData(),clearInterval(this.refreshIntervalHandler),this.setRefreshInterval(this.$refs.refreshInterval.value)},toggleDarkMode(){"dark"===localStorage.theme?localStorage.theme="light":localStorage.theme="dark",this.applyTheme()},applyTheme(){"dark"===localStorage.theme||!("theme"in localStorage)&&window.matchMedia("(prefers-color-scheme: dark)").matches?(this.darkMode=!0,document.documentElement.classList.add("dark")):(this.darkMode=!1,document.documentElement.classList.remove("dark"))}},created(){10!==this.refreshInterval&&30!==this.refreshInterval&&60!==this.refreshInterval&&120!==this.refreshInterval&&300!==this.refreshInterval&&600!==this.refreshInterval&&(this.refreshInterval=300),this.setRefreshInterval(this.refreshInterval),this.applyTheme()},unmounted(){clearInterval(this.refreshIntervalHandler)},data(){return{refreshInterval:sessionStorage.getItem("gatus:refresh-interval")<10?300:parseInt(sessionStorage.getItem("gatus:refresh-interval")),refreshIntervalHandler:0,darkMode:!0}}};const Te=(0,P.Z)(ke,[["render",fe]]);var be=Te;const Re={id:"results"};function _e(e,t,s,n,a,r){const i=(0,o.up)("EndpointGroup");return(0,o.wg)(),(0,o.iD)("div",Re,[((0,o.wg)(!0),(0,o.iD)(o.HY,null,(0,o.Ko)(a.endpointGroups,(t=>(0,o.WI)(e.$slots,"default",{key:t},(()=>[(0,o.Wm)(i,{endpoints:t.endpoints,name:t.name,onShowTooltip:r.showTooltip,onToggleShowAverageResponseTime:r.toggleShowAverageResponseTime,showAverageResponseTime:s.showAverageResponseTime},null,8,["endpoints","name","onShowTooltip","onToggleShowAverageResponseTime","showAverageResponseTime"])])))),128))])}const Se={class:"font-mono text-gray-400 text-xl font-medium pb-2 px-3 dark:text-gray-200 dark:hover:text-gray-500 dark:border-gray-500"},De={class:"endpoint-group-arrow mr-2"},Ie={key:0,class:"rounded-xl bg-red-600 text-white px-2 font-bold leading-6 float-right h-6 text-center hover:scale-110 text-sm",title:"Partial Outage"},Ae={key:1,class:"float-right text-green-600 w-7 hover:scale-110",title:"Operational"};function Ce(e,t,s,n,r,i){const l=(0,o.up)("CheckCircleIcon"),d=(0,o.up)("Endpoint");return(0,o.wg)(),(0,o.iD)("div",{class:(0,a.C_)(0===s.endpoints.length?"mt-3":"mt-4")},["undefined"!==s.name?(0,o.WI)(e.$slots,"default",{key:0},(()=>[(0,o._)("div",{class:"endpoint-group pt-2 border dark:bg-gray-800 dark:border-gray-500",onClick:t[0]||(t[0]=(...e)=>i.toggleGroup&&i.toggleGroup(...e))},[(0,o._)("h5",Se,[(0,o._)("span",De,(0,a.zw)(r.collapsed?"▼":"▲"),1),(0,o.Uk)(" "+(0,a.zw)(s.name)+" ",1),r.unhealthyCount?((0,o.wg)(),(0,o.iD)("span",Ie,(0,a.zw)(r.unhealthyCount),1)):((0,o.wg)(),(0,o.iD)("span",Ae,[(0,o.Wm)(l)]))])])])):(0,o.kq)("",!0),r.collapsed?(0,o.kq)("",!0):((0,o.wg)(),(0,o.iD)("div",{key:1,class:(0,a.C_)("undefined"===s.name?"":"endpoint-group-content")},[((0,o.wg)(!0),(0,o.iD)(o.HY,null,(0,o.Ko)(s.endpoints,((t,n)=>(0,o.WI)(e.$slots,"default",{key:n},(()=>[(0,o.Wm)(d,{data:t,maximumNumberOfResults:20,onShowTooltip:i.showTooltip,onToggleShowAverageResponseTime:i.toggleShowAverageResponseTime,showAverageResponseTime:s.showAverageResponseTime},null,8,["data","onShowTooltip","onToggleShowAverageResponseTime","showAverageResponseTime"])])))),128))],2))],2)}const $e={key:0,class:"endpoint px-3 py-3 border-l border-r border-t rounded-none hover:bg-gray-100 dark:hover:bg-gray-700 dark:border-gray-500"},Pe={class:"flex flex-wrap mb-2"},Ee={class:"w-3/4"},He={key:0,class:"text-gray-500 font-light"},Le={class:"w-1/4 text-right"},Ue=["title"],We={class:"status-over-time flex flex-row"},Me=["onMouseenter"],Oe=["onMouseenter"],je={class:"flex flex-wrap status-time-ago"},qe={class:"w-1/2"},Be={class:"w-1/2 text-right"},ze=(0,o._)("div",{class:"w-1/2"}," ",-1);function Ye(e,t,s,n,r,i){const l=(0,o.up)("router-link");return s.data?((0,o.wg)(),(0,o.iD)("div",$e,[(0,o._)("div",Pe,[(0,o._)("div",Ee,[(0,o.Wm)(l,{to:i.generatePath(),class:"font-bold hover:text-blue-800 hover:underline dark:hover:text-blue-400",title:"View detailed endpoint health"},{default:(0,o.w5)((()=>[(0,o.Uk)((0,a.zw)(s.data.name),1)])),_:1},8,["to"]),s.data.results&&s.data.results.length&&s.data.results[s.data.results.length-1].hostname?((0,o.wg)(),(0,o.iD)("span",He," | "+(0,a.zw)(s.data.results[s.data.results.length-1].hostname),1)):(0,o.kq)("",!0)]),(0,o._)("div",Le,[s.data.results&&s.data.results.length?((0,o.wg)(),(0,o.iD)("span",{key:0,class:"font-light overflow-x-hidden cursor-pointer select-none hover:text-gray-500",onClick:t[0]||(t[0]=(...e)=>i.toggleShowAverageResponseTime&&i.toggleShowAverageResponseTime(...e)),title:s.showAverageResponseTime?"Average response time":"Minimum and maximum response time"},[s.showAverageResponseTime?(0,o.WI)(e.$slots,"default",{key:0},(()=>[(0,o.Uk)(" ~"+(0,a.zw)(r.averageResponseTime)+"ms ",1)])):(0,o.WI)(e.$slots,"default",{key:1},(()=>[(0,o.Uk)((0,a.zw)(r.minResponseTime===r.maxResponseTime?r.minResponseTime:r.minResponseTime+"-"+r.maxResponseTime)+"ms ",1)]))],8,Ue)):(0,o.kq)("",!0)])]),(0,o._)("div",null,[(0,o._)("div",We,[s.data.results&&s.data.results.length?(0,o.WI)(e.$slots,"default",{key:0},(()=>[s.data.results.length[((0,o.wg)(!0),(0,o.iD)(o.HY,null,(0,o.Ko)(s.maximumNumberOfResults-s.data.results.length,(e=>((0,o.wg)(),(0,o.iD)("span",{key:e,class:"status rounded border border-dashed border-gray-400"}," ")))),128))])):(0,o.kq)("",!0),((0,o.wg)(!0),(0,o.iD)(o.HY,null,(0,o.Ko)(s.data.results,(s=>(0,o.WI)(e.$slots,"default",{key:s},(()=>[s.success?((0,o.wg)(),(0,o.iD)("span",{key:0,class:"status status-success rounded bg-success",onMouseenter:e=>i.showTooltip(s,e),onMouseleave:t[1]||(t[1]=e=>i.showTooltip(null,e))},null,40,Me)):((0,o.wg)(),(0,o.iD)("span",{key:1,class:"status status-failure rounded bg-red-600",onMouseenter:e=>i.showTooltip(s,e),onMouseleave:t[2]||(t[2]=e=>i.showTooltip(null,e))},null,40,Oe))])))),128))])):(0,o.WI)(e.$slots,"default",{key:1},(()=>[((0,o.wg)(!0),(0,o.iD)(o.HY,null,(0,o.Ko)(s.maximumNumberOfResults,(e=>((0,o.wg)(),(0,o.iD)("span",{key:e,class:"status rounded border border-dashed border-gray-400"}," ")))),128))]))])]),(0,o._)("div",je,[s.data.results&&s.data.results.length?(0,o.WI)(e.$slots,"default",{key:0},(()=>[(0,o._)("div",qe,(0,a.zw)(e.generatePrettyTimeAgo(s.data.results[0].timestamp)),1),(0,o._)("div",Be,(0,a.zw)(e.generatePrettyTimeAgo(s.data.results[s.data.results.length-1].timestamp)),1)])):(0,o.WI)(e.$slots,"default",{key:1},(()=>[ze]))])])):(0,o.kq)("",!0)}var Ne={name:"Endpoint",props:{maximumNumberOfResults:Number,data:Object,showAverageResponseTime:Boolean},emits:["showTooltip","toggleShowAverageResponseTime"],mixins:[G],methods:{updateMinAndMaxResponseTimes(){let e=null,t=null,s=0;for(let n in this.data.results){const o=parseInt((this.data.results[n].duration/1e6).toFixed(0));s+=o,(null==e||e>o)&&(e=o),(null==t||t0&&(this.endpoints[t].results[this.endpoints[t].results.length-1].success||e++);this.unhealthyCount=e},toggleGroup(){this.collapsed=!this.collapsed,sessionStorage.setItem(`gatus:endpoint-group:${this.name}:collapsed`,this.collapsed)},showTooltip(e,t){this.$emit("showTooltip",e,t)},toggleShowAverageResponseTime(){this.$emit("toggleShowAverageResponseTime")}},watch:{endpoints:function(){this.healthCheck()}},created(){this.healthCheck()},data(){return{unhealthyCount:0,collapsed:"true"===sessionStorage.getItem(`gatus:endpoint-group:${this.name}:collapsed`)}}};const Ve=(0,P.Z)(Ke,[["render",Ce]]);var Je=Ve,Xe={name:"Endpoints",components:{EndpointGroup:Je},props:{showStatusOnHover:Boolean,endpointStatuses:Object,showAverageResponseTime:Boolean},emits:["showTooltip","toggleShowAverageResponseTime"],methods:{process(){let e={};for(let s in this.endpointStatuses){let t=this.endpointStatuses[s];e[t.group]&&0!==e[t.group].length||(e[t.group]=[]),e[t.group].push(t)}let t=[];for(let s in e)"undefined"!==s&&t.push({name:s,endpoints:e[s]});e["undefined"]&&t.push({name:"undefined",endpoints:e["undefined"]}),this.endpointGroups=t},showTooltip(e,t){this.$emit("showTooltip",e,t)},toggleShowAverageResponseTime(){this.$emit("toggleShowAverageResponseTime")}},watch:{endpointStatuses:function(){this.process()}},data(){return{userClickedStatus:!1,endpointGroups:[]}}};const Qe=(0,P.Z)(Xe,[["render",_e]]);var et=Qe;const tt={class:"mt-3 flex"},st={class:"flex-1"},nt={class:"flex-1 text-right"};function ot(e,t,s,n,a,r){return(0,o.wg)(),(0,o.iD)("div",tt,[(0,o._)("div",st,[a.currentPage<5?((0,o.wg)(),(0,o.iD)("button",{key:0,onClick:t[0]||(t[0]=(...e)=>r.nextPage&&r.nextPage(...e)),class:"bg-gray-100 hover:bg-gray-200 text-gray-500 border border-gray-200 px-2 rounded font-mono dark:bg-gray-700 dark:text-gray-200 dark:border-gray-500 dark:hover:bg-gray-600"},"<")):(0,o.kq)("",!0)]),(0,o._)("div",nt,[a.currentPage>1?((0,o.wg)(),(0,o.iD)("button",{key:0,onClick:t[1]||(t[1]=(...e)=>r.previousPage&&r.previousPage(...e)),class:"bg-gray-100 hover:bg-gray-200 text-gray-500 border border-gray-200 px-2 rounded font-mono dark:bg-gray-700 dark:text-gray-200 dark:border-gray-500 dark:hover:bg-gray-600"},">")):(0,o.kq)("",!0)])])}var at={name:"Pagination",components:{},emits:["page"],methods:{nextPage(){this.currentPage++,this.$emit("page",this.currentPage)},previousPage(){this.currentPage--,this.$emit("page",this.currentPage)}},data(){return{currentPage:1}}};const rt=(0,P.Z)(at,[["render",ot]]);var it=rt,lt={name:"Home",components:{Loading:ne,Pagination:it,Endpoints:et,Settings:be},emits:["showTooltip","toggleShowAverageResponseTime"],methods:{fetchData(){fetch(`${us}/api/v1/endpoints/statuses?page=${this.currentPage}`,{credentials:"include"}).then((e=>{this.retrievedData=!0,200===e.status?e.json().then((e=>{JSON.stringify(this.endpointStatuses)!==JSON.stringify(e)&&(this.endpointStatuses=e)})):e.text().then((e=>{console.log(`[Home][fetchData] Error: ${e}`)}))}))},changePage(e){this.retrievedData=!1,this.currentPage=e,this.fetchData()},showTooltip(e,t){this.$emit("showTooltip",e,t)},toggleShowAverageResponseTime(){this.showAverageResponseTime=!this.showAverageResponseTime}},data(){return{endpointStatuses:[],currentPage:1,showAverageResponseTime:!0,retrievedData:!1}},created(){this.retrievedData=!1,this.fetchData()}};const dt=(0,P.Z)(lt,[["render",le]]);var gt=dt;const ht=e=>((0,o.dD)("data-v-dd19f9b4"),e=e(),(0,o.Cn)(),e),ut=(0,o.Uk)(" ← "),ct=ht((()=>(0,o._)("h1",{class:"text-xl xl:text-3xl font-mono text-gray-400"},"RECENT CHECKS",-1))),pt=ht((()=>(0,o._)("hr",{class:"mb-4"},null,-1))),mt={key:1,class:"mt-12"},vt=ht((()=>(0,o._)("h1",{class:"text-xl xl:text-3xl font-mono text-gray-400"},"UPTIME",-1))),ft=ht((()=>(0,o._)("hr",null,null,-1))),wt={class:"flex space-x-4 text-center text-2xl mt-6 relative bottom-2 mb-10"},xt={class:"flex-1"},yt=ht((()=>(0,o._)("h2",{class:"text-sm text-gray-400 mb-1"},"Last 7 days",-1))),kt=["src"],Tt={class:"flex-1"},bt=ht((()=>(0,o._)("h2",{class:"text-sm text-gray-400 mb-1"},"Last 24 hours",-1))),Rt=["src"],_t={class:"flex-1"},St=ht((()=>(0,o._)("h2",{class:"text-sm text-gray-400 mb-1"},"Last hour",-1))),Dt=["src"],It={key:2,class:"mt-12"},At=ht((()=>(0,o._)("h1",{class:"text-xl xl:text-3xl font-mono text-gray-400"},"RESPONSE TIME",-1))),Ct=ht((()=>(0,o._)("hr",null,null,-1))),$t=["src"],Pt={class:"flex space-x-4 text-center text-2xl mt-6 relative bottom-2 mb-10"},Et={class:"flex-1"},Ht=ht((()=>(0,o._)("h2",{class:"text-sm text-gray-400 mb-1"},"Last 7 days",-1))),Lt=["src"],Ut={class:"flex-1"},Wt=ht((()=>(0,o._)("h2",{class:"text-sm text-gray-400 mb-1"},"Last 24 hours",-1))),Mt=["src"],Ot={class:"flex-1"},jt=ht((()=>(0,o._)("h2",{class:"text-sm text-gray-400 mb-1"},"Last hour",-1))),qt=["src"],Bt={key:3},zt=ht((()=>(0,o._)("h1",{class:"text-xl xl:text-3xl font-mono text-gray-400 mt-4"},"CURRENT HEALTH",-1))),Yt=ht((()=>(0,o._)("hr",null,null,-1))),Nt={class:"flex space-x-4 text-center text-2xl mt-6 relative bottom-2 mb-10"},Zt={class:"flex-1"},Gt=["src"],Ft={key:4},Kt=ht((()=>(0,o._)("h1",{class:"text-xl xl:text-3xl font-mono text-gray-400 mt-4"},"EVENTS",-1))),Vt=ht((()=>(0,o._)("hr",null,null,-1))),Jt={role:"list",class:"px-0 xl:px-24 divide-y divide-gray-200 dark:divide-gray-600"},Xt={class:"text-sm sm:text-lg"},Qt={class:"flex mt-1 text-xs sm:text-sm text-gray-400"},es={class:"flex-2 text-left pl-12"},ts={class:"flex-1 text-right"};function ss(e,t,s,n,r,i){const l=(0,o.up)("router-link"),d=(0,o.up)("Endpoint"),g=(0,o.up)("Pagination"),h=(0,o.up)("ArrowUpCircleIcon"),u=(0,o.up)("ArrowDownCircleIcon"),c=(0,o.up)("PlayCircleIcon"),p=(0,o.up)("Settings");return(0,o.wg)(),(0,o.iD)(o.HY,null,[(0,o.Wm)(l,{to:"../",class:"absolute top-2 left-2 inline-block px-2 pb-0.5 text-lg text-black bg-gray-100 rounded hover:bg-gray-200 focus:outline-none border border-gray-200 dark:bg-gray-700 dark:text-gray-200 dark:border-gray-500 dark:hover:bg-gray-600"},{default:(0,o.w5)((()=>[ut])),_:1}),(0,o._)("div",null,[r.endpointStatus?(0,o.WI)(e.$slots,"default",{key:0},(()=>[ct,pt,(0,o.Wm)(d,{data:r.endpointStatus,maximumNumberOfResults:20,onShowTooltip:i.showTooltip,onToggleShowAverageResponseTime:i.toggleShowAverageResponseTime,showAverageResponseTime:r.showAverageResponseTime},null,8,["data","onShowTooltip","onToggleShowAverageResponseTime","showAverageResponseTime"]),(0,o.Wm)(g,{onPage:i.changePage},null,8,["onPage"])]),!0):(0,o.kq)("",!0),r.endpointStatus&&r.endpointStatus.key?((0,o.wg)(),(0,o.iD)("div",mt,[vt,ft,(0,o._)("div",wt,[(0,o._)("div",xt,[yt,(0,o._)("img",{src:i.generateUptimeBadgeImageURL("7d"),alt:"7d uptime badge",class:"mx-auto"},null,8,kt)]),(0,o._)("div",Tt,[bt,(0,o._)("img",{src:i.generateUptimeBadgeImageURL("24h"),alt:"24h uptime badge",class:"mx-auto"},null,8,Rt)]),(0,o._)("div",_t,[St,(0,o._)("img",{src:i.generateUptimeBadgeImageURL("1h"),alt:"1h uptime badge",class:"mx-auto"},null,8,Dt)])])])):(0,o.kq)("",!0),r.endpointStatus&&r.endpointStatus.key?((0,o.wg)(),(0,o.iD)("div",It,[At,Ct,(0,o._)("img",{src:i.generateResponseTimeChartImageURL(),alt:"response time chart",class:"mt-6"},null,8,$t),(0,o._)("div",Pt,[(0,o._)("div",Et,[Ht,(0,o._)("img",{src:i.generateResponseTimeBadgeImageURL("7d"),alt:"7d response time badge",class:"mx-auto mt-2"},null,8,Lt)]),(0,o._)("div",Ut,[Wt,(0,o._)("img",{src:i.generateResponseTimeBadgeImageURL("24h"),alt:"24h response time badge",class:"mx-auto mt-2"},null,8,Mt)]),(0,o._)("div",Ot,[jt,(0,o._)("img",{src:i.generateResponseTimeBadgeImageURL("1h"),alt:"1h response time badge",class:"mx-auto mt-2"},null,8,qt)])])])):(0,o.kq)("",!0),r.endpointStatus&&r.endpointStatus.key?((0,o.wg)(),(0,o.iD)("div",Bt,[zt,Yt,(0,o._)("div",Nt,[(0,o._)("div",Zt,[(0,o._)("img",{src:i.generateHealthBadgeImageURL(),alt:"health badge",class:"mx-auto"},null,8,Gt)])])])):(0,o.kq)("",!0),r.endpointStatus&&r.endpointStatus.key?((0,o.wg)(),(0,o.iD)("div",Ft,[Kt,Vt,(0,o._)("ul",Jt,[((0,o.wg)(!0),(0,o.iD)(o.HY,null,(0,o.Ko)(r.events,(t=>((0,o.wg)(),(0,o.iD)("li",{key:t,class:"p-3 my-4"},[(0,o._)("h2",Xt,["HEALTHY"===t.type?((0,o.wg)(),(0,o.j4)(h,{key:0,class:"w-8 inline mr-2 text-green-600"})):"UNHEALTHY"===t.type?((0,o.wg)(),(0,o.j4)(u,{key:1,class:"w-8 inline mr-2 text-red-500"})):"START"===t.type?((0,o.wg)(),(0,o.j4)(c,{key:2,class:"w-8 inline mr-2 text-gray-400 dark:text-gray-100"})):(0,o.kq)("",!0),(0,o.Uk)(" "+(0,a.zw)(t.fancyText),1)]),(0,o._)("div",Qt,[(0,o._)("div",es,(0,a.zw)(e.prettifyTimestamp(t.timestamp)),1),(0,o._)("div",ts,(0,a.zw)(t.fancyTimeAgo),1)])])))),128))])])):(0,o.kq)("",!0)]),(0,o.Wm)(p,{onRefreshData:i.fetchData},null,8,["onRefreshData"])],64)}var ns=s(9505),os=s(7163),as=s(8585),rs={name:"Details",components:{Pagination:it,Endpoint:Ge,Settings:be,ArrowDownCircleIcon:ns.Z,ArrowUpCircleIcon:os.Z,PlayCircleIcon:as.Z},emits:["showTooltip"],mixins:[G],methods:{fetchData(){fetch(`${this.serverUrl}/api/v1/endpoints/${this.$route.params.key}/statuses?page=${this.currentPage}`,{credentials:"include"}).then((e=>{200===e.status?e.json().then((e=>{if(JSON.stringify(this.endpointStatus)!==JSON.stringify(e)){this.endpointStatus=e,this.uptime=e.uptime;let t=[];for(let s=e.events.length-1;s>=0;s--){let n=e.events[s];if(s===e.events.length-1)"UNHEALTHY"===n.type?n.fancyText="Endpoint is unhealthy":"HEALTHY"===n.type?n.fancyText="Endpoint is healthy":"START"===n.type&&(n.fancyText="Monitoring started");else{let t=e.events[s+1];"HEALTHY"===n.type?n.fancyText="Endpoint became healthy":"UNHEALTHY"===n.type?n.fancyText=t?"Endpoint was unhealthy for "+this.generatePrettyTimeDifference(t.timestamp,n.timestamp):"Endpoint became unhealthy":"START"===n.type&&(n.fancyText="Monitoring started")}n.fancyTimeAgo=this.generatePrettyTimeAgo(n.timestamp),t.push(n)}this.events=t}})):e.text().then((e=>{console.log(`[Details][fetchData] Error: ${e}`)}))}))},generateHealthBadgeImageURL(){return`${this.serverUrl}/api/v1/endpoints/${this.endpointStatus.key}/health/badge.svg`},generateUptimeBadgeImageURL(e){return`${this.serverUrl}/api/v1/endpoints/${this.endpointStatus.key}/uptimes/${e}/badge.svg`},generateResponseTimeBadgeImageURL(e){return`${this.serverUrl}/api/v1/endpoints/${this.endpointStatus.key}/response-times/${e}/badge.svg`},generateResponseTimeChartImageURL(){return`${this.serverUrl}/api/v1/endpoints/${this.endpointStatus.key}/response-times/24h/chart.svg`},changePage(e){this.currentPage=e,this.fetchData()},showTooltip(e,t){this.$emit("showTooltip",e,t)},toggleShowAverageResponseTime(){this.showAverageResponseTime=!this.showAverageResponseTime}},data(){return{endpointStatus:{},uptime:{},events:[],hourlyAverageResponseTime:{},serverUrl:"."===us?"..":us,currentPage:1,showAverageResponseTime:!0,chartLabels:[],chartValues:[]}},created(){this.fetchData()}};const is=(0,P.Z)(rs,[["render",ss],["__scopeId","data-v-dd19f9b4"]]);var ls=is;const ds=[{path:"/",name:"Home",component:gt},{path:"/endpoints/:key",name:"Details",component:ls}],gs=(0,ie.p7)({history:(0,ie.PO)("/"),routes:ds});var hs=gs;const us="";(0,n.ri)(re).use(hs).mount("#app")}},t={};function s(n){var o=t[n];if(void 0!==o)return o.exports;var a=t[n]={exports:{}};return e[n](a,a.exports,s),a.exports}s.m=e,function(){var e=[];s.O=function(t,n,o,a){if(!n){var r=1/0;for(g=0;g=a)&&Object.keys(s.O).every((function(e){return s.O[e](n[l])}))?n.splice(l--,1):(i=!1,a0&&e[g-1][2]>a;g--)e[g]=e[g-1];e[g]=[n,o,a]}}(),function(){s.d=function(e,t){for(var n in t)s.o(t,n)&&!s.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})}}(),function(){s.g=function(){if("object"===typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"===typeof window)return window}}()}(),function(){s.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)}}(),function(){s.p="/"}(),function(){var e={143:0};s.O.j=function(t){return 0===e[t]};var t=function(t,n){var o,a,r=n[0],i=n[1],l=n[2],d=0;if(r.some((function(t){return 0!==e[t]}))){for(o in i)s.o(i,o)&&(s.m[o]=i[o]);if(l)var g=l(s)}for(t&&t(n);d[_.logo?((0,o.wg)(),(0,o.iD)("img",{key:0,src:_.logo,alt:"Gatus",class:"object-scale-down",style:{"max-width":"100px","min-width":"50px","min-height":"50px"}},null,8,h)):((0,o.wg)(),(0,o.iD)("img",p))])),_:1},8,["href"]))])]),_.buttons?((0,o.wg)(),(0,o.iD)("div",c,[((0,o.wg)(!0),(0,o.iD)(o.HY,null,(0,o.Ko)(_.buttons,(e=>((0,o.wg)(),(0,o.iD)("a",{key:e.name,href:e.link,target:"_blank",class:"px-2 py-0.5 font-medium select-none text-gray-600 hover:text-gray-500 dark:text-gray-300 dark:hover:text-gray-400 hover:underline"},(0,a.zw)(e.name),9,m)))),128))])):(0,o.kq)("",!0)]),(0,o.Wm)(D,{onShowTooltip:_.showTooltip},null,8,["onShowTooltip"])],2)):((0,o.wg)(),(0,o.j4)(S,{key:0,class:"h-64 w-64 px-4"})),r.config&&r.config.oidc&&!r.config.authenticated?((0,o.wg)(),(0,o.iD)("div",v,[w,f,(0,o._)("div",x,[e.$route&&e.$route.query.error?((0,o.wg)(),(0,o.iD)("div",y,[(0,o._)("div",k,["access_denied"===e.$route.query.error?((0,o.wg)(),(0,o.iD)("span",T,"You do not have access to this status page")):((0,o.wg)(),(0,o.iD)("span",b,(0,a.zw)(e.$route.query.error),1))])])):(0,o.kq)("",!0),(0,o._)("div",null,[(0,o._)("a",{href:`${r.SERVER_URL}/oidc/login`,class:"max-w-lg mx-auto w-full flex justify-center py-3 px-4 border border-green-800 rounded-md shadow-lg text-sm text-white bg-green-700 bg-gradient-to-r from-green-600 to-green-700 hover:from-green-700 hover:to-green-800"}," Login with OIDC ",8,R)])])])):(0,o.kq)("",!0),(0,o.Wm)(I,{result:r.tooltip.result,event:r.tooltip.event},null,8,["result","event"]),(0,o.Wm)(A)],64)}const S=e=>((0,o.dD)("data-v-a4b3d200"),e=e(),(0,o.Cn)(),e),D={id:"social"},I=S((()=>(0,o._)("a",{href:"https://github.com/TwiN/gatus",target:"_blank",title:"Gatus on GitHub"},[(0,o._)("svg",{xmlns:"http://www.w3.org/2000/svg",width:"32",height:"32",viewBox:"0 0 16 16",class:"hover:scale-110"},[(0,o._)("path",{fill:"gray",d:"M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"})])],-1))),A=[I];function C(e,t,s,n,a,r){return(0,o.wg)(),(0,o.iD)("div",D,A)}var $={name:"Social"},P=s(3744);const E=(0,P.Z)($,[["render",C],["__scopeId","data-v-a4b3d200"]]);var H=E;const M=(0,o._)("div",{class:"tooltip-title"},"Timestamp:",-1),L={id:"tooltip-timestamp"},U=(0,o._)("div",{class:"tooltip-title"},"Response time:",-1),W={id:"tooltip-response-time"},O=(0,o._)("div",{class:"tooltip-title"},"Conditions:",-1),j={id:"tooltip-conditions"},q=(0,o._)("br",null,null,-1),B={key:0,id:"tooltip-errors-container"},z=(0,o._)("div",{class:"tooltip-title"},"Errors:",-1),Y={id:"tooltip-errors"},N=(0,o._)("br",null,null,-1);function Z(e,t,s,n,r,i){return(0,o.wg)(),(0,o.iD)("div",{id:"tooltip",ref:"tooltip",class:(0,a.C_)(r.hidden?"invisible":""),style:(0,a.j5)("top:"+r.top+"px; left:"+r.left+"px")},[s.result?(0,o.WI)(e.$slots,"default",{key:0},(()=>[M,(0,o._)("code",L,(0,a.zw)(e.prettifyTimestamp(s.result.timestamp)),1),U,(0,o._)("code",W,(0,a.zw)((s.result.duration/1e6).toFixed(0))+"ms",1),O,(0,o._)("code",j,[((0,o.wg)(!0),(0,o.iD)(o.HY,null,(0,o.Ko)(s.result.conditionResults,(t=>(0,o.WI)(e.$slots,"default",{key:t},(()=>[(0,o.Uk)((0,a.zw)(t.success?"✓":"X")+" ~ "+(0,a.zw)(t.condition),1),q])))),128))]),s.result.errors&&s.result.errors.length?((0,o.wg)(),(0,o.iD)("div",B,[z,(0,o._)("code",Y,[((0,o.wg)(!0),(0,o.iD)(o.HY,null,(0,o.Ko)(s.result.errors,(t=>(0,o.WI)(e.$slots,"default",{key:t},(()=>[(0,o.Uk)(" - "+(0,a.zw)(t),1),N])))),128))])])):(0,o.kq)("",!0)])):(0,o.kq)("",!0)],6)}s(5306);const G={methods:{generatePrettyTimeAgo(e){let t=(new Date).getTime()-new Date(e).getTime();if(t<500)return"now";if(t>2592e5){let e=(t/864e5).toFixed(0);return e+" day"+("1"!==e?"s":"")+" ago"}if(t>36e5){let e=(t/36e5).toFixed(0);return e+" hour"+("1"!==e?"s":"")+" ago"}if(t>6e4){let e=(t/6e4).toFixed(0);return e+" minute"+("1"!==e?"s":"")+" ago"}let s=(t/1e3).toFixed(0);return s+" second"+("1"!==s?"s":"")+" ago"},generatePrettyTimeDifference(e,t){let s=Math.ceil((new Date(e)-new Date(t))/1e3/60);return s+(1===s?" minute":" minutes")},prettifyTimestamp(e){let t=new Date(e),s=t.getFullYear(),n=(t.getMonth()+1<10?"0":"")+(t.getMonth()+1),o=(t.getDate()<10?"0":"")+t.getDate(),a=(t.getHours()<10?"0":"")+t.getHours(),r=(t.getMinutes()<10?"0":"")+t.getMinutes(),i=(t.getSeconds()<10?"0":"")+t.getSeconds();return s+"-"+n+"-"+o+" "+a+":"+r+":"+i}}};var F={name:"Endpoints",props:{event:Event,result:Object},mixins:[G],methods:{htmlEntities(e){return String(e).replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")},reposition(){if(this.event&&this.event.type)if("mouseenter"===this.event.type){let e=this.event.target.getBoundingClientRect().y+30,t=this.event.target.getBoundingClientRect().x,s=this.$refs.tooltip.getBoundingClientRect();t+window.scrollX+s.width+50>document.body.getBoundingClientRect().width&&(t=this.event.target.getBoundingClientRect().x-s.width+this.event.target.getBoundingClientRect().width,t<0&&(t+=-t)),e+window.scrollY+s.height+50>document.body.getBoundingClientRect().height&&e>=0&&(e=this.event.target.getBoundingClientRect().y-(s.height+10),e<0&&(e=this.event.target.getBoundingClientRect().y+30)),this.top=e,this.left=t}else"mouseleave"===this.event.type&&(this.hidden=!0)}},watch:{event:function(e){e&&e.type&&("mouseenter"===e.type?this.hidden=!1:"mouseleave"===e.type&&(this.hidden=!0))}},updated(){this.reposition()},created(){this.reposition()},data(){return{hidden:!0,top:0,left:0}}};const K=(0,P.Z)(F,[["render",Z]]);var V=K;const J={class:"flex justify-center items-center mx-auto"},X=(0,o._)("img",{class:(0,a.C_)("animate-spin opacity-60 rounded-full"),src:r,alt:"Gatus logo"},null,-1),Q=[X];function ee(e,t,s,n,a,r){return(0,o.wg)(),(0,o.iD)("div",J,Q)}var te={};const se=(0,P.Z)(te,[["render",ee]]);var ne=se,oe={name:"App",components:{Loading:ne,Social:H,Tooltip:V},methods:{fetchConfig(){fetch(`${ms}/api/v1/config`,{credentials:"include"}).then((e=>{this.retrievedConfig=!0,200===e.status&&e.json().then((e=>{this.config=e}))}))},showTooltip(e,t){this.tooltip={result:e,event:t}}},computed:{logo(){return window.config&&window.config.logo&&"{{ .Logo }}"!==window.config.logo?window.config.logo:""},header(){return window.config&&window.config.header&&"{{ .Header }}"!==window.config.header?window.config.header:"Health Status"},link(){return window.config&&window.config.link&&"{{ .Link }}"!==window.config.link?window.config.link:null},buttons(){return window.config&&window.config.buttons?window.config.buttons:[]}},data(){return{error:"",retrievedConfig:!1,config:{oidc:!1,authenticated:!0},tooltip:{},SERVER_URL:ms}},created(){this.fetchConfig()}};const ae=(0,P.Z)(oe,[["render",_]]);var re=ae,ie=s(2119);function le(e,t,s,a,r,i){const l=(0,o.up)("Loading"),d=(0,o.up)("Endpoints"),g=(0,o.up)("Pagination"),u=(0,o.up)("Settings");return(0,o.wg)(),(0,o.iD)(o.HY,null,[r.retrievedData?(0,o.kq)("",!0):((0,o.wg)(),(0,o.j4)(l,{key:0,class:"h-64 w-64 px-4 my-24"})),(0,o.WI)(e.$slots,"default",{},(()=>[(0,o.wy)((0,o.Wm)(d,{endpointStatuses:r.endpointStatuses,showStatusOnHover:!0,onShowTooltip:i.showTooltip,onToggleShowAverageResponseTime:i.toggleShowAverageResponseTime,showAverageResponseTime:r.showAverageResponseTime},null,8,["endpointStatuses","onShowTooltip","onToggleShowAverageResponseTime","showAverageResponseTime"]),[[n.F8,r.retrievedData]]),(0,o.wy)((0,o.Wm)(g,{onPage:i.changePage},null,8,["onPage"]),[[n.F8,r.retrievedData]])])),(0,o.Wm)(u,{onRefreshData:i.fetchData},null,8,["onRefreshData"])],64)}s(3948);const de={id:"settings",class:"flex bg-gray-200 border-gray-300 rounded border shadow dark:text-gray-200 dark:bg-gray-800 dark:border-gray-500"},ge={class:"text-xs text-gray-600 rounded-xl py-1.5 px-1.5 dark:text-gray-200"},ue=["selected"],he=["selected"],pe=["selected"],ce=["selected"],me=["selected"],ve=["selected"];function we(e,t,s,n,a,r){const i=(0,o.up)("ArrowPathIcon"),l=(0,o.up)("SunIcon"),d=(0,o.up)("MoonIcon");return(0,o.wg)(),(0,o.iD)("div",de,[(0,o._)("div",ge,[(0,o.Wm)(i,{class:"w-3"})]),(0,o._)("select",{class:"text-center text-gray-500 text-xs dark:text-gray-200 dark:bg-gray-800 border-r border-l border-gray-300 dark:border-gray-500",id:"refresh-rate",ref:"refreshInterval",onChange:t[0]||(t[0]=(...e)=>r.handleChangeRefreshInterval&&r.handleChangeRefreshInterval(...e))},[(0,o._)("option",{value:"10",selected:10===a.refreshInterval},"10s",8,ue),(0,o._)("option",{value:"30",selected:30===a.refreshInterval},"30s",8,he),(0,o._)("option",{value:"60",selected:60===a.refreshInterval},"1m",8,pe),(0,o._)("option",{value:"120",selected:120===a.refreshInterval},"2m",8,ce),(0,o._)("option",{value:"300",selected:300===a.refreshInterval},"5m",8,me),(0,o._)("option",{value:"600",selected:600===a.refreshInterval},"10m",8,ve)],544),(0,o._)("button",{onClick:t[1]||(t[1]=(...e)=>r.toggleDarkMode&&r.toggleDarkMode(...e)),class:"text-xs p-1"},[a.darkMode?(0,o.WI)(e.$slots,"default",{key:0},(()=>[(0,o.Wm)(l,{class:"w-4"})])):(0,o.WI)(e.$slots,"default",{key:1},(()=>[(0,o.Wm)(d,{class:"w-4 text-gray-500"})]))])])}var fe=s(6758),xe=s(4913),ye=s(7886),ke={name:"Settings",components:{ArrowPathIcon:ye.Z,MoonIcon:fe.Z,SunIcon:xe.Z},props:{},methods:{setRefreshInterval(e){sessionStorage.setItem("gatus:refresh-interval",e);let t=this;this.refreshIntervalHandler=setInterval((function(){t.refreshData()}),1e3*e)},refreshData(){this.$emit("refreshData")},handleChangeRefreshInterval(){this.refreshData(),clearInterval(this.refreshIntervalHandler),this.setRefreshInterval(this.$refs.refreshInterval.value)},toggleDarkMode(){"dark"===localStorage.theme?localStorage.theme="light":localStorage.theme="dark",this.applyTheme()},applyTheme(){"dark"===localStorage.theme||!("theme"in localStorage)&&window.matchMedia("(prefers-color-scheme: dark)").matches?(this.darkMode=!0,document.documentElement.classList.add("dark")):(this.darkMode=!1,document.documentElement.classList.remove("dark"))}},created(){10!==this.refreshInterval&&30!==this.refreshInterval&&60!==this.refreshInterval&&120!==this.refreshInterval&&300!==this.refreshInterval&&600!==this.refreshInterval&&(this.refreshInterval=300),this.setRefreshInterval(this.refreshInterval),this.applyTheme()},unmounted(){clearInterval(this.refreshIntervalHandler)},data(){return{refreshInterval:sessionStorage.getItem("gatus:refresh-interval")<10?300:parseInt(sessionStorage.getItem("gatus:refresh-interval")),refreshIntervalHandler:0,darkMode:!0}}};const Te=(0,P.Z)(ke,[["render",we]]);var be=Te;const Re={id:"results"};function _e(e,t,s,n,a,r){const i=(0,o.up)("EndpointGroup");return(0,o.wg)(),(0,o.iD)("div",Re,[((0,o.wg)(!0),(0,o.iD)(o.HY,null,(0,o.Ko)(a.endpointGroups,(t=>(0,o.WI)(e.$slots,"default",{key:t},(()=>[(0,o.Wm)(i,{endpoints:t.endpoints,name:t.name,onShowTooltip:r.showTooltip,onToggleShowAverageResponseTime:r.toggleShowAverageResponseTime,showAverageResponseTime:s.showAverageResponseTime},null,8,["endpoints","name","onShowTooltip","onToggleShowAverageResponseTime","showAverageResponseTime"])])))),128))])}const Se={class:"font-mono text-gray-400 text-xl font-medium pb-2 px-3 dark:text-gray-200 dark:hover:text-gray-500 dark:border-gray-500"},De={class:"endpoint-group-arrow mr-2"},Ie={key:0,class:"rounded-xl bg-red-600 text-white px-2 font-bold leading-6 float-right h-6 text-center hover:scale-110 text-sm",title:"Partial Outage"},Ae={key:1,class:"float-right text-green-600 w-7 hover:scale-110",title:"Operational"};function Ce(e,t,s,n,r,i){const l=(0,o.up)("CheckCircleIcon"),d=(0,o.up)("Endpoint");return(0,o.wg)(),(0,o.iD)("div",{class:(0,a.C_)(0===s.endpoints.length?"mt-3":"mt-4")},["undefined"!==s.name?(0,o.WI)(e.$slots,"default",{key:0},(()=>[(0,o._)("div",{class:"endpoint-group pt-2 border dark:bg-gray-800 dark:border-gray-500",onClick:t[0]||(t[0]=(...e)=>i.toggleGroup&&i.toggleGroup(...e))},[(0,o._)("h5",Se,[(0,o._)("span",De,(0,a.zw)(r.collapsed?"▼":"▲"),1),(0,o.Uk)(" "+(0,a.zw)(s.name)+" ",1),r.unhealthyCount?((0,o.wg)(),(0,o.iD)("span",Ie,(0,a.zw)(r.unhealthyCount),1)):((0,o.wg)(),(0,o.iD)("span",Ae,[(0,o.Wm)(l)]))])])])):(0,o.kq)("",!0),r.collapsed?(0,o.kq)("",!0):((0,o.wg)(),(0,o.iD)("div",{key:1,class:(0,a.C_)("undefined"===s.name?"":"endpoint-group-content")},[((0,o.wg)(!0),(0,o.iD)(o.HY,null,(0,o.Ko)(s.endpoints,((t,n)=>(0,o.WI)(e.$slots,"default",{key:n},(()=>[(0,o.Wm)(d,{data:t,maximumNumberOfResults:20,onShowTooltip:i.showTooltip,onToggleShowAverageResponseTime:i.toggleShowAverageResponseTime,showAverageResponseTime:s.showAverageResponseTime},null,8,["data","onShowTooltip","onToggleShowAverageResponseTime","showAverageResponseTime"])])))),128))],2))],2)}const $e={key:0,class:"endpoint px-3 py-3 border-l border-r border-t rounded-none hover:bg-gray-100 dark:hover:bg-gray-700 dark:border-gray-500"},Pe={class:"flex flex-wrap mb-2"},Ee={class:"w-3/4"},He={key:0,class:"text-gray-500 font-light"},Me={class:"w-1/4 text-right"},Le=["title"],Ue={class:"status-over-time flex flex-row"},We=["onMouseenter"],Oe=["onMouseenter"],je=["onMouseenter"],qe=["onMouseenter"],Be=["onMouseenter"],ze={class:"flex flex-wrap status-time-ago"},Ye={class:"w-1/2"},Ne={class:"w-1/2 text-right"},Ze=(0,o._)("div",{class:"w-1/2"}," ",-1);function Ge(e,t,s,n,r,i){const l=(0,o.up)("router-link");return s.data?((0,o.wg)(),(0,o.iD)("div",$e,[(0,o._)("div",Pe,[(0,o._)("div",Ee,[(0,o.Wm)(l,{to:i.generatePath(),class:"font-bold hover:text-blue-800 hover:underline dark:hover:text-blue-400",title:"View detailed endpoint health"},{default:(0,o.w5)((()=>[(0,o.Uk)((0,a.zw)(s.data.name),1)])),_:1},8,["to"]),s.data.results&&s.data.results.length&&s.data.results[s.data.results.length-1].hostname?((0,o.wg)(),(0,o.iD)("span",He," | "+(0,a.zw)(s.data.results[s.data.results.length-1].hostname),1)):(0,o.kq)("",!0)]),(0,o._)("div",Me,[s.data.results&&s.data.results.length?((0,o.wg)(),(0,o.iD)("span",{key:0,class:"font-light overflow-x-hidden cursor-pointer select-none hover:text-gray-500",onClick:t[0]||(t[0]=(...e)=>i.toggleShowAverageResponseTime&&i.toggleShowAverageResponseTime(...e)),title:s.showAverageResponseTime?"Average response time":"Minimum and maximum response time"},[s.showAverageResponseTime?(0,o.WI)(e.$slots,"default",{key:0},(()=>[(0,o.Uk)(" ~"+(0,a.zw)(r.averageResponseTime)+"ms ",1)])):(0,o.WI)(e.$slots,"default",{key:1},(()=>[(0,o.Uk)((0,a.zw)(r.minResponseTime===r.maxResponseTime?r.minResponseTime:r.minResponseTime+"-"+r.maxResponseTime)+"ms ",1)]))],8,Le)):(0,o.kq)("",!0)])]),(0,o._)("div",null,[(0,o._)("div",Ue,[s.data.results&&s.data.results.length?(0,o.WI)(e.$slots,"default",{key:0},(()=>[s.data.results.length[((0,o.wg)(!0),(0,o.iD)(o.HY,null,(0,o.Ko)(s.maximumNumberOfResults-s.data.results.length,(e=>((0,o.wg)(),(0,o.iD)("span",{key:e,class:"status rounded border border-dashed border-gray-400"}," ")))),128))])):(0,o.kq)("",!0),((0,o.wg)(!0),(0,o.iD)(o.HY,null,(0,o.Ko)(s.data.results,(s=>(0,o.WI)(e.$slots,"default",{key:s},(()=>[s.success?((0,o.wg)(),(0,o.iD)("span",{key:0,class:"status status-success rounded bg-success",onMouseenter:e=>i.showTooltip(s,e),onMouseleave:t[1]||(t[1]=e=>i.showTooltip(null,e))},null,40,We)):1===s.severity_status?((0,o.wg)(),(0,o.iD)("span",{key:1,class:"status status-warning rounded bg-cyan-600",onMouseenter:e=>i.showTooltip(s,e),onMouseleave:t[2]||(t[2]=e=>i.showTooltip(null,e))},null,40,Oe)):2===s.severity_status?((0,o.wg)(),(0,o.iD)("span",{key:2,class:"status status-warning rounded bg-yellow-600",onMouseenter:e=>i.showTooltip(s,e),onMouseleave:t[3]||(t[3]=e=>i.showTooltip(null,e))},null,40,je)):3===s.severity_status?((0,o.wg)(),(0,o.iD)("span",{key:3,class:"status status-warning rounded bg-orange-600",onMouseenter:e=>i.showTooltip(s,e),onMouseleave:t[4]||(t[4]=e=>i.showTooltip(null,e))},null,40,qe)):((0,o.wg)(),(0,o.iD)("span",{key:4,class:"status status-failure rounded bg-red-600",onMouseenter:e=>i.showTooltip(s,e),onMouseleave:t[5]||(t[5]=e=>i.showTooltip(null,e))},null,40,Be))])))),128))])):(0,o.WI)(e.$slots,"default",{key:1},(()=>[((0,o.wg)(!0),(0,o.iD)(o.HY,null,(0,o.Ko)(s.maximumNumberOfResults,(e=>((0,o.wg)(),(0,o.iD)("span",{key:e,class:"status rounded border border-dashed border-gray-400"}," ")))),128))]))])]),(0,o._)("div",ze,[s.data.results&&s.data.results.length?(0,o.WI)(e.$slots,"default",{key:0},(()=>[(0,o._)("div",Ye,(0,a.zw)(e.generatePrettyTimeAgo(s.data.results[0].timestamp)),1),(0,o._)("div",Ne,(0,a.zw)(e.generatePrettyTimeAgo(s.data.results[s.data.results.length-1].timestamp)),1)])):(0,o.WI)(e.$slots,"default",{key:1},(()=>[Ze]))])])):(0,o.kq)("",!0)}var Fe={name:"Endpoint",props:{maximumNumberOfResults:Number,data:Object,showAverageResponseTime:Boolean},emits:["showTooltip","toggleShowAverageResponseTime"],mixins:[G],methods:{updateMinAndMaxResponseTimes(){let e=null,t=null,s=0;for(let n in this.data.results){const o=parseInt((this.data.results[n].duration/1e6).toFixed(0));s+=o,(null==e||e>o)&&(e=o),(null==t||t0&&(this.endpoints[t].results[this.endpoints[t].results.length-1].success||e++);this.unhealthyCount=e},toggleGroup(){this.collapsed=!this.collapsed,sessionStorage.setItem(`gatus:endpoint-group:${this.name}:collapsed`,this.collapsed)},showTooltip(e,t){this.$emit("showTooltip",e,t)},toggleShowAverageResponseTime(){this.$emit("toggleShowAverageResponseTime")}},watch:{endpoints:function(){this.healthCheck()}},created(){this.healthCheck()},data(){return{unhealthyCount:0,collapsed:"true"===sessionStorage.getItem(`gatus:endpoint-group:${this.name}:collapsed`)}}};const Qe=(0,P.Z)(Xe,[["render",Ce]]);var et=Qe,tt={name:"Endpoints",components:{EndpointGroup:et},props:{showStatusOnHover:Boolean,endpointStatuses:Object,showAverageResponseTime:Boolean},emits:["showTooltip","toggleShowAverageResponseTime"],methods:{process(){let e={};for(let s in this.endpointStatuses){let t=this.endpointStatuses[s];e[t.group]&&0!==e[t.group].length||(e[t.group]=[]),e[t.group].push(t)}let t=[];for(let s in e)"undefined"!==s&&t.push({name:s,endpoints:e[s]});e["undefined"]&&t.push({name:"undefined",endpoints:e["undefined"]}),this.endpointGroups=t},showTooltip(e,t){this.$emit("showTooltip",e,t)},toggleShowAverageResponseTime(){this.$emit("toggleShowAverageResponseTime")}},watch:{endpointStatuses:function(){this.process()}},data(){return{userClickedStatus:!1,endpointGroups:[]}}};const st=(0,P.Z)(tt,[["render",_e]]);var nt=st;const ot={class:"mt-3 flex"},at={class:"flex-1"},rt={class:"flex-1 text-right"};function it(e,t,s,n,a,r){return(0,o.wg)(),(0,o.iD)("div",ot,[(0,o._)("div",at,[a.currentPage<5?((0,o.wg)(),(0,o.iD)("button",{key:0,onClick:t[0]||(t[0]=(...e)=>r.nextPage&&r.nextPage(...e)),class:"bg-gray-100 hover:bg-gray-200 text-gray-500 border border-gray-200 px-2 rounded font-mono dark:bg-gray-700 dark:text-gray-200 dark:border-gray-500 dark:hover:bg-gray-600"},"<")):(0,o.kq)("",!0)]),(0,o._)("div",rt,[a.currentPage>1?((0,o.wg)(),(0,o.iD)("button",{key:0,onClick:t[1]||(t[1]=(...e)=>r.previousPage&&r.previousPage(...e)),class:"bg-gray-100 hover:bg-gray-200 text-gray-500 border border-gray-200 px-2 rounded font-mono dark:bg-gray-700 dark:text-gray-200 dark:border-gray-500 dark:hover:bg-gray-600"},">")):(0,o.kq)("",!0)])])}var lt={name:"Pagination",components:{},emits:["page"],methods:{nextPage(){this.currentPage++,this.$emit("page",this.currentPage)},previousPage(){this.currentPage--,this.$emit("page",this.currentPage)}},data(){return{currentPage:1}}};const dt=(0,P.Z)(lt,[["render",it]]);var gt=dt,ut={name:"Home",components:{Loading:ne,Pagination:gt,Endpoints:nt,Settings:be},emits:["showTooltip","toggleShowAverageResponseTime"],methods:{fetchData(){fetch(`${ms}/api/v1/endpoints/statuses?page=${this.currentPage}`,{credentials:"include"}).then((e=>{this.retrievedData=!0,200===e.status?e.json().then((e=>{JSON.stringify(this.endpointStatuses)!==JSON.stringify(e)&&(this.endpointStatuses=e)})):e.text().then((e=>{console.log(`[Home][fetchData] Error: ${e}`)}))}))},changePage(e){this.retrievedData=!1,this.currentPage=e,this.fetchData()},showTooltip(e,t){this.$emit("showTooltip",e,t)},toggleShowAverageResponseTime(){this.showAverageResponseTime=!this.showAverageResponseTime}},data(){return{endpointStatuses:[],currentPage:1,showAverageResponseTime:!0,retrievedData:!1}},created(){this.retrievedData=!1,this.fetchData()}};const ht=(0,P.Z)(ut,[["render",le]]);var pt=ht;const ct=e=>((0,o.dD)("data-v-dd19f9b4"),e=e(),(0,o.Cn)(),e),mt=(0,o.Uk)(" ← "),vt=ct((()=>(0,o._)("h1",{class:"text-xl xl:text-3xl font-mono text-gray-400"},"RECENT CHECKS",-1))),wt=ct((()=>(0,o._)("hr",{class:"mb-4"},null,-1))),ft={key:1,class:"mt-12"},xt=ct((()=>(0,o._)("h1",{class:"text-xl xl:text-3xl font-mono text-gray-400"},"UPTIME",-1))),yt=ct((()=>(0,o._)("hr",null,null,-1))),kt={class:"flex space-x-4 text-center text-2xl mt-6 relative bottom-2 mb-10"},Tt={class:"flex-1"},bt=ct((()=>(0,o._)("h2",{class:"text-sm text-gray-400 mb-1"},"Last 7 days",-1))),Rt=["src"],_t={class:"flex-1"},St=ct((()=>(0,o._)("h2",{class:"text-sm text-gray-400 mb-1"},"Last 24 hours",-1))),Dt=["src"],It={class:"flex-1"},At=ct((()=>(0,o._)("h2",{class:"text-sm text-gray-400 mb-1"},"Last hour",-1))),Ct=["src"],$t={key:2,class:"mt-12"},Pt=ct((()=>(0,o._)("h1",{class:"text-xl xl:text-3xl font-mono text-gray-400"},"RESPONSE TIME",-1))),Et=ct((()=>(0,o._)("hr",null,null,-1))),Ht=["src"],Mt={class:"flex space-x-4 text-center text-2xl mt-6 relative bottom-2 mb-10"},Lt={class:"flex-1"},Ut=ct((()=>(0,o._)("h2",{class:"text-sm text-gray-400 mb-1"},"Last 7 days",-1))),Wt=["src"],Ot={class:"flex-1"},jt=ct((()=>(0,o._)("h2",{class:"text-sm text-gray-400 mb-1"},"Last 24 hours",-1))),qt=["src"],Bt={class:"flex-1"},zt=ct((()=>(0,o._)("h2",{class:"text-sm text-gray-400 mb-1"},"Last hour",-1))),Yt=["src"],Nt={key:3},Zt=ct((()=>(0,o._)("h1",{class:"text-xl xl:text-3xl font-mono text-gray-400 mt-4"},"CURRENT HEALTH",-1))),Gt=ct((()=>(0,o._)("hr",null,null,-1))),Ft={class:"flex space-x-4 text-center text-2xl mt-6 relative bottom-2 mb-10"},Kt={class:"flex-1"},Vt=["src"],Jt={key:4},Xt=ct((()=>(0,o._)("h1",{class:"text-xl xl:text-3xl font-mono text-gray-400 mt-4"},"EVENTS",-1))),Qt=ct((()=>(0,o._)("hr",null,null,-1))),es={role:"list",class:"px-0 xl:px-24 divide-y divide-gray-200 dark:divide-gray-600"},ts={class:"text-sm sm:text-lg"},ss={class:"flex mt-1 text-xs sm:text-sm text-gray-400"},ns={class:"flex-2 text-left pl-12"},os={class:"flex-1 text-right"};function as(e,t,s,n,r,i){const l=(0,o.up)("router-link"),d=(0,o.up)("Endpoint"),g=(0,o.up)("Pagination"),u=(0,o.up)("ArrowUpCircleIcon"),h=(0,o.up)("ArrowDownCircleIcon"),p=(0,o.up)("PlayCircleIcon"),c=(0,o.up)("Settings");return(0,o.wg)(),(0,o.iD)(o.HY,null,[(0,o.Wm)(l,{to:"../",class:"absolute top-2 left-2 inline-block px-2 pb-0.5 text-lg text-black bg-gray-100 rounded hover:bg-gray-200 focus:outline-none border border-gray-200 dark:bg-gray-700 dark:text-gray-200 dark:border-gray-500 dark:hover:bg-gray-600"},{default:(0,o.w5)((()=>[mt])),_:1}),(0,o._)("div",null,[r.endpointStatus?(0,o.WI)(e.$slots,"default",{key:0},(()=>[vt,wt,(0,o.Wm)(d,{data:r.endpointStatus,maximumNumberOfResults:20,onShowTooltip:i.showTooltip,onToggleShowAverageResponseTime:i.toggleShowAverageResponseTime,showAverageResponseTime:r.showAverageResponseTime},null,8,["data","onShowTooltip","onToggleShowAverageResponseTime","showAverageResponseTime"]),(0,o.Wm)(g,{onPage:i.changePage},null,8,["onPage"])]),!0):(0,o.kq)("",!0),r.endpointStatus&&r.endpointStatus.key?((0,o.wg)(),(0,o.iD)("div",ft,[xt,yt,(0,o._)("div",kt,[(0,o._)("div",Tt,[bt,(0,o._)("img",{src:i.generateUptimeBadgeImageURL("7d"),alt:"7d uptime badge",class:"mx-auto"},null,8,Rt)]),(0,o._)("div",_t,[St,(0,o._)("img",{src:i.generateUptimeBadgeImageURL("24h"),alt:"24h uptime badge",class:"mx-auto"},null,8,Dt)]),(0,o._)("div",It,[At,(0,o._)("img",{src:i.generateUptimeBadgeImageURL("1h"),alt:"1h uptime badge",class:"mx-auto"},null,8,Ct)])])])):(0,o.kq)("",!0),r.endpointStatus&&r.endpointStatus.key?((0,o.wg)(),(0,o.iD)("div",$t,[Pt,Et,(0,o._)("img",{src:i.generateResponseTimeChartImageURL(),alt:"response time chart",class:"mt-6"},null,8,Ht),(0,o._)("div",Mt,[(0,o._)("div",Lt,[Ut,(0,o._)("img",{src:i.generateResponseTimeBadgeImageURL("7d"),alt:"7d response time badge",class:"mx-auto mt-2"},null,8,Wt)]),(0,o._)("div",Ot,[jt,(0,o._)("img",{src:i.generateResponseTimeBadgeImageURL("24h"),alt:"24h response time badge",class:"mx-auto mt-2"},null,8,qt)]),(0,o._)("div",Bt,[zt,(0,o._)("img",{src:i.generateResponseTimeBadgeImageURL("1h"),alt:"1h response time badge",class:"mx-auto mt-2"},null,8,Yt)])])])):(0,o.kq)("",!0),r.endpointStatus&&r.endpointStatus.key?((0,o.wg)(),(0,o.iD)("div",Nt,[Zt,Gt,(0,o._)("div",Ft,[(0,o._)("div",Kt,[(0,o._)("img",{src:i.generateHealthBadgeImageURL(),alt:"health badge",class:"mx-auto"},null,8,Vt)])])])):(0,o.kq)("",!0),r.endpointStatus&&r.endpointStatus.key?((0,o.wg)(),(0,o.iD)("div",Jt,[Xt,Qt,(0,o._)("ul",es,[((0,o.wg)(!0),(0,o.iD)(o.HY,null,(0,o.Ko)(r.events,(t=>((0,o.wg)(),(0,o.iD)("li",{key:t,class:"p-3 my-4"},[(0,o._)("h2",ts,["HEALTHY"===t.type?((0,o.wg)(),(0,o.j4)(u,{key:0,class:"w-8 inline mr-2 text-green-600"})):"UNHEALTHY"===t.type?((0,o.wg)(),(0,o.j4)(h,{key:1,class:"w-8 inline mr-2 text-red-500"})):"START"===t.type?((0,o.wg)(),(0,o.j4)(p,{key:2,class:"w-8 inline mr-2 text-gray-400 dark:text-gray-100"})):(0,o.kq)("",!0),(0,o.Uk)(" "+(0,a.zw)(t.fancyText),1)]),(0,o._)("div",ss,[(0,o._)("div",ns,(0,a.zw)(e.prettifyTimestamp(t.timestamp)),1),(0,o._)("div",os,(0,a.zw)(t.fancyTimeAgo),1)])])))),128))])])):(0,o.kq)("",!0)]),(0,o.Wm)(c,{onRefreshData:i.fetchData},null,8,["onRefreshData"])],64)}var rs=s(9505),is=s(7163),ls=s(8585),ds={name:"Details",components:{Pagination:gt,Endpoint:Ve,Settings:be,ArrowDownCircleIcon:rs.Z,ArrowUpCircleIcon:is.Z,PlayCircleIcon:ls.Z},emits:["showTooltip"],mixins:[G],methods:{fetchData(){fetch(`${this.serverUrl}/api/v1/endpoints/${this.$route.params.key}/statuses?page=${this.currentPage}`,{credentials:"include"}).then((e=>{200===e.status?e.json().then((e=>{if(JSON.stringify(this.endpointStatus)!==JSON.stringify(e)){this.endpointStatus=e,this.uptime=e.uptime;let t=[];for(let s=e.events.length-1;s>=0;s--){let n=e.events[s];if(s===e.events.length-1)"UNHEALTHY"===n.type?n.fancyText="Endpoint is unhealthy":"HEALTHY"===n.type?n.fancyText="Endpoint is healthy":"START"===n.type&&(n.fancyText="Monitoring started");else{let t=e.events[s+1];"HEALTHY"===n.type?n.fancyText="Endpoint became healthy":"UNHEALTHY"===n.type?n.fancyText=t?"Endpoint was unhealthy for "+this.generatePrettyTimeDifference(t.timestamp,n.timestamp):"Endpoint became unhealthy":"START"===n.type&&(n.fancyText="Monitoring started")}n.fancyTimeAgo=this.generatePrettyTimeAgo(n.timestamp),t.push(n)}this.events=t}})):e.text().then((e=>{console.log(`[Details][fetchData] Error: ${e}`)}))}))},generateHealthBadgeImageURL(){return`${this.serverUrl}/api/v1/endpoints/${this.endpointStatus.key}/health/badge.svg`},generateUptimeBadgeImageURL(e){return`${this.serverUrl}/api/v1/endpoints/${this.endpointStatus.key}/uptimes/${e}/badge.svg`},generateResponseTimeBadgeImageURL(e){return`${this.serverUrl}/api/v1/endpoints/${this.endpointStatus.key}/response-times/${e}/badge.svg`},generateResponseTimeChartImageURL(){return`${this.serverUrl}/api/v1/endpoints/${this.endpointStatus.key}/response-times/24h/chart.svg`},changePage(e){this.currentPage=e,this.fetchData()},showTooltip(e,t){this.$emit("showTooltip",e,t)},toggleShowAverageResponseTime(){this.showAverageResponseTime=!this.showAverageResponseTime}},data(){return{endpointStatus:{},uptime:{},events:[],hourlyAverageResponseTime:{},serverUrl:"."===ms?"..":ms,currentPage:1,showAverageResponseTime:!0,chartLabels:[],chartValues:[]}},created(){this.fetchData()}};const gs=(0,P.Z)(ds,[["render",as],["__scopeId","data-v-dd19f9b4"]]);var us=gs;const hs=[{path:"/",name:"Home",component:pt},{path:"/endpoints/:key",name:"Details",component:us}],ps=(0,ie.p7)({history:(0,ie.PO)("/"),routes:hs});var cs=ps;const ms="";(0,n.ri)(re).use(cs).mount("#app")}},t={};function s(n){var o=t[n];if(void 0!==o)return o.exports;var a=t[n]={exports:{}};return e[n](a,a.exports,s),a.exports}s.m=e,function(){var e=[];s.O=function(t,n,o,a){if(!n){var r=1/0;for(g=0;g=a)&&Object.keys(s.O).every((function(e){return s.O[e](n[l])}))?n.splice(l--,1):(i=!1,a0&&e[g-1][2]>a;g--)e[g]=e[g-1];e[g]=[n,o,a]}}(),function(){s.d=function(e,t){for(var n in t)s.o(t,n)&&!s.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})}}(),function(){s.g=function(){if("object"===typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"===typeof window)return window}}()}(),function(){s.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)}}(),function(){s.p="/"}(),function(){var e={143:0};s.O.j=function(t){return 0===e[t]};var t=function(t,n){var o,a,r=n[0],i=n[1],l=n[2],d=0;if(r.some((function(t){return 0!==e[t]}))){for(o in i)s.o(i,o)&&(s.m[o]=i[o]);if(l)var g=l(s)}for(t&&t(n);d