Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d412678ff | ||
|
|
48c7514fa5 | ||
|
|
d7b437595c | ||
|
|
50f530a05c | ||
|
|
2a632e8f87 | ||
|
|
857ad584e7 | ||
|
|
8b3b2f70bf | ||
|
|
4308f2c1ef | ||
|
|
425c93ed8f | ||
|
|
752e82d80b | ||
|
|
e91462ce41 | ||
|
|
347297a8ea | ||
|
|
56dbe2fea0 | ||
|
|
ebcca4317d | ||
|
|
e6355dfee8 | ||
|
|
7309888db5 | ||
|
|
f60eee86ee |
No files matched your search
@@ -14,10 +14,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Set up Go 1.15
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.15
|
||||
go-version: 1.16
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
- name: Build binary to make sure it works
|
||||
|
||||
@@ -6,6 +6,7 @@ jobs:
|
||||
build:
|
||||
name: Publish
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -18,6 +18,7 @@ core applications: https://status.twinnation.org/
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Why Gatus?](#why-gatus)
|
||||
- [Features](#features)
|
||||
- [Usage](#usage)
|
||||
- [Configuration](#configuration)
|
||||
@@ -54,6 +55,25 @@ core applications: https://status.twinnation.org/
|
||||
- [API](#API)
|
||||
|
||||
|
||||
## Why Gatus?
|
||||
|
||||
Before getting into the specifics, I want to address the most common question:
|
||||
> Why would I use Gatus when I can just use Prometheus’ Alertmanager, Cloudwatch or even Splunk?
|
||||
|
||||
Neither of these can tell you that there’s a problem if there are no clients actively calling the endpoint.
|
||||
In other words, it's because monitoring metrics mostly rely on existing traffic, which effectively means that unless
|
||||
your clients are already experiencing a problem, you won't be notified.
|
||||
|
||||
Gatus, on the other hand, allows you to configure health checks for each of your features, which in turn allows it to
|
||||
monitor these features and potentially alert you before any clients are impacted.
|
||||
|
||||
A sign you may want to look into Gatus is by simply asking yourself whether you'd receive an alert if your load balancer
|
||||
was to go down right now. Will any of your existing alerts by triggered? Your metrics won’t report an increase in errors
|
||||
if there’s no traffic that makes it to your applications. This puts you in a situation where your clients are the ones
|
||||
that will notify you about the degradation of your services rather than you reassuring them that you're working on
|
||||
fixing the issue before they even know about it.
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
The main features of Gatus are:
|
||||
@@ -113,7 +133,7 @@ Note that you can also add environment variables in the configuration file (e.g.
|
||||
| `services[].url` | URL to send the request to | Required `""` |
|
||||
| `services[].method` | Request method | `GET` |
|
||||
| `services[].insecure` | Whether to skip verifying the server's certificate chain and host name | `false` |
|
||||
| `services[].conditions` | Conditions used to determine the health of the service. See [Conditions](#conditions) | `[]` |
|
||||
| `services[].conditions` | Conditions used to determine the health of the service. See [Conditions](#conditions). | `[]` |
|
||||
| `services[].interval` | Duration to wait between every status check | `60s` |
|
||||
| `services[].graphql` | Whether to wrap the body in a query param (`{"query":"$body"}`) | `false` |
|
||||
| `services[].body` | Request body | `""` |
|
||||
@@ -121,13 +141,13 @@ Note that you can also add environment variables in the configuration file (e.g.
|
||||
| `services[].dns` | Configuration for a service of type DNS. See [Monitoring a service using DNS queries](#monitoring-a-service-using-dns-queries). | `""` |
|
||||
| `services[].dns.query-type` | Query type for DNS service | `""` |
|
||||
| `services[].dns.query-name` | Query name for DNS service | `""` |
|
||||
| `services[].alerts[].type` | Type of alert. Valid types: `slack`, `discord`m `pagerduty`, `twilio`, `mattermost`, `messagebird`, `custom` | Required `""` |
|
||||
| `services[].alerts[].type` | Type of alert. Valid types: `slack`, `discord`, `pagerduty`, `twilio`, `mattermost`, `messagebird`, `custom` | Required `""` |
|
||||
| `services[].alerts[].enabled` | Whether to enable the alert | `false` |
|
||||
| `services[].alerts[].failure-threshold` | Number of failures in a row needed before triggering the alert | `3` |
|
||||
| `services[].alerts[].success-threshold` | Number of successes in a row before an ongoing incident is marked as resolved | `2` |
|
||||
| `services[].alerts[].send-on-resolved` | Whether to send a notification once a triggered alert is marked as resolved | `false` |
|
||||
| `services[].alerts[].description` | Description of the alert. Will be included in the alert sent | `""` |
|
||||
| `alerting` | Configuration for alerting. See [Alerting](#alerting) | `{}` |
|
||||
| `alerting` | Configuration for alerting. See [Alerting](#alerting). | `{}` |
|
||||
| `security` | Security configuration | `{}` |
|
||||
| `security.basic` | Basic authentication security configuration | `{}` |
|
||||
| `security.basic.username` | Username for Basic authentication | Required `""` |
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
// AlertProvider is the configuration necessary for sending an alert using Slack
|
||||
type AlertProvider struct {
|
||||
WebhookURL string `yaml:"webhook-url"`
|
||||
WebhookURL string `yaml:"webhook-url"` // Slack webhook URL
|
||||
}
|
||||
|
||||
// IsValid returns whether the provider's configuration is valid
|
||||
|
||||
@@ -42,11 +42,10 @@ func (provider *AlertProvider) ToCustomAlertProvider(service *core.Service, aler
|
||||
} else {
|
||||
text = fmt.Sprintf("⛑ *Gatus* \\n%s \\n*Condition results*\\n%s", message, results)
|
||||
}
|
||||
|
||||
return &custom.AlertProvider{
|
||||
URL: fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", provider.Token),
|
||||
Method: http.MethodPost,
|
||||
Body: fmt.Sprintf(`{"chat_id": "%s", "text": "%s", "parse_mode": "MARKDOWN" }`, provider.ID, text),
|
||||
Body: fmt.Sprintf(`{"chat_id": "%s", "text": "%s", "parse_mode": "MARKDOWN"}`, provider.ID, text),
|
||||
Headers: map[string]string{"Content-Type": "application/json"},
|
||||
}
|
||||
}
|
||||
+17
-2
@@ -4,6 +4,8 @@ import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/go-ping/ping"
|
||||
@@ -16,14 +18,27 @@ var (
|
||||
// pingTimeout is the timeout for the Ping function
|
||||
// This is mainly exposed for testing purposes
|
||||
pingTimeout = 5 * time.Second
|
||||
|
||||
// httpTimeout is the timeout for secureHTTPClient and insecureHTTPClient
|
||||
httpTimeout = 10 * time.Second
|
||||
)
|
||||
|
||||
func init() {
|
||||
// XXX: This is an undocumented feature. See https://github.com/TwinProduction/gatus/issues/104.
|
||||
httpTimeoutInSecondsFromEnvironmentVariable := os.Getenv("HTTP_CLIENT_TIMEOUT_IN_SECONDS")
|
||||
if len(httpTimeoutInSecondsFromEnvironmentVariable) > 0 {
|
||||
if httpTimeoutInSeconds, err := strconv.Atoi(httpTimeoutInSecondsFromEnvironmentVariable); err == nil {
|
||||
httpTimeout = time.Duration(httpTimeoutInSeconds) * time.Second
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetHTTPClient returns the shared HTTP client
|
||||
func GetHTTPClient(insecure bool) *http.Client {
|
||||
if insecure {
|
||||
if insecureHTTPClient == nil {
|
||||
insecureHTTPClient = &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
Timeout: httpTimeout,
|
||||
Transport: &http.Transport{
|
||||
MaxIdleConns: 100,
|
||||
MaxIdleConnsPerHost: 20,
|
||||
@@ -38,7 +53,7 @@ func GetHTTPClient(insecure bool) *http.Client {
|
||||
}
|
||||
if secureHTTPClient == nil {
|
||||
secureHTTPClient = &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
Timeout: httpTimeout,
|
||||
Transport: &http.Transport{
|
||||
MaxIdleConns: 100,
|
||||
MaxIdleConnsPerHost: 20,
|
||||
|
||||
+10
@@ -36,3 +36,13 @@ services:
|
||||
- "[STATUS] == 200"
|
||||
- "[BODY].deleted == false"
|
||||
- "len([BODY].text) > 0"
|
||||
|
||||
- name: example-dns-query
|
||||
url: "8.8.8.8" # Address of the DNS server to use
|
||||
interval: 5m
|
||||
dns:
|
||||
query-name: "example.com"
|
||||
query-type: "A"
|
||||
conditions:
|
||||
- "[BODY] == 93.184.216.34"
|
||||
- "[DNS_RCODE] == NOERROR"
|
||||
+1
-1
@@ -232,7 +232,7 @@ func sanitizeAndResolve(elements []string, result *Result) ([]string, []string)
|
||||
checkingForExistence = true
|
||||
element = strings.TrimSuffix(strings.TrimPrefix(element, HasFunctionPrefix), FunctionSuffix)
|
||||
}
|
||||
resolvedElement, resolvedElementLength, err := jsonpath.Eval(strings.TrimPrefix(element, BodyPlaceholder+"."), result.body)
|
||||
resolvedElement, resolvedElementLength, err := jsonpath.Eval(strings.TrimPrefix(strings.TrimPrefix(element, BodyPlaceholder), "."), result.body)
|
||||
if checkingForExistence {
|
||||
if err != nil {
|
||||
element = "false"
|
||||
|
||||
@@ -183,6 +183,13 @@ func TestCondition_evaluate(t *testing.T) {
|
||||
ExpectedSuccess: false,
|
||||
ExpectedOutput: "[BODY].user.firstName (john) == [BODY].user.lastName (doe)",
|
||||
},
|
||||
{
|
||||
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-int",
|
||||
Condition: Condition("[BODY].data.id == 1"),
|
||||
|
||||
+68
-28
@@ -1,6 +1,7 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -10,55 +11,70 @@ const (
|
||||
)
|
||||
|
||||
// Uptime is the struct that contains the relevant data for calculating the uptime as well as the uptime itself
|
||||
// and some other statistics
|
||||
type Uptime struct {
|
||||
// LastSevenDays is the uptime percentage over the past 7 days
|
||||
LastSevenDays float64 `json:"7d"`
|
||||
|
||||
// LastTwentyFourHours is the uptime percentage over the past 24 hours
|
||||
LastTwentyFourHours float64 `json:"24h"`
|
||||
|
||||
// LastHour is the uptime percentage over the past hour
|
||||
LastHour float64 `json:"1h"`
|
||||
LastSevenDays float64 `json:"7d"` // Uptime percentage over the past 7 days
|
||||
LastTwentyFourHours float64 `json:"24h"` // Uptime percentage over the past 24 hours
|
||||
LastHour float64 `json:"1h"` // Uptime percentage over the past hour
|
||||
|
||||
// SuccessfulExecutionsPerHour is a map containing the number of successes (value)
|
||||
// for every hourly unix timestamps (key)
|
||||
// Deprecated
|
||||
SuccessfulExecutionsPerHour map[int64]uint64 `json:"-"`
|
||||
|
||||
// TotalExecutionsPerHour is a map containing the total number of checks (value)
|
||||
// for every hourly unix timestamps (key)
|
||||
// Deprecated
|
||||
TotalExecutionsPerHour map[int64]uint64 `json:"-"`
|
||||
|
||||
// HourlyStatistics is a map containing metrics collected (value) for every hourly unix timestamps (key)
|
||||
HourlyStatistics map[int64]*HourlyUptimeStatistics `json:"-"`
|
||||
}
|
||||
|
||||
// HourlyUptimeStatistics is a struct containing all metrics collected over the course of an hour
|
||||
type HourlyUptimeStatistics struct {
|
||||
TotalExecutions uint64 // Total number of checks
|
||||
SuccessfulExecutions uint64 // Number of successful executions
|
||||
TotalExecutionsResponseTime uint64 // Total response time for all executions
|
||||
}
|
||||
|
||||
// NewUptime creates a new Uptime
|
||||
func NewUptime() *Uptime {
|
||||
return &Uptime{
|
||||
SuccessfulExecutionsPerHour: make(map[int64]uint64),
|
||||
TotalExecutionsPerHour: make(map[int64]uint64),
|
||||
HourlyStatistics: make(map[int64]*HourlyUptimeStatistics),
|
||||
}
|
||||
}
|
||||
|
||||
// ProcessResult processes the result by extracting the relevant from the result and recalculating the uptime
|
||||
// if necessary
|
||||
func (uptime *Uptime) ProcessResult(result *Result) {
|
||||
if uptime.SuccessfulExecutionsPerHour == nil || uptime.TotalExecutionsPerHour == nil {
|
||||
uptime.SuccessfulExecutionsPerHour = make(map[int64]uint64)
|
||||
uptime.TotalExecutionsPerHour = make(map[int64]uint64)
|
||||
// XXX: Remove this on v3.0.0
|
||||
if len(uptime.SuccessfulExecutionsPerHour) != 0 || len(uptime.TotalExecutionsPerHour) != 0 {
|
||||
uptime.migrateToHourlyStatistics()
|
||||
}
|
||||
if uptime.HourlyStatistics == nil {
|
||||
uptime.HourlyStatistics = make(map[int64]*HourlyUptimeStatistics)
|
||||
}
|
||||
unixTimestampFlooredAtHour := result.Timestamp.Unix() - (result.Timestamp.Unix() % 3600)
|
||||
if result.Success {
|
||||
uptime.SuccessfulExecutionsPerHour[unixTimestampFlooredAtHour]++
|
||||
hourlyStats, _ := uptime.HourlyStatistics[unixTimestampFlooredAtHour]
|
||||
if hourlyStats == nil {
|
||||
hourlyStats = &HourlyUptimeStatistics{}
|
||||
uptime.HourlyStatistics[unixTimestampFlooredAtHour] = hourlyStats
|
||||
}
|
||||
uptime.TotalExecutionsPerHour[unixTimestampFlooredAtHour]++
|
||||
if result.Success {
|
||||
hourlyStats.SuccessfulExecutions++
|
||||
}
|
||||
hourlyStats.TotalExecutions++
|
||||
hourlyStats.TotalExecutionsResponseTime += uint64(result.Duration.Milliseconds())
|
||||
// Clean up only when we're starting to have too many useless keys
|
||||
// Note that this is only triggered when there are more entries than there should be after
|
||||
// 10 days, despite the fact that we are deleting everything that's older than 7 days.
|
||||
// This is to prevent re-iterating on every `ProcessResult` as soon as the uptime has been logged for 7 days.
|
||||
if len(uptime.TotalExecutionsPerHour) > numberOfHoursInTenDays {
|
||||
if len(uptime.HourlyStatistics) > numberOfHoursInTenDays {
|
||||
sevenDaysAgo := time.Now().Add(-(sevenDays + time.Hour)).Unix()
|
||||
for hourlyUnixTimestamp := range uptime.TotalExecutionsPerHour {
|
||||
for hourlyUnixTimestamp := range uptime.HourlyStatistics {
|
||||
if sevenDaysAgo > hourlyUnixTimestamp {
|
||||
delete(uptime.TotalExecutionsPerHour, hourlyUnixTimestamp)
|
||||
delete(uptime.SuccessfulExecutionsPerHour, hourlyUnixTimestamp)
|
||||
delete(uptime.HourlyStatistics, hourlyUnixTimestamp)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,17 +102,20 @@ func (uptime *Uptime) recalculate() {
|
||||
timestamp := now.Add(-sevenDays)
|
||||
for now.Sub(timestamp) >= 0 {
|
||||
hourlyUnixTimestamp := timestamp.Unix() - (timestamp.Unix() % 3600)
|
||||
successCountForTimestamp := uptime.SuccessfulExecutionsPerHour[hourlyUnixTimestamp]
|
||||
totalCountForTimestamp := uptime.TotalExecutionsPerHour[hourlyUnixTimestamp]
|
||||
uptimeBrackets["7d_success"] += successCountForTimestamp
|
||||
uptimeBrackets["7d_total"] += totalCountForTimestamp
|
||||
hourlyStats := uptime.HourlyStatistics[hourlyUnixTimestamp]
|
||||
if hourlyStats == nil || hourlyStats.TotalExecutions == 0 {
|
||||
timestamp = timestamp.Add(time.Hour)
|
||||
continue
|
||||
}
|
||||
uptimeBrackets["7d_success"] += hourlyStats.SuccessfulExecutions
|
||||
uptimeBrackets["7d_total"] += hourlyStats.TotalExecutions
|
||||
if now.Sub(timestamp) <= 24*time.Hour {
|
||||
uptimeBrackets["24h_success"] += successCountForTimestamp
|
||||
uptimeBrackets["24h_total"] += totalCountForTimestamp
|
||||
uptimeBrackets["24h_success"] += hourlyStats.SuccessfulExecutions
|
||||
uptimeBrackets["24h_total"] += hourlyStats.TotalExecutions
|
||||
}
|
||||
if now.Sub(timestamp) <= time.Hour {
|
||||
uptimeBrackets["1h_success"] += successCountForTimestamp
|
||||
uptimeBrackets["1h_total"] += totalCountForTimestamp
|
||||
uptimeBrackets["1h_success"] += hourlyStats.SuccessfulExecutions
|
||||
uptimeBrackets["1h_total"] += hourlyStats.TotalExecutions
|
||||
}
|
||||
timestamp = timestamp.Add(time.Hour)
|
||||
}
|
||||
@@ -110,3 +129,24 @@ func (uptime *Uptime) recalculate() {
|
||||
uptime.LastHour = float64(uptimeBrackets["1h_success"]) / float64(uptimeBrackets["1h_total"])
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: Remove this on v3.0.0
|
||||
// Deprecated
|
||||
func (uptime *Uptime) migrateToHourlyStatistics() {
|
||||
log.Println("[migrateToHourlyStatistics] Got", len(uptime.SuccessfulExecutionsPerHour), "entries for successful executions and", len(uptime.TotalExecutionsPerHour), "entries for total executions")
|
||||
uptime.HourlyStatistics = make(map[int64]*HourlyUptimeStatistics)
|
||||
for hourlyUnixTimestamp, totalExecutions := range uptime.TotalExecutionsPerHour {
|
||||
if totalExecutions == 0 {
|
||||
log.Println("[migrateToHourlyStatistics] Skipping entry at", hourlyUnixTimestamp, "because total number of executions is 0")
|
||||
continue
|
||||
}
|
||||
uptime.HourlyStatistics[hourlyUnixTimestamp] = &HourlyUptimeStatistics{
|
||||
TotalExecutions: totalExecutions,
|
||||
SuccessfulExecutions: uptime.SuccessfulExecutionsPerHour[hourlyUnixTimestamp],
|
||||
TotalExecutionsResponseTime: 0,
|
||||
}
|
||||
}
|
||||
log.Println("[migrateToHourlyStatistics] Migrated", len(uptime.HourlyStatistics), "entries")
|
||||
uptime.SuccessfulExecutionsPerHour = nil
|
||||
uptime.TotalExecutionsPerHour = nil
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func BenchmarkUptime_ProcessResult(b *testing.B) {
|
||||
uptime := NewUptime()
|
||||
now := time.Now()
|
||||
now = time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, now.Location())
|
||||
// Start 12000 days ago
|
||||
timestamp := now.Add(-12000 * 24 * time.Hour)
|
||||
for n := 0; n < b.N; n++ {
|
||||
uptime.ProcessResult(&Result{
|
||||
Duration: 18 * time.Millisecond,
|
||||
Success: n%15 == 0,
|
||||
Timestamp: timestamp,
|
||||
})
|
||||
// Simulate service with an interval of 3 minutes
|
||||
timestamp = timestamp.Add(3 * time.Minute)
|
||||
}
|
||||
b.ReportAllocs()
|
||||
}
|
||||
+38
-20
@@ -12,33 +12,39 @@ func TestUptime_ProcessResult(t *testing.T) {
|
||||
|
||||
checkUptimes(t, serviceStatus, 0.00, 0.00, 0.00)
|
||||
|
||||
uptime.ProcessResult(&Result{Timestamp: time.Now().Add(-7 * 24 * time.Hour), Success: true})
|
||||
now := time.Now()
|
||||
now = time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, now.Location())
|
||||
uptime.ProcessResult(&Result{Timestamp: now.Add(-7 * 24 * time.Hour), Success: true})
|
||||
checkUptimes(t, serviceStatus, 1.00, 0.00, 0.00)
|
||||
|
||||
uptime.ProcessResult(&Result{Timestamp: time.Now().Add(-6 * 24 * time.Hour), Success: false})
|
||||
uptime.ProcessResult(&Result{Timestamp: now.Add(-6 * 24 * time.Hour), Success: false})
|
||||
checkUptimes(t, serviceStatus, 0.50, 0.00, 0.00)
|
||||
|
||||
uptime.ProcessResult(&Result{Timestamp: time.Now().Add(-8 * 24 * time.Hour), Success: true})
|
||||
uptime.ProcessResult(&Result{Timestamp: now.Add(-8 * 24 * time.Hour), Success: true})
|
||||
checkUptimes(t, serviceStatus, 0.50, 0.00, 0.00)
|
||||
|
||||
uptime.ProcessResult(&Result{Timestamp: time.Now().Add(-24 * time.Hour), Success: true})
|
||||
uptime.ProcessResult(&Result{Timestamp: time.Now().Add(-12 * time.Hour), Success: true})
|
||||
uptime.ProcessResult(&Result{Timestamp: now.Add(-24 * time.Hour), Success: true})
|
||||
uptime.ProcessResult(&Result{Timestamp: now.Add(-12 * time.Hour), Success: true})
|
||||
checkUptimes(t, serviceStatus, 0.75, 1.00, 0.00)
|
||||
|
||||
uptime.ProcessResult(&Result{Timestamp: time.Now().Add(-1 * time.Hour), Success: true})
|
||||
uptime.ProcessResult(&Result{Timestamp: time.Now().Add(-30 * time.Minute), Success: false})
|
||||
uptime.ProcessResult(&Result{Timestamp: time.Now().Add(-15 * time.Minute), Success: false})
|
||||
uptime.ProcessResult(&Result{Timestamp: time.Now().Add(-10 * time.Minute), Success: false})
|
||||
uptime.ProcessResult(&Result{Timestamp: now.Add(-1 * time.Hour), Success: true, Duration: 10 * time.Millisecond})
|
||||
checkHourlyStatistics(t, uptime.HourlyStatistics[now.Unix()-now.Unix()%3600-3600], 10, 1, 1)
|
||||
uptime.ProcessResult(&Result{Timestamp: now.Add(-30 * time.Minute), Success: false, Duration: 500 * time.Millisecond})
|
||||
checkHourlyStatistics(t, uptime.HourlyStatistics[now.Unix()-now.Unix()%3600-3600], 510, 2, 1)
|
||||
uptime.ProcessResult(&Result{Timestamp: now.Add(-15 * time.Minute), Success: false, Duration: 25 * time.Millisecond})
|
||||
checkHourlyStatistics(t, uptime.HourlyStatistics[now.Unix()-now.Unix()%3600-3600], 535, 3, 1)
|
||||
|
||||
uptime.ProcessResult(&Result{Timestamp: now.Add(-10 * time.Minute), Success: false})
|
||||
checkUptimes(t, serviceStatus, 0.50, 0.50, 0.25)
|
||||
|
||||
uptime.ProcessResult(&Result{Timestamp: time.Now().Add(-120 * time.Hour), Success: true})
|
||||
uptime.ProcessResult(&Result{Timestamp: time.Now().Add(-119 * time.Hour), Success: true})
|
||||
uptime.ProcessResult(&Result{Timestamp: time.Now().Add(-118 * time.Hour), Success: true})
|
||||
uptime.ProcessResult(&Result{Timestamp: time.Now().Add(-117 * time.Hour), Success: true})
|
||||
uptime.ProcessResult(&Result{Timestamp: time.Now().Add(-10 * time.Hour), Success: true})
|
||||
uptime.ProcessResult(&Result{Timestamp: time.Now().Add(-8 * time.Hour), Success: true})
|
||||
uptime.ProcessResult(&Result{Timestamp: time.Now().Add(-30 * time.Minute), Success: true})
|
||||
uptime.ProcessResult(&Result{Timestamp: time.Now().Add(-25 * time.Minute), Success: true})
|
||||
uptime.ProcessResult(&Result{Timestamp: now.Add(-120 * time.Hour), Success: true})
|
||||
uptime.ProcessResult(&Result{Timestamp: now.Add(-119 * time.Hour), Success: true})
|
||||
uptime.ProcessResult(&Result{Timestamp: now.Add(-118 * time.Hour), Success: true})
|
||||
uptime.ProcessResult(&Result{Timestamp: now.Add(-117 * time.Hour), Success: true})
|
||||
uptime.ProcessResult(&Result{Timestamp: now.Add(-10 * time.Hour), Success: true})
|
||||
uptime.ProcessResult(&Result{Timestamp: now.Add(-8 * time.Hour), Success: true})
|
||||
uptime.ProcessResult(&Result{Timestamp: now.Add(-30 * time.Minute), Success: true})
|
||||
uptime.ProcessResult(&Result{Timestamp: now.Add(-25 * time.Minute), Success: true})
|
||||
checkUptimes(t, serviceStatus, 0.75, 0.70, 0.50)
|
||||
}
|
||||
|
||||
@@ -51,8 +57,8 @@ func TestServiceStatus_AddResultUptimeIsCleaningUpAfterItself(t *testing.T) {
|
||||
timestamp := now.Add(-12 * 24 * time.Hour)
|
||||
for timestamp.Unix() <= now.Unix() {
|
||||
serviceStatus.AddResult(&Result{Timestamp: timestamp, Success: true})
|
||||
if len(serviceStatus.Uptime.SuccessfulExecutionsPerHour) > numberOfHoursInTenDays {
|
||||
t.Errorf("At no point in time should there be more than %d entries in serviceStatus.SuccessfulExecutionsPerHour, but there are %d", numberOfHoursInTenDays, len(serviceStatus.Uptime.SuccessfulExecutionsPerHour))
|
||||
if len(serviceStatus.Uptime.HourlyStatistics) > numberOfHoursInTenDays {
|
||||
t.Errorf("At no point in time should there be more than %d entries in serviceStatus.SuccessfulExecutionsPerHour, but there are %d", numberOfHoursInTenDays, len(serviceStatus.Uptime.HourlyStatistics))
|
||||
}
|
||||
if now.Sub(timestamp) > time.Hour && serviceStatus.Uptime.LastHour != 0 {
|
||||
t.Error("most recent timestamp > 1h ago, expected serviceStatus.Uptime.LastHour to be 0, got", serviceStatus.Uptime.LastHour)
|
||||
@@ -60,7 +66,7 @@ func TestServiceStatus_AddResultUptimeIsCleaningUpAfterItself(t *testing.T) {
|
||||
if now.Sub(timestamp) < time.Hour && serviceStatus.Uptime.LastHour == 0 {
|
||||
t.Error("most recent timestamp < 1h ago, expected serviceStatus.Uptime.LastHour to NOT be 0, got", serviceStatus.Uptime.LastHour)
|
||||
}
|
||||
// Simulate service with an interval of 1 minute
|
||||
// Simulate service with an interval of 3 minutes
|
||||
timestamp = timestamp.Add(3 * time.Minute)
|
||||
}
|
||||
}
|
||||
@@ -76,3 +82,15 @@ func checkUptimes(t *testing.T, status *ServiceStatus, expectedUptimeDuringLastS
|
||||
t.Errorf("expected status.Uptime.LastHour to be %f, got %f", expectedUptimeDuringLastHour, status.Uptime.LastHour)
|
||||
}
|
||||
}
|
||||
|
||||
func checkHourlyStatistics(t *testing.T, hourlyUptimeStatistics *HourlyUptimeStatistics, expectedTotalExecutionsResponseTime uint64, expectedTotalExecutions uint64, expectedSuccessfulExecutions uint64) {
|
||||
if hourlyUptimeStatistics.TotalExecutionsResponseTime != expectedTotalExecutionsResponseTime {
|
||||
t.Error("TotalExecutionsResponseTime should've been", expectedTotalExecutionsResponseTime, "got", hourlyUptimeStatistics.TotalExecutionsResponseTime)
|
||||
}
|
||||
if hourlyUptimeStatistics.TotalExecutions != expectedTotalExecutions {
|
||||
t.Error("TotalExecutions should've been", expectedTotalExecutions, "got", hourlyUptimeStatistics.TotalExecutions)
|
||||
}
|
||||
if hourlyUptimeStatistics.SuccessfulExecutions != expectedSuccessfulExecutions {
|
||||
t.Error("SuccessfulExecutions should've been", expectedSuccessfulExecutions, "got", hourlyUptimeStatistics.SuccessfulExecutions)
|
||||
}
|
||||
}
|
||||
Generated
+1039
-152
File diff suppressed because it is too large.
Load diff
@@ -8,23 +8,23 @@
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tailwindcss/postcss7-compat": "^2.0.2",
|
||||
"autoprefixer": "^9.8.6",
|
||||
"core-js": "^3.6.5",
|
||||
"postcss": "^7.0.35",
|
||||
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.0.2",
|
||||
"vue": "^3.0.0",
|
||||
"vue-router": "^4.0.0-0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss7-compat": "^2.1.0",
|
||||
"@vue/cli-plugin-babel": "~4.5.0",
|
||||
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||
"@vue/cli-plugin-router": "~4.5.0",
|
||||
"@vue/cli-service": "~4.5.0",
|
||||
"@vue/compiler-sfc": "^3.0.0",
|
||||
"autoprefixer": "^9.8.6",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-vue": "^7.0.0-0"
|
||||
"eslint-plugin-vue": "^7.0.0-0",
|
||||
"postcss": "^7.0.35",
|
||||
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.1.0"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
const tailwindcss = require('tailwindcss');
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
require('tailwindcss')
|
||||
tailwindcss('./tailwind.config.js'),
|
||||
require('autoprefixer'),
|
||||
],
|
||||
};
|
||||
@@ -7,7 +7,7 @@
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title>Health Dashboard | Gatus</title>
|
||||
</head>
|
||||
<body>
|
||||
<body class="dark:bg-gray-900">
|
||||
<noscript>
|
||||
<strong>Enable JavaScript to view this page.</strong>
|
||||
</noscript>
|
||||
|
||||
+1
-16
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="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" id="global">
|
||||
<div class="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">
|
||||
<div class="mb-2">
|
||||
<div class="flex flex-wrap">
|
||||
<div class="w-3/4 text-left my-auto">
|
||||
@@ -39,18 +39,3 @@ export default {
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style>
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
html, body {
|
||||
background-color: #f7f9fb;
|
||||
}
|
||||
|
||||
#global, #results {
|
||||
max-width: 1200px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div class="mt-3 flex">
|
||||
<div class="flex-1">
|
||||
<button v-if="currentPage < 5" @click="nextPage" class="bg-gray-100 hover:bg-gray-200 px-2 rounded border-gray-200 border text-monospace text-gray-500"><</button>
|
||||
<button v-if="currentPage < 5" @click="nextPage" 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"><</button>
|
||||
</div>
|
||||
<div class="flex-1 text-right">
|
||||
<button v-if="currentPage > 1" @click="previousPage" class="bg-gray-100 hover:bg-gray-200 px-2 rounded border-gray-200 border text-monospace text-gray-500">></button>
|
||||
<button v-if="currentPage > 1" @click="previousPage" 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">></button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class='service px-3 py-3 border-l border-r border-t rounded-none hover:bg-gray-100' v-if="data">
|
||||
<div class='service 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' v-if="data">
|
||||
<div class='flex flex-wrap mb-2'>
|
||||
<div class='w-3/4'>
|
||||
<router-link :to="generatePath()" class="font-bold hover:text-blue-800 hover:underline" title="View detailed service health">
|
||||
<router-link :to="generatePath()" class="font-bold hover:text-blue-800 hover:underline dark:hover:text-blue-400" title="View detailed service health">
|
||||
{{ data.name }}
|
||||
</router-link>
|
||||
<span v-if="data.results && data.results.length" class='text-gray-500 font-light'> | {{ data.results[data.results.length - 1].hostname }}</span>
|
||||
@@ -22,7 +22,7 @@
|
||||
<div class='status-over-time flex flex-row'>
|
||||
<slot v-if="data.results && data.results.length">
|
||||
<slot v-if="data.results.length < maximumNumberOfResults">
|
||||
<span v-for="filler in maximumNumberOfResults - data.results.length" :key="filler" class="status rounded border border-dashed"> </span>
|
||||
<span v-for="filler in maximumNumberOfResults - data.results.length" :key="filler" class="status rounded border border-dashed border-gray-400"> </span>
|
||||
</slot>
|
||||
<slot v-for="result in data.results" :key="result">
|
||||
<span v-if="result.success" class="status status-success rounded bg-success" @mouseenter="showTooltip(result, $event)" @mouseleave="showTooltip(null, $event)"></span>
|
||||
@@ -30,7 +30,7 @@
|
||||
</slot>
|
||||
</slot>
|
||||
<slot v-else>
|
||||
<span v-for="filler in maximumNumberOfResults" :key="filler" class="status rounded border border-dashed"> </span>
|
||||
<span v-for="filler in maximumNumberOfResults" :key="filler" class="status rounded border border-dashed border-gray-400"> </span>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
@@ -132,8 +132,6 @@ export default {
|
||||
border-bottom-left-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
border-bottom-width: 3px;
|
||||
border-color: #dee2e6;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
.status-over-time {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div :class="services.length === 0 ? 'mt-3' : 'mt-4'">
|
||||
<slot v-if="name !== 'undefined'">
|
||||
<div class="service-group pt-2 border" @click="toggleGroup">
|
||||
<h5 class='text-monospace text-gray-400 text-xl font-medium pb-2 px-3'>
|
||||
<div class="service-group pt-2 border dark:bg-gray-800 dark:border-gray-500" @click="toggleGroup">
|
||||
<h5 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'>
|
||||
<span v-if="healthy" class='text-green-600'>✓</span>
|
||||
<span v-else class='text-yellow-400'>~</span>
|
||||
{{ name }}
|
||||
@@ -96,6 +96,6 @@ export default {
|
||||
}
|
||||
|
||||
.service-group h5:hover {
|
||||
color: #1b1e21 !important;
|
||||
color: #1b1e21;
|
||||
}
|
||||
</style>
|
||||
@@ -1,17 +1,23 @@
|
||||
<template>
|
||||
<div id="settings">
|
||||
<div class="flex bg-gray-200 rounded border border-gray-300 shadow">
|
||||
<div class="text-sm text-gray-600 rounded-xl py-1 px-2">
|
||||
↻
|
||||
<div class="flex">
|
||||
<div class="flex bg-gray-200 border-gray-300 rounded border shadow dark:text-gray-200 dark:bg-gray-800 dark:border-gray-500">
|
||||
<div class="text-xs text-gray-600 rounded-xl py-1 px-2 dark:text-gray-200">
|
||||
↻
|
||||
</div>
|
||||
<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" @change="handleChangeRefreshInterval">
|
||||
<option value="10" :selected="refreshInterval === 10">10s</option>
|
||||
<option value="30" :selected="refreshInterval === 30">30s</option>
|
||||
<option value="60" :selected="refreshInterval === 60">1m</option>
|
||||
<option value="120" :selected="refreshInterval === 120">2m</option>
|
||||
<option value="300" :selected="refreshInterval === 300">5m</option>
|
||||
<option value="600" :selected="refreshInterval === 600">10m</option>
|
||||
</select>
|
||||
<button @click="toggleDarkMode" class="text-xs p-1">
|
||||
<slot v-if="darkMode">☀</slot>
|
||||
<slot v-else>🌙</slot>
|
||||
</button>
|
||||
</div>
|
||||
<select class="text-center text-gray-500 text-sm" id="refresh-rate" ref="refreshInterval" @change="handleChangeRefreshInterval">
|
||||
<option value="10" :selected="refreshInterval === 10">10s</option>
|
||||
<option value="30" :selected="refreshInterval === 30">30s</option>
|
||||
<option value="60" :selected="refreshInterval === 60">1m</option>
|
||||
<option value="120" :selected="refreshInterval === 120">2m</option>
|
||||
<option value="300" :selected="refreshInterval === 300">5m</option>
|
||||
<option value="600" :selected="refreshInterval === 600">10m</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -36,6 +42,24 @@ export default {
|
||||
this.refreshData();
|
||||
clearInterval(this.refreshIntervalHandler);
|
||||
this.setRefreshInterval(this.$refs.refreshInterval.value);
|
||||
},
|
||||
toggleDarkMode() {
|
||||
if (localStorage.theme === 'dark') {
|
||||
localStorage.theme = 'light';
|
||||
} else {
|
||||
localStorage.theme = 'dark';
|
||||
}
|
||||
this.applyTheme();
|
||||
},
|
||||
applyTheme() {
|
||||
console.log(localStorage.theme, 'theme' in localStorage);
|
||||
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||||
this.darkMode = true;
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
this.darkMode = false;
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@@ -43,6 +67,8 @@ export default {
|
||||
this.refreshInterval = 60;
|
||||
}
|
||||
this.setRefreshInterval(this.refreshInterval);
|
||||
// dark mode
|
||||
this.applyTheme();
|
||||
},
|
||||
unmounted() {
|
||||
clearInterval(this.refreshIntervalHandler);
|
||||
@@ -51,6 +77,7 @@ export default {
|
||||
return {
|
||||
refreshInterval: sessionStorage.getItem('gatus:refresh-interval') < 10 ? 60 : parseInt(sessionStorage.getItem('gatus:refresh-interval')),
|
||||
refreshIntervalHandler: 0,
|
||||
darkMode: false
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
+31
-3
@@ -6,6 +6,34 @@
|
||||
background-color: #28a745;
|
||||
}
|
||||
|
||||
.text-monospace {
|
||||
font-family: Consolas, monospace;
|
||||
}
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
padding-top: 20px;
|
||||
padding-bottom: 20px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
html, body {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<router-link to="../" class="absolute top-2 left-2 inline-block px-2 py-0 text-lg text-black transition bg-gray-100 rounded shadow ripple hover:shadow-lg hover:bg-gray-200 focus:outline-none">
|
||||
<router-link 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">
|
||||
←
|
||||
</router-link>
|
||||
<div>
|
||||
<slot v-if="serviceStatus">
|
||||
<h1 class="text-xl xl:text-3xl text-monospace text-gray-400">RECENT CHECKS</h1>
|
||||
<h1 class="text-xl xl:text-3xl font-mono text-gray-400">RECENT CHECKS</h1>
|
||||
<hr class="mb-4" />
|
||||
<Service
|
||||
:data="serviceStatus"
|
||||
@@ -15,7 +15,7 @@
|
||||
<Pagination @page="changePage"/>
|
||||
</slot>
|
||||
<div v-if="uptime" class="mt-12">
|
||||
<h1 class="text-xl xl:text-3xl text-monospace text-gray-400">UPTIME</h1>
|
||||
<h1 class="text-xl xl:text-3xl font-mono text-gray-400">UPTIME</h1>
|
||||
<hr />
|
||||
<div class="flex space-x-4 text-center text-xl xl:text-2xl mt-3">
|
||||
<div class="flex-1">
|
||||
@@ -32,7 +32,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<hr class="mt-1"/>
|
||||
<h3 class="text-xl text-monospace text-gray-400 mt-1 text-right">BADGES</h3>
|
||||
<h3 class="text-xl font-mono text-gray-400 mt-1 text-right">BADGES</h3>
|
||||
<div v-if="serviceStatus && serviceStatus.key" class="flex space-x-4 text-center text-2xl mt-6 relative bottom-12">
|
||||
<div class="flex-1">
|
||||
<img :src="generateBadgeImageURL('7d')" alt="7d uptime badge" class="mx-auto" />
|
||||
@@ -46,7 +46,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-xl xl:text-3xl text-monospace text-gray-400 mt-4">EVENTS</h1>
|
||||
<h1 class="text-xl xl:text-3xl font-mono text-gray-400 mt-4">EVENTS</h1>
|
||||
<hr class="mb-4" />
|
||||
<div>
|
||||
<slot v-for="event in events" :key="event">
|
||||
@@ -176,7 +176,5 @@ export default {
|
||||
.service {
|
||||
border-radius: 3px;
|
||||
border-bottom-width: 3px;
|
||||
border-color: #dee2e6;
|
||||
border-style: solid;
|
||||
}
|
||||
</style>
|
||||
@@ -1,7 +1,10 @@
|
||||
module.exports = {
|
||||
purge: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
|
||||
darkMode: false, // or 'media' or 'class'
|
||||
purge: ['./public/index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
|
||||
darkMode: 'class', // or 'media' or 'class'
|
||||
theme: {
|
||||
fontFamily: {
|
||||
'mono': ['Consolas', 'Monaco', '"Courier New"', 'monospace']
|
||||
},
|
||||
extend: {},
|
||||
},
|
||||
variants: {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#social[data-v-1cbbc992]{position:fixed;right:5px;bottom:5px;padding:5px;margin:0;z-index:100}#social img[data-v-1cbbc992]{opacity:.3}#social img[data-v-1cbbc992]:hover{opacity:1}#tooltip{position:fixed;background-color:#fff;border:1px solid #d3d3d3;border-radius:4px;padding:6px;font-size:13px}#tooltip code{color:#212529;line-height:1}#tooltip .tooltip-title{font-weight:700;margin-bottom:0;display:block;margin-top:8px}#tooltip>.tooltip-title:first-child{margin-top:0}html{height:100%}body,html{background-color:#f7f9fb}#global,#results{max-width:1200px}
|
||||
#social[data-v-1cbbc992]{position:fixed;right:5px;bottom:5px;padding:5px;margin:0;z-index:100}#social img[data-v-1cbbc992]{opacity:.3}#social img[data-v-1cbbc992]:hover{opacity:1}#tooltip{position:fixed;background-color:#fff;border:1px solid #d3d3d3;border-radius:4px;padding:6px;font-size:13px}#tooltip code{color:#212529;line-height:1}#tooltip .tooltip-title{font-weight:700;margin-bottom:0;display:block;margin-top:8px}#tooltip>.tooltip-title:first-child{margin-top:0}
|
||||
|
||||
/*! tailwindcss v2.0.4 | MIT License | https://tailwindcss.com */
|
||||
/*! tailwindcss v2.1.0 | MIT License | https://tailwindcss.com */
|
||||
|
||||
/*! modern-normalize v1.0.0 | MIT License | https://github.com/sindresorhus/modern-normalize */:root{-moz-tab-size:4;tab-size:4}html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji}hr{height:0;color:inherit}abbr[title]{text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,Menlo,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}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=button],button{-webkit-appearance:button}legend{padding:0}progress{vertical-align:baseline}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}button{background-color:transparent;background-image:none}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}fieldset,ol,ul{margin:0;padding:0}ol,ul{list-style:none}html{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;line-height:1.5}body{font-family:inherit;line-height:inherit}*,:after,:before{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}hr{border-top-width:1px}img{border-style:solid}textarea{resize:vertical}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button{cursor:pointer}table{border-collapse:collapse}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}button,input,optgroup,select,textarea{padding:0;line-height:inherit;color:inherit}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}.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}}.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)))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgba(243,244,246,var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgba(229,231,235,var(--tw-bg-opacity))}.bg-red-100{--tw-bg-opacity:1;background-color:rgba(254,226,226,var(--tw-bg-opacity))}.bg-red-600{--tw-bg-opacity:1;background-color:rgba(220,38,38,var(--tw-bg-opacity))}.bg-green-100{--tw-bg-opacity:1;background-color:rgba(209,250,229,var(--tw-bg-opacity))}.hover\:bg-gray-100:hover{--tw-bg-opacity:1;background-color:rgba(243,244,246,var(--tw-bg-opacity))}.hover\:bg-gray-200:hover{--tw-bg-opacity:1;background-color:rgba(229,231,235,var(--tw-bg-opacity))}.border-gray-200{--tw-border-opacity:1;border-color:rgba(229,231,235,var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity:1;border-color:rgba(209,213,219,var(--tw-border-opacity))}.border-gray-500{--tw-border-opacity:1;border-color:rgba(107,114,128,var(--tw-border-opacity))}.border-red-500{--tw-border-opacity:1;border-color:rgba(239,68,68,var(--tw-border-opacity))}.border-green-600{--tw-border-opacity:1;border-color:rgba(5,150,105,var(--tw-border-opacity))}.rounded-none{border-radius:0}.rounded{border-radius:.25rem}.rounded-xl{border-radius:.75rem}.rounded-full{border-radius:9999px}.border-dashed{border-style:dashed}.border{border-width:1px}.border-t{border-top-width:1px}.border-r{border-right-width:1px}.border-l{border-left-width:1px}.cursor-pointer{cursor:pointer}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.table{display:table}.hidden{display:none}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.justify-end{justify-content:flex-end}.flex-1{flex:1 1 0%}.float-right{float:right}.font-light{font-weight:300}.font-medium{font-weight:500}.font-bold{font-weight:700}.text-sm{font-size:.875rem;line-height:1.25rem}.text-lg{font-size:1.125rem}.text-lg,.text-xl{line-height:1.75rem}.text-xl{font-size:1.25rem}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.my-4{margin-top:1rem;margin-bottom:1rem}.my-auto{margin-top:auto;margin-bottom:auto}.mx-auto{margin-left:auto;margin-right:auto}.mt-1{margin-top:.25rem}.mr-2{margin-right:.5rem}.mb-2{margin-bottom:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mb-4{margin-bottom:1rem}.mt-6{margin-top:1.5rem}.mt-12{margin-top:3rem}.object-scale-down{object-fit:scale-down}.opacity-75{opacity:.75}.focusLine truncated
|
||||
/*! modern-normalize v1.0.0 | MIT License | https://github.com/sindresorhus/modern-normalize */:root{-moz-tab-size:4;-o-tab-size:4;tab-size:4}html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji}hr{height:0;color:inherit}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,Menlo,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}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=button],button{-webkit-appearance:button}legend{padding:0}progress{vertical-align:baseline}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}button{background-color:transparent;background-image:none}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}fieldset,ol,ul{margin:0;padding:0}ol,ul{list-style:none}html{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;line-height:1.5}body{font-family:inherit;line-height:inherit}*,:after,:before{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}hr{border-top-width:1px}img{border-style:solid}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input:-ms-input-placeholder,textarea:-ms-input-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button{cursor:pointer}table{border-collapse:collapse}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}button,input,optgroup,select,textarea{padding:0;line-height:inherit;color:inherit}code,kbd,pre,samp{font-family:Consolas,Monaco,Courier New,monospace}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}.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}}.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)))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgba(243,244,246,var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgba(229,231,235,var(--tw-bg-opacity))}.bg-red-100{--tw-bg-opacity:1;background-color:rgba(254,226,226,var(--tw-bg-opacity))}.bg-red-600{--tw-bg-opacity:1;background-color:rgba(220,38,38,var(--tw-bg-opacity))}.bg-green-100{--tw-bg-opacity:1;background-color:rgba(209,250,229,var(--tw-bg-opacity))}.hover\:bg-gray-100:hover{--tw-bg-opacity:1;background-color:rgba(243,244,246,var(--tw-bg-opacity))}.hover\:bg-gray-200:hover{--tw-bg-opacity:1;background-color:rgba(229,231,235,var(--tw-bg-opacity))}.dark .dark\:bg-gray-700{--tw-bg-opacity:1;background-color:rgba(55,65,81,var(--tw-bg-opacity))}.dark .dark\:bg-gray-800{--tw-bg-opacity:1;background-color:rgba(31,41,55,var(--tw-bg-opacity))}.dark .dark\:bg-gray-900{--tw-bg-opacity:1;background-color:rgba(17,24,39,var(--tw-bg-opacity))}.dark .dark\:hover\:bg-gray-600:hover{--tw-bg-opacity:1;background-color:rgba(75,85,99,var(--tw-bg-opacity))}.dark .dark\:hover\:bg-gray-700:hover{--tw-bg-opacity:1;background-color:rgba(55,65,81,var(--tw-bg-opacity))}.border-gray-200{--tw-border-opacity:1;border-color:rgba(229,231,235,var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity:1;border-color:rgba(209,213,219,var(--tw-border-opacity))}.border-gray-400{--tw-border-opacity:1;border-color:rgba(156,163,175,var(--tw-border-opacity))}.border-gray-500{--tw-border-opacity:1;border-color:rgba(107,114,128,var(--tw-border-opacity))}.border-red-500{--tw-border-opacity:1;border-color:rgba(239,68,68,var(--tw-border-opacity))}.border-green-600{--tw-border-opacity:1;border-color:rgba(5,150,105,var(--tw-border-opacity))}.dark .dark\:border-gray-500{--tw-border-opacity:1;border-color:rgba(107,114,128,var(--tw-border-opacity))}.rounded-none{border-radius:0}.rounded{border-radius:.25rem}.rounded-xl{border-radius:.75rem}.rounded-full{border-radius:9999px}.border-dashed{border-style:dashed}.border{border-width:1px}.border-t{border-top-width:1px}.border-r{border-right-width:1px}.border-l{border-left-width:1px}.cursor-pointer{cursor:pointer}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.table{display:tablLine truncated
|
||||
@@ -1 +1 @@
|
||||
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><title>Health Dashboard | Gatus</title><link href="/css/app.css" rel="preload" as="style"><link href="/js/app.js" rel="preload" as="script"><link href="/js/chunk-vendors.js" rel="preload" as="script"><link href="/css/app.css" rel="stylesheet"></head><body><noscript><strong>Enable JavaScript to view this page.</strong></noscript><div id="app"></div><script src="/js/chunk-vendors.js"></script><script src="/js/app.js"></script></body></html>
|
||||
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><title>Health Dashboard | Gatus</title><link href="/css/app.css" rel="preload" as="style"><link href="/js/app.js" rel="preload" as="script"><link href="/js/chunk-vendors.js" rel="preload" as="script"><link href="/css/app.css" rel="stylesheet"></head><body class="dark:bg-gray-900"><noscript><strong>Enable JavaScript to view this page.</strong></noscript><div id="app"></div><script src="/js/chunk-vendors.js"></script><script src="/js/app.js"></script></body></html>
|
||||
@@ -1 +1 @@
|
||||
(function(e){function t(t){for(var n,a,i=t[0],c=t[1],l=t[2],g=0,h=[];g<i.length;g++)a=i[g],Object.prototype.hasOwnProperty.call(r,a)&&r[a]&&h.push(r[a][0]),r[a]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);u&&u(t);while(h.length)h.shift()();return o.push.apply(o,l||[]),s()}function s(){for(var e,t=0;t<o.length;t++){for(var s=o[t],n=!0,i=1;i<s.length;i++){var c=s[i];0!==r[c]&&(n=!1)}n&&(o.splice(t--,1),e=a(a.s=s[0]))}return e}var n={},r={app:0},o=[];function a(t){if(n[t])return n[t].exports;var s=n[t]={i:t,l:!1,exports:{}};return e[t].call(s.exports,s,s.exports,a),s.l=!0,s.exports}a.m=e,a.c=n,a.d=function(e,t,s){a.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:s})},a.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,t){if(1&t&&(e=a(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var s=Object.create(null);if(a.r(s),Object.defineProperty(s,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)a.d(s,n,function(t){return e[t]}.bind(null,n));return s},a.n=function(e){var t=e&&e.__esModule?function(){return e["default"]}:function(){return e};return a.d(t,"a",t),t},a.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},a.p="/";var i=window["webpackJsonp"]=window["webpackJsonp"]||[],c=i.push.bind(i);i.push=t,i=i.slice();for(var l=0;l<i.length;l++)t(i[l]);var u=c;o.push([0,"chunk-vendors"]),s()})({0:function(e,t,s){e.exports=s("56d7")},"1dd9":function(e,t,s){"use strict";s("ae5b")},"22c8":function(e,t,s){},"359c":function(e,t,s){e.exports=s.p+"img/github.png"},"3a83":function(e,t,s){},"3d28":function(e,t,s){"use strict";s("afea")},"508b":function(e,t,s){"use strict";s("22c8")},"56d7":function(e,t,s){"use strict";s.r(t),s.d(t,"SERVER_URL",(function(){return lt}));s("e260"),s("e6cf"),s("cca6"),s("a79d");var n=s("7a23"),r=s("cf05"),o=s.n(r),a={class:"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",id:"global"},i=Object(n["g"])("div",{class:"mb-2"},[Object(n["g"])("div",{class:"flex flex-wrap"},[Object(n["g"])("div",{class:"w-3/4 text-left my-auto"},[Object(n["g"])("div",{class:"text-3xl xl:text-5xl lg:text-4xl font-light"},"Health Status")]),Object(n["g"])("div",{class:"w-1/4 flex justify-end"},[Object(n["g"])("img",{src:o.a,alt:"Gatus",class:"object-scale-down",style:{"max-width":"100px","min-width":"50px","min-height":"50px"}})])])],-1);function c(e,t,s,r,o,c){var l=Object(n["x"])("router-view"),u=Object(n["x"])("Tooltip"),g=Object(n["x"])("Social");return Object(n["p"])(),Object(n["d"])(n["a"],null,[Object(n["g"])("div",a,[i,Object(n["g"])(l,{onShowTooltip:c.showTooltip},null,8,["onShowTooltip"])]),Object(n["g"])(u,{result:o.tooltip.result,event:o.tooltip.event},null,8,["result","event"]),Object(n["g"])(g)],64)}var l=s("359c"),u=s.n(l),g=Object(n["D"])("data-v-1cbbc992");Object(n["s"])("data-v-1cbbc992");var h={id:"social"},d=Object(n["g"])("a",{href:"https://github.com/TwinProduction/gatus",target:"_blank",title:"Gatus on GitHub"},[Object(n["g"])("img",{src:u.a,alt:"GitHub",width:"32",height:"auto"})],-1);Object(n["q"])();var p=g((function(e,t,s,r,o,a){return Object(n["p"])(),Object(n["d"])("div",h,[d])})),f={name:"Social"};s("508b");f.render=p,f.__scopeId="data-v-1cbbc992";var v=f,b=(s("b680"),Object(n["g"])("div",{class:"tooltip-title"},"Timestamp:",-1)),A={id:"tooltip-timestamp"},m=Object(n["g"])("div",{class:"tooltip-title"},"Response time:",-1),O={id:"tooltip-response-time"},j=Object(n["g"])("div",{class:"tooltip-title"},"Conditions:",-1),w={id:"tooltip-conditions"},x=Object(n["g"])("br",null,null,-1),y={key:0,id:"tooltip-errors-container"},T=Object(n["g"])("div",{class:"tooltip-title"},"Errors:",-1),R={id:"tooltip-errors"},I=Object(n["g"])("br",null,null,-1);function S(e,t,s,r,o,a){return Object(n["p"])(),Object(n["d"])("div",{id:"tooltip",ref:"tooltip",class:o.hidden?"invisible":"",style:"top:"+o.top+"px; left:"+o.left+"px"},[s.result?Object(n["w"])(e.$slots,"default",{key:0},(function(){return[b,Object(n["g"])("code",A,Object(n["z"])(a.prettifyTimestamp(s.result.timestamp)),1),m,Object(n["g"])("code",O,Object(n["z"])((s.result.duration/1e6).toFixed(0))+"ms",1),j,Object(n["g"])("code",w,[(Object(n["p"])(!0),Object(n["d"])(n["a"],null,Object(n["v"])(s.result.conditionResults,(function(t){return Object(n["w"])(e.$slots,"default",{key:t},(function(){return[Object(n["f"])(Object(n["z"])(t.success?"✓":"X")+" ~ "+Object(n["z"])(t.condition),1),x]}))})),128))]),s.result.errors&&s.result.errors.length?(Object(n["p"])(),Object(n["d"])("div",y,[T,Object(n["g"])("code",R,[(Object(n["p"])(!0),Object(n["d"])(n["a"],null,Object(n["v"])(s.result.errors,(function(t){return Object(n["w"])(e.$slots,"default",{key:t},(function(){return[Object(n["f"])(" - "+Object(n["z"])(t),1),I]}))})),128))])])):Object(n["e"])("",!0)]})):Object(n["e"])("",!0)Line truncated
|
||||
(function(e){function t(t){for(var s,a,i=t[0],c=t[1],l=t[2],u=0,d=[];u<i.length;u++)a=i[u],Object.prototype.hasOwnProperty.call(r,a)&&r[a]&&d.push(r[a][0]),r[a]=0;for(s in c)Object.prototype.hasOwnProperty.call(c,s)&&(e[s]=c[s]);g&&g(t);while(d.length)d.shift()();return o.push.apply(o,l||[]),n()}function n(){for(var e,t=0;t<o.length;t++){for(var n=o[t],s=!0,i=1;i<n.length;i++){var c=n[i];0!==r[c]&&(s=!1)}s&&(o.splice(t--,1),e=a(a.s=n[0]))}return e}var s={},r={app:0},o=[];function a(t){if(s[t])return s[t].exports;var n=s[t]={i:t,l:!1,exports:{}};return e[t].call(n.exports,n,n.exports,a),n.l=!0,n.exports}a.m=e,a.c=s,a.d=function(e,t,n){a.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},a.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,t){if(1&t&&(e=a(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(a.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var s in e)a.d(n,s,function(t){return e[t]}.bind(null,s));return n},a.n=function(e){var t=e&&e.__esModule?function(){return e["default"]}:function(){return e};return a.d(t,"a",t),t},a.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},a.p="/";var i=window["webpackJsonp"]=window["webpackJsonp"]||[],c=i.push.bind(i);i.push=t,i=i.slice();for(var l=0;l<i.length;l++)t(i[l]);var g=c;o.push([0,"chunk-vendors"]),n()})({0:function(e,t,n){e.exports=n("56d7")},1289:function(e,t,n){"use strict";n("d502")},"17e7":function(e,t,n){"use strict";n("31c1")},"1dd9":function(e,t,n){"use strict";n("ae5b")},"22c8":function(e,t,n){},"31c1":function(e,t,n){},"359c":function(e,t,n){e.exports=n.p+"img/github.png"},"508b":function(e,t,n){"use strict";n("22c8")},5661:function(e,t,n){"use strict";n("bf65")},"56d7":function(e,t,n){"use strict";n.r(t),n.d(t,"SERVER_URL",(function(){return dt}));n("e260"),n("e6cf"),n("cca6"),n("a79d");var s=n("7a23"),r=n("cf05"),o=n.n(r),a={class:"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"},i=Object(s["g"])("div",{class:"mb-2"},[Object(s["g"])("div",{class:"flex flex-wrap"},[Object(s["g"])("div",{class:"w-3/4 text-left my-auto"},[Object(s["g"])("div",{class:"text-3xl xl:text-5xl lg:text-4xl font-light"},"Health Status")]),Object(s["g"])("div",{class:"w-1/4 flex justify-end"},[Object(s["g"])("img",{src:o.a,alt:"Gatus",class:"object-scale-down",style:{"max-width":"100px","min-width":"50px","min-height":"50px"}})])])],-1);function c(e,t,n,r,o,c){var l=Object(s["x"])("router-view"),g=Object(s["x"])("Tooltip"),u=Object(s["x"])("Social");return Object(s["p"])(),Object(s["d"])(s["a"],null,[Object(s["g"])("div",a,[i,Object(s["g"])(l,{onShowTooltip:c.showTooltip},null,8,["onShowTooltip"])]),Object(s["g"])(g,{result:o.tooltip.result,event:o.tooltip.event},null,8,["result","event"]),Object(s["g"])(u)],64)}var l=n("359c"),g=n.n(l),u=Object(s["D"])("data-v-1cbbc992");Object(s["s"])("data-v-1cbbc992");var d={id:"social"},h=Object(s["g"])("a",{href:"https://github.com/TwinProduction/gatus",target:"_blank",title:"Gatus on GitHub"},[Object(s["g"])("img",{src:g.a,alt:"GitHub",width:"32",height:"auto"})],-1);Object(s["q"])();var f=u((function(e,t,n,r,o,a){return Object(s["p"])(),Object(s["d"])("div",d,[h])})),p={name:"Social"};n("508b");p.render=f,p.__scopeId="data-v-1cbbc992";var b=p,v=(n("b680"),Object(s["g"])("div",{class:"tooltip-title"},"Timestamp:",-1)),A={id:"tooltip-timestamp"},m=Object(s["g"])("div",{class:"tooltip-title"},"Response time:",-1),O={id:"tooltip-response-time"},j=Object(s["g"])("div",{class:"tooltip-title"},"Conditions:",-1),y={id:"tooltip-conditions"},w=Object(s["g"])("br",null,null,-1),x={key:0,id:"tooltip-errors-container"},T=Object(s["g"])("div",{class:"tooltip-title"},"Errors:",-1),R={id:"tooltip-errors"},I=Object(s["g"])("br",null,null,-1);function S(e,t,n,r,o,a){return Object(s["p"])(),Object(s["d"])("div",{id:"tooltip",ref:"tooltip",class:o.hidden?"invisible":"",style:"top:"+o.top+"px; left:"+o.left+"px"},[n.result?Object(s["w"])(e.$slots,"default",{key:0},(function(){return[v,Object(s["g"])("code",A,Object(s["z"])(a.prettifyTimestamp(n.result.timestamp)),1),m,Object(s["g"])("code",O,Object(s["z"])((n.result.duration/1e6).toFixed(0))+"ms",1),j,Object(s["g"])("code",y,[(Object(s["p"])(!0),Object(s["d"])(s["a"],null,Object(s["v"])(n.result.conditionResults,(function(t){return Object(s["w"])(e.$slots,"default",{key:t},(function(){return[Object(s["f"])(Object(s["z"])(t.success?"✓":"X")+" ~ "+Object(s["z"])(t.condition),1),w]}))})),128))]),n.result.errors&&n.result.errors.length?(Object(s["p"])(),Object(s["d"])("div",x,[T,Object(s["g"])("code",R,[(Object(s["p"])(!0),Object(s["d"])(s["a"],null,Object(s["v"])(n.result.errors,(function(t){return Object(s["w"])(e.$slotsLine truncated
|
||||
Reference in new issue
Block a user