Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf455fb7cc | ||
|
|
dfd2f7943f | ||
|
|
fece11540b | ||
|
|
ac43ef4ab7 | ||
|
|
bc25fea1c0 | ||
|
|
30cb7b6ec8 | ||
|
|
289d834587 | ||
|
|
428e415616 | ||
|
|
0d284c2494 | ||
|
|
4a46a5ae9e | ||
|
|
df3a2016ff | ||
|
|
dda83761b5 | ||
|
|
882444e0d5 | ||
|
|
fa4736c672 | ||
|
|
dc173b29bc | ||
|
|
c3a4ce1eb4 | ||
|
|
044f0454f8 | ||
|
|
9bd5c38a96 | ||
|
|
d6b4c2394a | ||
|
|
9fe4678193 |
No files matched your search
+1
-1
@@ -1 +1 @@
|
||||
* text=lf
|
||||
* text=auto eol=lf
|
||||
@@ -37,7 +37,6 @@ func (provider *AlertProvider) ToCustomAlertProvider(service *core.Service, aler
|
||||
} else {
|
||||
message = fmt.Sprintf("TRIGGERED: %s - %s", service.Name, alert.GetDescription())
|
||||
}
|
||||
|
||||
return &custom.AlertProvider{
|
||||
URL: restAPIURL,
|
||||
Method: http.MethodPost,
|
||||
|
||||
+21
-3
@@ -38,7 +38,11 @@ func CanPerformStartTLS(address string, config *Config) (connected bool, certifi
|
||||
if len(hostAndPort) != 2 {
|
||||
return false, nil, errors.New("invalid address for starttls, format must be host:port")
|
||||
}
|
||||
smtpClient, err := smtp.Dial(address)
|
||||
connection, err := net.DialTimeout("tcp", address, config.Timeout)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
smtpClient, err := smtp.NewClient(connection, hostAndPort[0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -57,6 +61,20 @@ func CanPerformStartTLS(address string, config *Config) (connected bool, certifi
|
||||
return true, certificate, nil
|
||||
}
|
||||
|
||||
// CanPerformTLS checks whether a connection can be established to an address using the TLS protocol
|
||||
func CanPerformTLS(address string, config *Config) (connected bool, certificate *x509.Certificate, err error) {
|
||||
connection, err := tls.DialWithDialer(&net.Dialer{Timeout: config.Timeout}, "tcp", address, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer connection.Close()
|
||||
verifiedChains := connection.ConnectionState().VerifiedChains
|
||||
if len(verifiedChains) == 0 || len(verifiedChains[0]) == 0 {
|
||||
return
|
||||
}
|
||||
return true, verifiedChains[0][0], nil
|
||||
}
|
||||
|
||||
// Ping checks if an address can be pinged and returns the round-trip time if the address can be pinged
|
||||
//
|
||||
// Note that this function takes at least 100ms, even if the address is 127.0.0.1
|
||||
@@ -67,9 +85,9 @@ func Ping(address string, config *Config) (bool, time.Duration) {
|
||||
}
|
||||
pinger.Count = 1
|
||||
pinger.Timeout = config.Timeout
|
||||
// Set the pinger's privileged mode to true for every operating system except darwin
|
||||
// Set the pinger's privileged mode to true for windows
|
||||
// https://github.com/TwinProduction/gatus/issues/132
|
||||
pinger.SetPrivileged(runtime.GOOS != "darwin")
|
||||
pinger.SetPrivileged(runtime.GOOS == "windows")
|
||||
err = pinger.Run()
|
||||
if err != nil {
|
||||
return false, 0
|
||||
|
||||
@@ -91,6 +91,56 @@ func TestCanPerformStartTLS(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanPerformTLS(t *testing.T) {
|
||||
type args struct {
|
||||
address string
|
||||
insecure bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantConnected bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "invalid address",
|
||||
args: args{
|
||||
address: "test",
|
||||
},
|
||||
wantConnected: false,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "error dial",
|
||||
args: args{
|
||||
address: "test:1234",
|
||||
},
|
||||
wantConnected: false,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "valid tls",
|
||||
args: args{
|
||||
address: "smtp.gmail.com:465",
|
||||
},
|
||||
wantConnected: true,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
connected, _, err := CanPerformTLS(tt.args.address, &Config{Insecure: tt.args.insecure, Timeout: 5 * time.Second})
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("CanPerformTLS() err=%v, wantErr=%v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if connected != tt.wantConnected {
|
||||
t.Errorf("CanPerformTLS() connected=%v, wantConnected=%v", connected, tt.wantConnected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanCreateTCPConnection(t *testing.T) {
|
||||
if CanCreateTCPConnection("127.0.0.1", &Config{Timeout: 5 * time.Second}) {
|
||||
t.Error("should've failed, because there's no port in the address")
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
services:
|
||||
- name: front-end
|
||||
group: core
|
||||
url: "https://twinnation.org/health"
|
||||
url: "https://twin.sh/health"
|
||||
interval: 1m
|
||||
conditions:
|
||||
- "[STATUS] == 200"
|
||||
|
||||
+26
-16
@@ -10,6 +10,9 @@ import (
|
||||
"github.com/TwinProduction/gatus/alerting"
|
||||
"github.com/TwinProduction/gatus/alerting/alert"
|
||||
"github.com/TwinProduction/gatus/alerting/provider"
|
||||
"github.com/TwinProduction/gatus/config/maintenance"
|
||||
"github.com/TwinProduction/gatus/config/ui"
|
||||
"github.com/TwinProduction/gatus/config/web"
|
||||
"github.com/TwinProduction/gatus/core"
|
||||
"github.com/TwinProduction/gatus/security"
|
||||
"github.com/TwinProduction/gatus/storage"
|
||||
@@ -24,12 +27,6 @@ const (
|
||||
// DefaultFallbackConfigurationFilePath is the default fallback path that will be used to search for the
|
||||
// configuration file if DefaultConfigurationFilePath didn't work
|
||||
DefaultFallbackConfigurationFilePath = "config/config.yml"
|
||||
|
||||
// DefaultAddress is the default address the service will bind to
|
||||
DefaultAddress = "0.0.0.0"
|
||||
|
||||
// DefaultPort is the default port the service will listen on
|
||||
DefaultPort = 8080
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -41,10 +38,6 @@ var (
|
||||
|
||||
// ErrInvalidSecurityConfig is an error returned when the security configuration is invalid
|
||||
ErrInvalidSecurityConfig = errors.New("invalid security configuration")
|
||||
|
||||
// StaticFolder is the path to the location of the static folder from the root path of the project
|
||||
// The only reason this is exposed is to allow running tests from a different path than the root path of the project
|
||||
StaticFolder = "./web/static"
|
||||
)
|
||||
|
||||
// Config is the main configuration structure
|
||||
@@ -77,10 +70,13 @@ type Config struct {
|
||||
Storage *storage.Config `yaml:"storage"`
|
||||
|
||||
// Web is the configuration for the web listener
|
||||
Web *WebConfig `yaml:"web"`
|
||||
Web *web.Config `yaml:"web"`
|
||||
|
||||
// UI is the configuration for the UI
|
||||
UI *UIConfig `yaml:"ui"`
|
||||
UI *ui.Config `yaml:"ui"`
|
||||
|
||||
// Maintenance is the configuration for creating a maintenance window in which no alerts are sent
|
||||
Maintenance *maintenance.Config `yaml:"maintenance"`
|
||||
|
||||
filePath string // path to the file from which config was loaded from
|
||||
lastFileModTime time.Time // last modification time
|
||||
@@ -172,6 +168,9 @@ func parseAndValidateConfigBytes(yamlBytes []byte) (config *Config, err error) {
|
||||
if err := validateUIConfig(config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := validateMaintenanceConfig(config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := validateStorageConfig(config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -201,11 +200,22 @@ func validateStorageConfig(config *Config) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateMaintenanceConfig(config *Config) error {
|
||||
if config.Maintenance == nil {
|
||||
config.Maintenance = maintenance.GetDefaultConfig()
|
||||
} else {
|
||||
if err := config.Maintenance.ValidateAndSetDefaults(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateUIConfig(config *Config) error {
|
||||
if config.UI == nil {
|
||||
config.UI = GetDefaultUIConfig()
|
||||
config.UI = ui.GetDefaultConfig()
|
||||
} else {
|
||||
if err := config.UI.validateAndSetDefaults(); err != nil {
|
||||
if err := config.UI.ValidateAndSetDefaults(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -214,9 +224,9 @@ func validateUIConfig(config *Config) error {
|
||||
|
||||
func validateWebConfig(config *Config) error {
|
||||
if config.Web == nil {
|
||||
config.Web = GetDefaultWebConfig()
|
||||
config.Web = web.GetDefaultConfig()
|
||||
} else {
|
||||
return config.Web.validateAndSetDefaults()
|
||||
return config.Web.ValidateAndSetDefaults()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+77
-67
@@ -17,6 +17,8 @@ import (
|
||||
"github.com/TwinProduction/gatus/alerting/provider/telegram"
|
||||
"github.com/TwinProduction/gatus/alerting/provider/twilio"
|
||||
"github.com/TwinProduction/gatus/client"
|
||||
"github.com/TwinProduction/gatus/config/ui"
|
||||
"github.com/TwinProduction/gatus/config/web"
|
||||
"github.com/TwinProduction/gatus/core"
|
||||
)
|
||||
|
||||
@@ -36,18 +38,23 @@ func TestLoadDefaultConfigurationFile(t *testing.T) {
|
||||
|
||||
func TestParseAndValidateConfigBytes(t *testing.T) {
|
||||
file := t.TempDir() + "/test.db"
|
||||
StaticFolder = "../web/static"
|
||||
ui.StaticFolder = "../web/static"
|
||||
defer func() {
|
||||
StaticFolder = "./web/static"
|
||||
ui.StaticFolder = "./web/static"
|
||||
}()
|
||||
config, err := parseAndValidateConfigBytes([]byte(fmt.Sprintf(`
|
||||
storage:
|
||||
file: %s
|
||||
maintenance:
|
||||
enabled: true
|
||||
start: 00:00
|
||||
duration: 4h
|
||||
every: [Monday, Thursday]
|
||||
ui:
|
||||
title: Test
|
||||
services:
|
||||
- name: twinnation
|
||||
url: https://twinnation.org/health
|
||||
- name: website
|
||||
url: https://twin.sh/health
|
||||
interval: 15s
|
||||
conditions:
|
||||
- "[STATUS] == 200"
|
||||
@@ -79,12 +86,15 @@ services:
|
||||
if config.UI == nil || config.UI.Title != "Test" {
|
||||
t.Error("Expected Config.UI.Title to be Test")
|
||||
}
|
||||
if mc := config.Maintenance; mc == nil || mc.Start != "00:00" || !mc.IsEnabled() || mc.Duration != 4*time.Hour || len(mc.Every) != 2 {
|
||||
t.Error("Expected Config.Maintenance to be configured properly")
|
||||
}
|
||||
if len(config.Services) != 3 {
|
||||
t.Error("Should have returned two services")
|
||||
}
|
||||
|
||||
if config.Services[0].URL != "https://twinnation.org/health" {
|
||||
t.Errorf("URL should have been %s", "https://twinnation.org/health")
|
||||
if config.Services[0].URL != "https://twin.sh/health" {
|
||||
t.Errorf("URL should have been %s", "https://twin.sh/health")
|
||||
}
|
||||
if config.Services[0].Method != "GET" {
|
||||
t.Errorf("Method should have been %s (default)", "GET")
|
||||
@@ -153,8 +163,8 @@ services:
|
||||
func TestParseAndValidateConfigBytesDefault(t *testing.T) {
|
||||
config, err := parseAndValidateConfigBytes([]byte(`
|
||||
services:
|
||||
- name: twinnation
|
||||
url: https://twinnation.org/health
|
||||
- name: website
|
||||
url: https://twin.sh/health
|
||||
conditions:
|
||||
- "[STATUS] == 200"
|
||||
`))
|
||||
@@ -167,14 +177,14 @@ services:
|
||||
if config.Metrics {
|
||||
t.Error("Metrics should've been false by default")
|
||||
}
|
||||
if config.Web.Address != DefaultAddress {
|
||||
t.Errorf("Bind address should have been %s, because it is the default value", DefaultAddress)
|
||||
if config.Web.Address != web.DefaultAddress {
|
||||
t.Errorf("Bind address should have been %s, because it is the default value", web.DefaultAddress)
|
||||
}
|
||||
if config.Web.Port != DefaultPort {
|
||||
t.Errorf("Port should have been %d, because it is the default value", DefaultPort)
|
||||
if config.Web.Port != web.DefaultPort {
|
||||
t.Errorf("Port should have been %d, because it is the default value", web.DefaultPort)
|
||||
}
|
||||
if config.Services[0].URL != "https://twinnation.org/health" {
|
||||
t.Errorf("URL should have been %s", "https://twinnation.org/health")
|
||||
if config.Services[0].URL != "https://twin.sh/health" {
|
||||
t.Errorf("URL should have been %s", "https://twin.sh/health")
|
||||
}
|
||||
if config.Services[0].Interval != 60*time.Second {
|
||||
t.Errorf("Interval should have been %s, because it is the default value", 60*time.Second)
|
||||
@@ -195,8 +205,8 @@ func TestParseAndValidateConfigBytesWithAddress(t *testing.T) {
|
||||
web:
|
||||
address: 127.0.0.1
|
||||
services:
|
||||
- name: twinnation
|
||||
url: https://twinnation.org/actuator/health
|
||||
- name: website
|
||||
url: https://twin.sh/actuator/health
|
||||
conditions:
|
||||
- "[STATUS] == 200"
|
||||
`))
|
||||
@@ -209,8 +219,8 @@ services:
|
||||
if config.Metrics {
|
||||
t.Error("Metrics should've been false by default")
|
||||
}
|
||||
if config.Services[0].URL != "https://twinnation.org/actuator/health" {
|
||||
t.Errorf("URL should have been %s", "https://twinnation.org/actuator/health")
|
||||
if config.Services[0].URL != "https://twin.sh/actuator/health" {
|
||||
t.Errorf("URL should have been %s", "https://twin.sh/actuator/health")
|
||||
}
|
||||
if config.Services[0].Interval != 60*time.Second {
|
||||
t.Errorf("Interval should have been %s, because it is the default value", 60*time.Second)
|
||||
@@ -218,8 +228,8 @@ services:
|
||||
if config.Web.Address != "127.0.0.1" {
|
||||
t.Errorf("Bind address should have been %s, because it is specified in config", "127.0.0.1")
|
||||
}
|
||||
if config.Web.Port != DefaultPort {
|
||||
t.Errorf("Port should have been %d, because it is the default value", DefaultPort)
|
||||
if config.Web.Port != web.DefaultPort {
|
||||
t.Errorf("Port should have been %d, because it is the default value", web.DefaultPort)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,8 +238,8 @@ func TestParseAndValidateConfigBytesWithPort(t *testing.T) {
|
||||
web:
|
||||
port: 12345
|
||||
services:
|
||||
- name: twinnation
|
||||
url: https://twinnation.org/health
|
||||
- name: website
|
||||
url: https://twin.sh/health
|
||||
conditions:
|
||||
- "[STATUS] == 200"
|
||||
`))
|
||||
@@ -242,14 +252,14 @@ services:
|
||||
if config.Metrics {
|
||||
t.Error("Metrics should've been false by default")
|
||||
}
|
||||
if config.Services[0].URL != "https://twinnation.org/health" {
|
||||
t.Errorf("URL should have been %s", "https://twinnation.org/health")
|
||||
if config.Services[0].URL != "https://twin.sh/health" {
|
||||
t.Errorf("URL should have been %s", "https://twin.sh/health")
|
||||
}
|
||||
if config.Services[0].Interval != 60*time.Second {
|
||||
t.Errorf("Interval should have been %s, because it is the default value", 60*time.Second)
|
||||
}
|
||||
if config.Web.Address != DefaultAddress {
|
||||
t.Errorf("Bind address should have been %s, because it is the default value", DefaultAddress)
|
||||
if config.Web.Address != web.DefaultAddress {
|
||||
t.Errorf("Bind address should have been %s, because it is the default value", web.DefaultAddress)
|
||||
}
|
||||
if config.Web.Port != 12345 {
|
||||
t.Errorf("Port should have been %d, because it is specified in config", 12345)
|
||||
@@ -262,8 +272,8 @@ web:
|
||||
port: 12345
|
||||
address: 127.0.0.1
|
||||
services:
|
||||
- name: twinnation
|
||||
url: https://twinnation.org/health
|
||||
- name: website
|
||||
url: https://twin.sh/health
|
||||
conditions:
|
||||
- "[STATUS] == 200"
|
||||
`))
|
||||
@@ -276,8 +286,8 @@ services:
|
||||
if config.Metrics {
|
||||
t.Error("Metrics should've been false by default")
|
||||
}
|
||||
if config.Services[0].URL != "https://twinnation.org/health" {
|
||||
t.Errorf("URL should have been %s", "https://twinnation.org/health")
|
||||
if config.Services[0].URL != "https://twin.sh/health" {
|
||||
t.Errorf("URL should have been %s", "https://twin.sh/health")
|
||||
}
|
||||
if config.Services[0].Interval != 60*time.Second {
|
||||
t.Errorf("Interval should have been %s, because it is the default value", 60*time.Second)
|
||||
@@ -296,8 +306,8 @@ web:
|
||||
port: 65536
|
||||
address: 127.0.0.1
|
||||
services:
|
||||
- name: twinnation
|
||||
url: https://twinnation.org/health
|
||||
- name: website
|
||||
url: https://twin.sh/health
|
||||
conditions:
|
||||
- "[STATUS] == 200"
|
||||
`))
|
||||
@@ -310,8 +320,8 @@ func TestParseAndValidateConfigBytesWithMetricsAndCustomUserAgentHeader(t *testi
|
||||
config, err := parseAndValidateConfigBytes([]byte(`
|
||||
metrics: true
|
||||
services:
|
||||
- name: twinnation
|
||||
url: https://twinnation.org/health
|
||||
- name: website
|
||||
url: https://twin.sh/health
|
||||
headers:
|
||||
User-Agent: Test/2.0
|
||||
conditions:
|
||||
@@ -326,17 +336,17 @@ services:
|
||||
if !config.Metrics {
|
||||
t.Error("Metrics should have been true")
|
||||
}
|
||||
if config.Services[0].URL != "https://twinnation.org/health" {
|
||||
t.Errorf("URL should have been %s", "https://twinnation.org/health")
|
||||
if config.Services[0].URL != "https://twin.sh/health" {
|
||||
t.Errorf("URL should have been %s", "https://twin.sh/health")
|
||||
}
|
||||
if config.Services[0].Interval != 60*time.Second {
|
||||
t.Errorf("Interval should have been %s, because it is the default value", 60*time.Second)
|
||||
}
|
||||
if config.Web.Address != DefaultAddress {
|
||||
t.Errorf("Bind address should have been %s, because it is the default value", DefaultAddress)
|
||||
if config.Web.Address != web.DefaultAddress {
|
||||
t.Errorf("Bind address should have been %s, because it is the default value", web.DefaultAddress)
|
||||
}
|
||||
if config.Web.Port != DefaultPort {
|
||||
t.Errorf("Port should have been %d, because it is the default value", DefaultPort)
|
||||
if config.Web.Port != web.DefaultPort {
|
||||
t.Errorf("Port should have been %d, because it is the default value", web.DefaultPort)
|
||||
}
|
||||
if userAgent := config.Services[0].Headers["User-Agent"]; userAgent != "Test/2.0" {
|
||||
t.Errorf("User-Agent should've been %s, got %s", "Test/2.0", userAgent)
|
||||
@@ -350,8 +360,8 @@ web:
|
||||
address: 192.168.0.1
|
||||
port: 9090
|
||||
services:
|
||||
- name: twinnation
|
||||
url: https://twinnation.org/health
|
||||
- name: website
|
||||
url: https://twin.sh/health
|
||||
conditions:
|
||||
- "[STATUS] == 200"
|
||||
`))
|
||||
@@ -370,8 +380,8 @@ services:
|
||||
if config.Web.Port != 9090 {
|
||||
t.Errorf("Port should have been %d, because it is specified in config", 9090)
|
||||
}
|
||||
if config.Services[0].URL != "https://twinnation.org/health" {
|
||||
t.Errorf("URL should have been %s", "https://twinnation.org/health")
|
||||
if config.Services[0].URL != "https://twin.sh/health" {
|
||||
t.Errorf("URL should have been %s", "https://twin.sh/health")
|
||||
}
|
||||
if config.Services[0].Interval != 60*time.Second {
|
||||
t.Errorf("Interval should have been %s, because it is the default value", 60*time.Second)
|
||||
@@ -427,8 +437,8 @@ alerting:
|
||||
webhook-url: "http://example.com"
|
||||
|
||||
services:
|
||||
- name: twinnation
|
||||
url: https://twinnation.org/health
|
||||
- name: website
|
||||
url: https://twin.sh/health
|
||||
alerts:
|
||||
- type: slack
|
||||
enabled: true
|
||||
@@ -471,8 +481,8 @@ services:
|
||||
if len(config.Services) != 1 {
|
||||
t.Error("There should've been 1 service")
|
||||
}
|
||||
if config.Services[0].URL != "https://twinnation.org/health" {
|
||||
t.Errorf("URL should have been %s", "https://twinnation.org/health")
|
||||
if config.Services[0].URL != "https://twin.sh/health" {
|
||||
t.Errorf("URL should have been %s", "https://twin.sh/health")
|
||||
}
|
||||
if config.Services[0].Interval != 60*time.Second {
|
||||
t.Errorf("Interval should have been %s, because it is the default value", 60*time.Second)
|
||||
@@ -633,8 +643,8 @@ alerting:
|
||||
enabled: true
|
||||
|
||||
services:
|
||||
- name: twinnation
|
||||
url: https://twinnation.org/health
|
||||
- name: website
|
||||
url: https://twin.sh/health
|
||||
alerts:
|
||||
- type: slack
|
||||
- type: pagerduty
|
||||
@@ -748,8 +758,8 @@ services:
|
||||
if len(config.Services) != 1 {
|
||||
t.Error("There should've been 1 service")
|
||||
}
|
||||
if config.Services[0].URL != "https://twinnation.org/health" {
|
||||
t.Errorf("URL should have been %s", "https://twinnation.org/health")
|
||||
if config.Services[0].URL != "https://twin.sh/health" {
|
||||
t.Errorf("URL should have been %s", "https://twin.sh/health")
|
||||
}
|
||||
if config.Services[0].Interval != 60*time.Second {
|
||||
t.Errorf("Interval should have been %s, because it is the default value", 60*time.Second)
|
||||
@@ -871,8 +881,8 @@ alerting:
|
||||
description: "description"
|
||||
|
||||
services:
|
||||
- name: twinnation
|
||||
url: https://twinnation.org/health
|
||||
- name: website
|
||||
url: https://twin.sh/health
|
||||
alerts:
|
||||
- type: slack
|
||||
failure-threshold: 10
|
||||
@@ -946,8 +956,8 @@ alerting:
|
||||
pagerduty:
|
||||
integration-key: "INVALID_KEY"
|
||||
services:
|
||||
- name: twinnation
|
||||
url: https://twinnation.org/health
|
||||
- name: website
|
||||
url: https://twin.sh/health
|
||||
alerts:
|
||||
- type: pagerduty
|
||||
conditions:
|
||||
@@ -980,8 +990,8 @@ alerting:
|
||||
"text": "[ALERT_TRIGGERED_OR_RESOLVED]: [SERVICE_NAME] - [ALERT_DESCRIPTION]"
|
||||
}
|
||||
services:
|
||||
- name: twinnation
|
||||
url: https://twinnation.org/health
|
||||
- name: website
|
||||
url: https://twin.sh/health
|
||||
alerts:
|
||||
- type: custom
|
||||
conditions:
|
||||
@@ -1025,8 +1035,8 @@ alerting:
|
||||
insecure: true
|
||||
body: "[ALERT_TRIGGERED_OR_RESOLVED]: [SERVICE_NAME] - [ALERT_DESCRIPTION]"
|
||||
services:
|
||||
- name: twinnation
|
||||
url: https://twinnation.org/health
|
||||
- name: website
|
||||
url: https://twin.sh/health
|
||||
alerts:
|
||||
- type: custom
|
||||
conditions:
|
||||
@@ -1065,8 +1075,8 @@ alerting:
|
||||
url: "https://example.com"
|
||||
body: "[ALERT_TRIGGERED_OR_RESOLVED]: [SERVICE_NAME] - [ALERT_DESCRIPTION]"
|
||||
services:
|
||||
- name: twinnation
|
||||
url: https://twinnation.org/health
|
||||
- name: website
|
||||
url: https://twin.sh/health
|
||||
alerts:
|
||||
- type: custom
|
||||
conditions:
|
||||
@@ -1099,7 +1109,7 @@ func TestParseAndValidateConfigBytesWithInvalidServiceName(t *testing.T) {
|
||||
_, err := parseAndValidateConfigBytes([]byte(`
|
||||
services:
|
||||
- name: ""
|
||||
url: https://twinnation.org/health
|
||||
url: https://twin.sh/health
|
||||
conditions:
|
||||
- "[STATUS] == 200"
|
||||
`))
|
||||
@@ -1145,8 +1155,8 @@ security:
|
||||
username: "admin"
|
||||
password-sha512: "invalid-sha512-hash"
|
||||
services:
|
||||
- name: twinnation
|
||||
url: https://twinnation.org/health
|
||||
- name: website
|
||||
url: https://twin.sh/health
|
||||
conditions:
|
||||
- "[STATUS] == 200"
|
||||
`))
|
||||
@@ -1164,8 +1174,8 @@ security:
|
||||
username: "%s"
|
||||
password-sha512: "%s"
|
||||
services:
|
||||
- name: twinnation
|
||||
url: https://twinnation.org/health
|
||||
- name: website
|
||||
url: https://twin.sh/health
|
||||
conditions:
|
||||
- "[STATUS] == 200"
|
||||
`, expectedUsername, expectedPasswordHash)))
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
package maintenance
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
errInvalidMaintenanceStartFormat = errors.New("invalid maintenance start format: must be hh:mm, between 00:00 and 23:59 inclusively (e.g. 23:00)")
|
||||
errInvalidMaintenanceDuration = errors.New("invalid maintenance duration: must be bigger than 0 (e.g. 30m)")
|
||||
errInvalidDayName = fmt.Errorf("invalid value specified for 'on'. supported values are %s", longDayNames)
|
||||
|
||||
longDayNames = []string{
|
||||
"Sunday",
|
||||
"Monday",
|
||||
"Tuesday",
|
||||
"Wednesday",
|
||||
"Thursday",
|
||||
"Friday",
|
||||
"Saturday",
|
||||
}
|
||||
)
|
||||
|
||||
// Config allows for the configuration of a maintenance period.
|
||||
// During this maintenance period, no alerts will be sent.
|
||||
//
|
||||
// Uses UTC.
|
||||
type Config struct {
|
||||
Enabled *bool `yaml:"enabled"` // Whether the maintenance period is enabled. Enabled by default if nil.
|
||||
Start string `yaml:"start"` // Time at which the maintenance period starts (e.g. 23:00)
|
||||
Duration time.Duration `yaml:"duration"` // Duration of the maintenance period (e.g. 4h)
|
||||
|
||||
// Every is a list of days of the week during which maintenance period applies.
|
||||
// See longDayNames for list of valid values.
|
||||
// Every day if empty.
|
||||
Every []string `yaml:"every"`
|
||||
|
||||
durationToStartFromMidnight time.Duration
|
||||
timeLocation *time.Location
|
||||
}
|
||||
|
||||
func GetDefaultConfig() *Config {
|
||||
defaultValue := false
|
||||
return &Config{
|
||||
Enabled: &defaultValue,
|
||||
}
|
||||
}
|
||||
|
||||
// IsEnabled returns whether maintenance is enabled or not
|
||||
func (c Config) IsEnabled() bool {
|
||||
if c.Enabled == nil {
|
||||
return true
|
||||
}
|
||||
return *c.Enabled
|
||||
}
|
||||
|
||||
// ValidateAndSetDefaults validates the maintenance configuration and sets the default values if necessary.
|
||||
//
|
||||
// Must be called once in the application's lifecycle before IsUnderMaintenance is called, since it
|
||||
// also sets durationToStartFromMidnight.
|
||||
func (c *Config) ValidateAndSetDefaults() error {
|
||||
if c == nil || !c.IsEnabled() {
|
||||
// Don't waste time validating if maintenance is not enabled.
|
||||
return nil
|
||||
}
|
||||
for _, day := range c.Every {
|
||||
isDayValid := false
|
||||
for _, longDayName := range longDayNames {
|
||||
if day == longDayName {
|
||||
isDayValid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isDayValid {
|
||||
return errInvalidDayName
|
||||
}
|
||||
}
|
||||
var err error
|
||||
c.durationToStartFromMidnight, err = hhmmToDuration(c.Start)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c.Duration <= 0 || c.Duration >= 24*time.Hour {
|
||||
return errInvalidMaintenanceDuration
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsUnderMaintenance checks whether the services that Gatus monitors are within the configured maintenance window
|
||||
func (c Config) IsUnderMaintenance() bool {
|
||||
if !c.IsEnabled() {
|
||||
return false
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
var dayWhereMaintenancePeriodWouldStart time.Time
|
||||
if now.Hour() >= int(c.durationToStartFromMidnight.Hours()) {
|
||||
dayWhereMaintenancePeriodWouldStart = now.Truncate(24 * time.Hour)
|
||||
} else {
|
||||
dayWhereMaintenancePeriodWouldStart = now.Add(-c.Duration).Truncate(24 * time.Hour)
|
||||
}
|
||||
hasMaintenanceEveryDay := len(c.Every) == 0
|
||||
hasMaintenancePeriodScheduledToStartOnThatWeekday := c.hasDay(dayWhereMaintenancePeriodWouldStart.Weekday().String())
|
||||
if !hasMaintenanceEveryDay && !hasMaintenancePeriodScheduledToStartOnThatWeekday {
|
||||
// The day when the maintenance period would start is not scheduled
|
||||
// to have any maintenance, so we can just return false.
|
||||
return false
|
||||
}
|
||||
startOfMaintenancePeriod := dayWhereMaintenancePeriodWouldStart.Add(c.durationToStartFromMidnight)
|
||||
endOfMaintenancePeriod := startOfMaintenancePeriod.Add(c.Duration)
|
||||
return now.After(startOfMaintenancePeriod) && now.Before(endOfMaintenancePeriod)
|
||||
}
|
||||
|
||||
func (c Config) hasDay(day string) bool {
|
||||
for _, d := range c.Every {
|
||||
if d == day {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hhmmToDuration(s string) (time.Duration, error) {
|
||||
if len(s) != 5 {
|
||||
return 0, errInvalidMaintenanceStartFormat
|
||||
}
|
||||
var hours, minutes int
|
||||
var err error
|
||||
if hours, err = extractNumericalValueFromPotentiallyZeroPaddedString(s[:2]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if minutes, err = extractNumericalValueFromPotentiallyZeroPaddedString(s[3:5]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
duration := (time.Duration(hours) * time.Hour) + (time.Duration(minutes) * time.Minute)
|
||||
if hours < 0 || hours > 23 || minutes < 0 || minutes > 59 || duration < 0 || duration >= 24*time.Hour {
|
||||
return 0, errInvalidMaintenanceStartFormat
|
||||
}
|
||||
return duration, nil
|
||||
}
|
||||
|
||||
func extractNumericalValueFromPotentiallyZeroPaddedString(s string) (int, error) {
|
||||
return strconv.Atoi(strings.TrimPrefix(s, "0"))
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
package maintenance
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestGetDefaultConfig(t *testing.T) {
|
||||
if *GetDefaultConfig().Enabled {
|
||||
t.Fatal("expected default config to be disabled by default")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_ValidateAndSetDefaults(t *testing.T) {
|
||||
yes, no := true, false
|
||||
scenarios := []struct {
|
||||
name string
|
||||
cfg *Config
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "nil",
|
||||
cfg: nil,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "disabled",
|
||||
cfg: &Config{
|
||||
Enabled: &no,
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid-day",
|
||||
cfg: &Config{
|
||||
Every: []string{"invalid-day"},
|
||||
},
|
||||
expectedError: errInvalidDayName,
|
||||
},
|
||||
{
|
||||
name: "invalid-day",
|
||||
cfg: &Config{
|
||||
Every: []string{"invalid-day"},
|
||||
},
|
||||
expectedError: errInvalidDayName,
|
||||
},
|
||||
{
|
||||
name: "invalid-start-format",
|
||||
cfg: &Config{
|
||||
Start: "0000",
|
||||
},
|
||||
expectedError: errInvalidMaintenanceStartFormat,
|
||||
},
|
||||
{
|
||||
name: "invalid-start-hours",
|
||||
cfg: &Config{
|
||||
Start: "25:00",
|
||||
},
|
||||
expectedError: errInvalidMaintenanceStartFormat,
|
||||
},
|
||||
{
|
||||
name: "invalid-start-minutes",
|
||||
cfg: &Config{
|
||||
Start: "0:61",
|
||||
},
|
||||
expectedError: errInvalidMaintenanceStartFormat,
|
||||
},
|
||||
{
|
||||
name: "invalid-start-minutes-non-numerical",
|
||||
cfg: &Config{
|
||||
Start: "00:zz",
|
||||
},
|
||||
expectedError: strconv.ErrSyntax,
|
||||
},
|
||||
{
|
||||
name: "invalid-start-hours-non-numerical",
|
||||
cfg: &Config{
|
||||
Start: "zz:00",
|
||||
},
|
||||
expectedError: strconv.ErrSyntax,
|
||||
},
|
||||
{
|
||||
name: "invalid-duration",
|
||||
cfg: &Config{
|
||||
Start: "23:00",
|
||||
Duration: 0,
|
||||
},
|
||||
expectedError: errInvalidMaintenanceDuration,
|
||||
},
|
||||
{
|
||||
name: "every-day-at-2300",
|
||||
cfg: &Config{
|
||||
Start: "23:00",
|
||||
Duration: time.Hour,
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "every-monday-at-0000",
|
||||
cfg: &Config{
|
||||
Start: "00:00",
|
||||
Duration: 30 * time.Minute,
|
||||
Every: []string{"Monday"},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "every-friday-and-sunday-at-0000-explicitly-enabled",
|
||||
cfg: &Config{
|
||||
Enabled: &yes,
|
||||
Start: "08:00",
|
||||
Duration: 8 * time.Hour,
|
||||
Every: []string{"Friday", "Sunday"},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.name, func(t *testing.T) {
|
||||
err := scenario.cfg.ValidateAndSetDefaults()
|
||||
if !errors.Is(err, scenario.expectedError) {
|
||||
t.Errorf("expected %v, got %v", scenario.expectedError, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_IsUnderMaintenance(t *testing.T) {
|
||||
yes, no := true, false
|
||||
now := time.Now().UTC()
|
||||
scenarios := []struct {
|
||||
name string
|
||||
cfg *Config
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "disabled",
|
||||
cfg: &Config{
|
||||
Enabled: &no,
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "under-maintenance-explicitly-enabled",
|
||||
cfg: &Config{
|
||||
Enabled: &yes,
|
||||
Start: fmt.Sprintf("%02d:00", now.Hour()),
|
||||
Duration: 2 * time.Hour,
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "under-maintenance-starting-now-for-2h",
|
||||
cfg: &Config{
|
||||
Start: fmt.Sprintf("%02d:00", now.Hour()),
|
||||
Duration: 2 * time.Hour,
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "under-maintenance-starting-now-for-8h",
|
||||
cfg: &Config{
|
||||
Start: fmt.Sprintf("%02d:00", now.Hour()),
|
||||
Duration: 8 * time.Hour,
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "under-maintenance-starting-4h-ago-for-8h",
|
||||
cfg: &Config{
|
||||
Start: fmt.Sprintf("%02d:00", now.Hour()-4),
|
||||
Duration: 8 * time.Hour,
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "under-maintenance-starting-4h-ago-for-3h",
|
||||
cfg: &Config{
|
||||
Start: fmt.Sprintf("%02d:00", now.Hour()-4),
|
||||
Duration: 3 * time.Hour,
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "under-maintenance-starting-5h-ago-for-1h",
|
||||
cfg: &Config{
|
||||
Start: fmt.Sprintf("%02d:00", now.Hour()-5),
|
||||
Duration: time.Hour,
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "not-under-maintenance-today",
|
||||
cfg: &Config{
|
||||
Start: fmt.Sprintf("%02d:00", now.Hour()),
|
||||
Duration: time.Hour,
|
||||
Every: []string{now.Add(48 * time.Hour).Weekday().String()},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.name, func(t *testing.T) {
|
||||
if scenario.cfg.ValidateAndSetDefaults() != nil {
|
||||
t.Fatal("validation shouldn't have returned an error")
|
||||
}
|
||||
isUnderMaintenance := scenario.cfg.IsUnderMaintenance()
|
||||
if isUnderMaintenance != scenario.expected {
|
||||
t.Errorf("expected %v, got %v", scenario.expected, isUnderMaintenance)
|
||||
t.Logf("start=%v; duration=%v; now=%v", scenario.cfg.Start, scenario.cfg.Duration, time.Now().UTC())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"html/template"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultTitle = "Health Dashboard | Gatus"
|
||||
defaultLogo = ""
|
||||
)
|
||||
|
||||
// UIConfig is the configuration for the UI of Gatus
|
||||
type UIConfig struct {
|
||||
Title string `yaml:"title"` // Title of the page
|
||||
Logo string `yaml:"logo"` // Logo to display on the page
|
||||
}
|
||||
|
||||
// GetDefaultUIConfig returns a UIConfig struct with the default values
|
||||
func GetDefaultUIConfig() *UIConfig {
|
||||
return &UIConfig{
|
||||
Title: defaultTitle,
|
||||
Logo: defaultLogo,
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *UIConfig) validateAndSetDefaults() error {
|
||||
if len(cfg.Title) == 0 {
|
||||
cfg.Title = defaultTitle
|
||||
}
|
||||
t, err := template.ParseFiles(StaticFolder + "/index.html")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var buffer bytes.Buffer
|
||||
err = t.Execute(&buffer, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"html/template"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultTitle = "Health Dashboard | Gatus"
|
||||
defaultLogo = ""
|
||||
)
|
||||
|
||||
var (
|
||||
// StaticFolder is the path to the location of the static folder from the root path of the project
|
||||
// The only reason this is exposed is to allow running tests from a different path than the root path of the project
|
||||
StaticFolder = "./web/static"
|
||||
)
|
||||
|
||||
// Config is the configuration for the UI of Gatus
|
||||
type Config struct {
|
||||
Title string `yaml:"title"` // Title of the page
|
||||
Logo string `yaml:"logo"` // Logo to display on the page
|
||||
}
|
||||
|
||||
// GetDefaultConfig returns a Config struct with the default values
|
||||
func GetDefaultConfig() *Config {
|
||||
return &Config{
|
||||
Title: defaultTitle,
|
||||
Logo: defaultLogo,
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateAndSetDefaults validates the UI configuration and sets the default values if necessary.
|
||||
func (cfg *Config) ValidateAndSetDefaults() error {
|
||||
if len(cfg.Title) == 0 {
|
||||
cfg.Title = defaultTitle
|
||||
}
|
||||
t, err := template.ParseFiles(StaticFolder + "/index.html")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var buffer bytes.Buffer
|
||||
err = t.Execute(&buffer, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestConfig_ValidateAndSetDefaults(t *testing.T) {
|
||||
StaticFolder = "../../web/static"
|
||||
defer func() {
|
||||
StaticFolder = "./web/static"
|
||||
}()
|
||||
cfg := &Config{Title: ""}
|
||||
if err := cfg.ValidateAndSetDefaults(); err != nil {
|
||||
t.Error("expected no error, got", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetDefaultConfig(t *testing.T) {
|
||||
defaultConfig := GetDefaultConfig()
|
||||
if defaultConfig.Title != defaultTitle {
|
||||
t.Error("expected GetDefaultConfig() to return defaultTitle, got", defaultConfig.Title)
|
||||
}
|
||||
if defaultConfig.Logo != defaultLogo {
|
||||
t.Error("expected GetDefaultConfig() to return defaultLogo, got", defaultConfig.Logo)
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package config
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestUIConfig_validateAndSetDefaults(t *testing.T) {
|
||||
StaticFolder = "../web/static"
|
||||
defer func() {
|
||||
StaticFolder = "./web/static"
|
||||
}()
|
||||
uiConfig := &UIConfig{Title: ""}
|
||||
if err := uiConfig.validateAndSetDefaults(); err != nil {
|
||||
t.Error("expected no error, got", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetDefaultUIConfig(t *testing.T) {
|
||||
defaultUIConfig := GetDefaultUIConfig()
|
||||
if defaultUIConfig.Title != defaultTitle {
|
||||
t.Error("expected GetDefaultUIConfig() to return defaultTitle, got", defaultUIConfig.Title)
|
||||
}
|
||||
if defaultUIConfig.Logo != defaultLogo {
|
||||
t.Error("expected GetDefaultUIConfig() to return defaultLogo, got", defaultUIConfig.Logo)
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,21 @@
|
||||
package config
|
||||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
// WebConfig is the structure which supports the configuration of the endpoint
|
||||
const (
|
||||
// DefaultAddress is the default address the service will bind to
|
||||
DefaultAddress = "0.0.0.0"
|
||||
|
||||
// DefaultPort is the default port the service will listen on
|
||||
DefaultPort = 8080
|
||||
)
|
||||
|
||||
// Config is the structure which supports the configuration of the endpoint
|
||||
// which provides access to the web frontend
|
||||
type WebConfig struct {
|
||||
type Config struct {
|
||||
// Address to listen on (defaults to 0.0.0.0 specified by DefaultAddress)
|
||||
Address string `yaml:"address"`
|
||||
|
||||
@@ -15,13 +23,13 @@ type WebConfig struct {
|
||||
Port int `yaml:"port"`
|
||||
}
|
||||
|
||||
// GetDefaultWebConfig returns a WebConfig struct with the default values
|
||||
func GetDefaultWebConfig() *WebConfig {
|
||||
return &WebConfig{Address: DefaultAddress, Port: DefaultPort}
|
||||
// GetDefaultConfig returns a Config struct with the default values
|
||||
func GetDefaultConfig() *Config {
|
||||
return &Config{Address: DefaultAddress, Port: DefaultPort}
|
||||
}
|
||||
|
||||
// validateAndSetDefaults checks and sets the default values for fields that are not set
|
||||
func (web *WebConfig) validateAndSetDefaults() error {
|
||||
// ValidateAndSetDefaults validates the web configuration and sets the default values if necessary.
|
||||
func (web *Config) ValidateAndSetDefaults() error {
|
||||
// Validate the Address
|
||||
if len(web.Address) == 0 {
|
||||
web.Address = DefaultAddress
|
||||
@@ -36,6 +44,6 @@ func (web *WebConfig) validateAndSetDefaults() error {
|
||||
}
|
||||
|
||||
// SocketAddress returns the combination of the Address and the Port
|
||||
func (web *WebConfig) SocketAddress() string {
|
||||
func (web *Config) SocketAddress() string {
|
||||
return fmt.Sprintf("%s:%d", web.Address, web.Port)
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetDefaultConfig(t *testing.T) {
|
||||
defaultConfig := GetDefaultConfig()
|
||||
if defaultConfig.Port != DefaultPort {
|
||||
t.Error("expected default config to have the default port")
|
||||
}
|
||||
if defaultConfig.Address != DefaultAddress {
|
||||
t.Error("expected default config to have the default address")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_ValidateAndSetDefaults(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
name string
|
||||
cfg *Config
|
||||
expectedAddress string
|
||||
expectedPort int
|
||||
expectedErr bool
|
||||
}{
|
||||
{
|
||||
name: "no-explicit-config",
|
||||
cfg: &Config{},
|
||||
expectedAddress: "0.0.0.0",
|
||||
expectedPort: 8080,
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalid-port",
|
||||
cfg: &Config{Port: 100000000},
|
||||
expectedErr: true,
|
||||
},
|
||||
}
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.name, func(t *testing.T) {
|
||||
err := scenario.cfg.ValidateAndSetDefaults()
|
||||
if (err != nil) != scenario.expectedErr {
|
||||
t.Errorf("expected the existence of an error to be %v, got %v", scenario.expectedErr, err)
|
||||
return
|
||||
}
|
||||
if !scenario.expectedErr {
|
||||
if scenario.cfg.Port != scenario.expectedPort {
|
||||
t.Errorf("expected port to be %d, got %d", scenario.expectedPort, scenario.cfg.Port)
|
||||
}
|
||||
if scenario.cfg.Address != scenario.expectedAddress {
|
||||
t.Errorf("expected address to be %s, got %s", scenario.expectedAddress, scenario.cfg.Address)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_SocketAddress(t *testing.T) {
|
||||
web := &Config{
|
||||
Address: "0.0.0.0",
|
||||
Port: 8081,
|
||||
}
|
||||
if web.SocketAddress() != "0.0.0.0:8081" {
|
||||
t.Errorf("expected %s, got %s", "0.0.0.0:8081", web.SocketAddress())
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestWebConfig_SocketAddress(t *testing.T) {
|
||||
web := &WebConfig{
|
||||
Address: "0.0.0.0",
|
||||
Port: 8081,
|
||||
}
|
||||
if web.SocketAddress() != "0.0.0.0:8081" {
|
||||
t.Errorf("expected %s, got %s", "0.0.0.0:8081", web.SocketAddress())
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,8 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/TwinProduction/gatus/config"
|
||||
"github.com/TwinProduction/gatus/config/ui"
|
||||
"github.com/TwinProduction/gatus/config/web"
|
||||
"github.com/TwinProduction/gatus/controller/handler"
|
||||
"github.com/TwinProduction/gatus/security"
|
||||
)
|
||||
@@ -20,8 +21,8 @@ var (
|
||||
)
|
||||
|
||||
// Handle creates the router and starts the server
|
||||
func Handle(securityConfig *security.Config, webConfig *config.WebConfig, uiConfig *config.UIConfig, enableMetrics bool) {
|
||||
var router http.Handler = handler.CreateRouter(config.StaticFolder, securityConfig, uiConfig, enableMetrics)
|
||||
func Handle(securityConfig *security.Config, webConfig *web.Config, uiConfig *ui.Config, enableMetrics bool) {
|
||||
var router http.Handler = handler.CreateRouter(ui.StaticFolder, securityConfig, uiConfig, enableMetrics)
|
||||
if os.Getenv("ENVIRONMENT") == "dev" {
|
||||
router = handler.DevelopmentCORS(router)
|
||||
}
|
||||
|
||||
@@ -8,12 +8,13 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/TwinProduction/gatus/config"
|
||||
"github.com/TwinProduction/gatus/config/web"
|
||||
"github.com/TwinProduction/gatus/core"
|
||||
)
|
||||
|
||||
func TestHandle(t *testing.T) {
|
||||
cfg := &config.Config{
|
||||
Web: &config.WebConfig{
|
||||
Web: &web.Config{
|
||||
Address: "0.0.0.0",
|
||||
Port: rand.Intn(65534),
|
||||
},
|
||||
|
||||
@@ -3,14 +3,14 @@ package handler
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/TwinProduction/gatus/config"
|
||||
"github.com/TwinProduction/gatus/config/ui"
|
||||
"github.com/TwinProduction/gatus/security"
|
||||
"github.com/TwinProduction/health"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
func CreateRouter(staticFolder string, securityConfig *security.Config, uiConfig *config.UIConfig, enabledMetrics bool) *mux.Router {
|
||||
func CreateRouter(staticFolder string, securityConfig *security.Config, uiConfig *ui.Config, enabledMetrics bool) *mux.Router {
|
||||
router := mux.NewRouter()
|
||||
if enabledMetrics {
|
||||
router.Handle("/metrics", promhttp.Handler()).Methods("GET")
|
||||
|
||||
@@ -5,10 +5,10 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/TwinProduction/gatus/config"
|
||||
"github.com/TwinProduction/gatus/config/ui"
|
||||
)
|
||||
|
||||
func SinglePageApplication(staticFolder string, ui *config.UIConfig) http.HandlerFunc {
|
||||
func SinglePageApplication(staticFolder string, ui *ui.Config) http.HandlerFunc {
|
||||
return func(writer http.ResponseWriter, request *http.Request) {
|
||||
t, err := template.ParseFiles(staticFolder + "/index.html")
|
||||
if err != nil {
|
||||
|
||||
+20
-3
@@ -44,7 +44,11 @@ var (
|
||||
)
|
||||
|
||||
// Service is the configuration of a monitored endpoint
|
||||
// XXX: Rename this to Endpoint in v4.0.0?
|
||||
type Service struct {
|
||||
// Enabled defines whether to enable the service
|
||||
Enabled *bool `yaml:"enabled,omitempty"`
|
||||
|
||||
// Name of the service. Can be anything.
|
||||
Name string `yaml:"name"`
|
||||
|
||||
@@ -91,6 +95,14 @@ type Service struct {
|
||||
NumberOfSuccessesInARow int
|
||||
}
|
||||
|
||||
// IsEnabled returns whether the service is enabled or not
|
||||
func (service Service) IsEnabled() bool {
|
||||
if service.Enabled == nil {
|
||||
return true
|
||||
}
|
||||
return *service.Enabled
|
||||
}
|
||||
|
||||
// ValidateAndSetDefaults validates the service's configuration and sets the default value of fields that have one
|
||||
func (service *Service) ValidateAndSetDefaults() error {
|
||||
// Set default values
|
||||
@@ -206,7 +218,8 @@ func (service *Service) call(result *Result) {
|
||||
isServiceTCP := strings.HasPrefix(service.URL, "tcp://")
|
||||
isServiceICMP := strings.HasPrefix(service.URL, "icmp://")
|
||||
isServiceStartTLS := strings.HasPrefix(service.URL, "starttls://")
|
||||
isServiceHTTP := !isServiceDNS && !isServiceTCP && !isServiceICMP && !isServiceStartTLS
|
||||
isServiceTLS := strings.HasPrefix(service.URL, "tls://")
|
||||
isServiceHTTP := !isServiceDNS && !isServiceTCP && !isServiceICMP && !isServiceStartTLS && !isServiceTLS
|
||||
if isServiceHTTP {
|
||||
request = service.buildHTTPRequest()
|
||||
}
|
||||
@@ -214,8 +227,12 @@ func (service *Service) call(result *Result) {
|
||||
if isServiceDNS {
|
||||
service.DNS.query(service.URL, result)
|
||||
result.Duration = time.Since(startTime)
|
||||
} else if isServiceStartTLS {
|
||||
result.Connected, certificate, err = client.CanPerformStartTLS(strings.TrimPrefix(service.URL, "starttls://"), service.ClientConfig)
|
||||
} else if isServiceStartTLS || isServiceTLS {
|
||||
if isServiceStartTLS {
|
||||
result.Connected, certificate, err = client.CanPerformStartTLS(strings.TrimPrefix(service.URL, "starttls://"), service.ClientConfig)
|
||||
} else {
|
||||
result.Connected, certificate, err = client.CanPerformStartTLS(strings.TrimPrefix(service.URL, "tls://"), service.ClientConfig)
|
||||
}
|
||||
if err != nil {
|
||||
result.AddError(err.Error())
|
||||
return
|
||||
|
||||
+32
-20
@@ -10,11 +10,23 @@ import (
|
||||
"github.com/TwinProduction/gatus/client"
|
||||
)
|
||||
|
||||
func TestService_IsEnabled(t *testing.T) {
|
||||
if !(Service{Enabled: nil}).IsEnabled() {
|
||||
t.Error("service.IsEnabled() should've returned true, because Enabled was set to nil")
|
||||
}
|
||||
if value := false; (Service{Enabled: &value}).IsEnabled() {
|
||||
t.Error("service.IsEnabled() should've returned false, because Enabled was set to false")
|
||||
}
|
||||
if value := true; !(Service{Enabled: &value}).IsEnabled() {
|
||||
t.Error("Service.IsEnabled() should've returned true, because Enabled was set to true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestService_ValidateAndSetDefaults(t *testing.T) {
|
||||
condition := Condition("[STATUS] == 200")
|
||||
service := Service{
|
||||
Name: "twinnation-health",
|
||||
URL: "https://twinnation.org/health",
|
||||
Name: "website-health",
|
||||
URL: "https://twin.sh/health",
|
||||
Conditions: []*Condition{&condition},
|
||||
Alerts: []*alert.Alert{{Type: alert.TypePagerDuty}},
|
||||
}
|
||||
@@ -58,8 +70,8 @@ func TestService_ValidateAndSetDefaults(t *testing.T) {
|
||||
func TestService_ValidateAndSetDefaultsWithClientConfig(t *testing.T) {
|
||||
condition := Condition("[STATUS] == 200")
|
||||
service := Service{
|
||||
Name: "twinnation-health",
|
||||
URL: "https://twinnation.org/health",
|
||||
Name: "website-health",
|
||||
URL: "https://twin.sh/health",
|
||||
Conditions: []*Condition{&condition},
|
||||
ClientConfig: &client.Config{
|
||||
Insecure: true,
|
||||
@@ -147,8 +159,8 @@ func TestService_ValidateAndSetDefaultsWithDNS(t *testing.T) {
|
||||
func TestService_buildHTTPRequest(t *testing.T) {
|
||||
condition := Condition("[STATUS] == 200")
|
||||
service := Service{
|
||||
Name: "twinnation-health",
|
||||
URL: "https://twinnation.org/health",
|
||||
Name: "website-health",
|
||||
URL: "https://twin.sh/health",
|
||||
Conditions: []*Condition{&condition},
|
||||
}
|
||||
service.ValidateAndSetDefaults()
|
||||
@@ -156,8 +168,8 @@ func TestService_buildHTTPRequest(t *testing.T) {
|
||||
if request.Method != "GET" {
|
||||
t.Error("request.Method should've been GET, but was", request.Method)
|
||||
}
|
||||
if request.Host != "twinnation.org" {
|
||||
t.Error("request.Host should've been twinnation.org, but was", request.Host)
|
||||
if request.Host != "twin.sh" {
|
||||
t.Error("request.Host should've been twin.sh, but was", request.Host)
|
||||
}
|
||||
if userAgent := request.Header.Get("User-Agent"); userAgent != GatusUserAgent {
|
||||
t.Errorf("request.Header.Get(User-Agent) should've been %s, but was %s", GatusUserAgent, userAgent)
|
||||
@@ -167,8 +179,8 @@ func TestService_buildHTTPRequest(t *testing.T) {
|
||||
func TestService_buildHTTPRequestWithCustomUserAgent(t *testing.T) {
|
||||
condition := Condition("[STATUS] == 200")
|
||||
service := Service{
|
||||
Name: "twinnation-health",
|
||||
URL: "https://twinnation.org/health",
|
||||
Name: "website-health",
|
||||
URL: "https://twin.sh/health",
|
||||
Conditions: []*Condition{&condition},
|
||||
Headers: map[string]string{
|
||||
"User-Agent": "Test/2.0",
|
||||
@@ -179,8 +191,8 @@ func TestService_buildHTTPRequestWithCustomUserAgent(t *testing.T) {
|
||||
if request.Method != "GET" {
|
||||
t.Error("request.Method should've been GET, but was", request.Method)
|
||||
}
|
||||
if request.Host != "twinnation.org" {
|
||||
t.Error("request.Host should've been twinnation.org, but was", request.Host)
|
||||
if request.Host != "twin.sh" {
|
||||
t.Error("request.Host should've been twin.sh, but was", request.Host)
|
||||
}
|
||||
if userAgent := request.Header.Get("User-Agent"); userAgent != "Test/2.0" {
|
||||
t.Errorf("request.Header.Get(User-Agent) should've been %s, but was %s", "Test/2.0", userAgent)
|
||||
@@ -190,8 +202,8 @@ func TestService_buildHTTPRequestWithCustomUserAgent(t *testing.T) {
|
||||
func TestService_buildHTTPRequestWithHostHeader(t *testing.T) {
|
||||
condition := Condition("[STATUS] == 200")
|
||||
service := Service{
|
||||
Name: "twinnation-health",
|
||||
URL: "https://twinnation.org/health",
|
||||
Name: "website-health",
|
||||
URL: "https://twin.sh/health",
|
||||
Method: "POST",
|
||||
Conditions: []*Condition{&condition},
|
||||
Headers: map[string]string{
|
||||
@@ -211,8 +223,8 @@ func TestService_buildHTTPRequestWithHostHeader(t *testing.T) {
|
||||
func TestService_buildHTTPRequestWithGraphQLEnabled(t *testing.T) {
|
||||
condition := Condition("[STATUS] == 200")
|
||||
service := Service{
|
||||
Name: "twinnation-graphql",
|
||||
URL: "https://twinnation.org/graphql",
|
||||
Name: "website-graphql",
|
||||
URL: "https://twin.sh/graphql",
|
||||
Method: "POST",
|
||||
Conditions: []*Condition{&condition},
|
||||
GraphQL: true,
|
||||
@@ -243,8 +255,8 @@ func TestIntegrationEvaluateHealth(t *testing.T) {
|
||||
condition := Condition("[STATUS] == 200")
|
||||
bodyCondition := Condition("[BODY].status == UP")
|
||||
service := Service{
|
||||
Name: "twinnation-health",
|
||||
URL: "https://twinnation.org/health",
|
||||
Name: "website-health",
|
||||
URL: "https://twin.sh/health",
|
||||
Conditions: []*Condition{&condition, &bodyCondition},
|
||||
}
|
||||
service.ValidateAndSetDefaults()
|
||||
@@ -263,8 +275,8 @@ func TestIntegrationEvaluateHealth(t *testing.T) {
|
||||
func TestIntegrationEvaluateHealthWithFailure(t *testing.T) {
|
||||
condition := Condition("[STATUS] == 500")
|
||||
service := Service{
|
||||
Name: "twinnation-health",
|
||||
URL: "https://twinnation.org/health",
|
||||
Name: "website-health",
|
||||
URL: "https://twin.sh/health",
|
||||
Conditions: []*Condition{&condition},
|
||||
}
|
||||
service.ValidateAndSetDefaults()
|
||||
|
||||
@@ -39,9 +39,9 @@ alerting:
|
||||
You can now add alerts of type `pagerduty` in the services you've defined, like so:
|
||||
```yaml
|
||||
services:
|
||||
- name: twinnation
|
||||
- name: website
|
||||
interval: 30s
|
||||
url: "https://twinnation.org/health"
|
||||
url: "https://twin.sh/health"
|
||||
alerts:
|
||||
- type: pagerduty
|
||||
enabled: true
|
||||
@@ -56,7 +56,7 @@ services:
|
||||
```
|
||||
|
||||
The sample above will do the following:
|
||||
- Send a request to the `https://twinnation.org/health` (`services[].url`) specified every **30s** (`services[].interval`)
|
||||
- Send a request to the `https://twin.sh/health` (`services[].url`) specified every **30s** (`services[].interval`)
|
||||
- Evaluate the conditions to determine whether the service is "healthy" or not
|
||||
- **If all conditions are not met 3 (`services[].alerts[].failure-threshold`) times in a row**: Gatus will create a new incident
|
||||
- **If, after an incident has been triggered, all conditions are met 5 (`services[].alerts[].success-threshold`) times in a row _AND_ `services[].alerts[].send-on-resolved` is set to `true`**: Gatus will resolve the triggered incident
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
metrics: true
|
||||
services:
|
||||
- name: TwiNNatioN
|
||||
url: https://twinnation.org/health
|
||||
- name: website
|
||||
url: https://twin.sh/health
|
||||
interval: 30s
|
||||
conditions:
|
||||
- "[STATUS] == 200"
|
||||
- name: GitHub
|
||||
- name: github
|
||||
url: https://api.github.com/healthz
|
||||
interval: 5m
|
||||
conditions:
|
||||
- "[STATUS] == 200"
|
||||
- name: Example
|
||||
- name: example
|
||||
url: https://example.com/
|
||||
conditions:
|
||||
- "[STATUS] == 200"
|
||||
@@ -3,12 +3,12 @@ data:
|
||||
config.yaml: |
|
||||
metrics: true
|
||||
services:
|
||||
- name: TwiNNatioN
|
||||
url: https://twinnation.org/health
|
||||
- name: website
|
||||
url: https://twin.sh/health
|
||||
interval: 1m
|
||||
conditions:
|
||||
- "[STATUS] == 200"
|
||||
- name: GitHub
|
||||
- name: github
|
||||
url: https://api.github.com/healthz
|
||||
interval: 5m
|
||||
conditions:
|
||||
@@ -23,7 +23,7 @@ data:
|
||||
- "[BODY].text == pat(*cat*)"
|
||||
- "[STATUS] == pat(2*)"
|
||||
- "[CONNECTED] == true"
|
||||
- name: Example
|
||||
- name: example
|
||||
url: https://example.com/
|
||||
conditions:
|
||||
- "[STATUS] == 200"
|
||||
|
||||
File renamed without changes.
File renamed without changes.
@@ -461,10 +461,7 @@ func (s *Store) updateServiceUptime(tx *sql.Tx, serviceID int64, result *core.Re
|
||||
successfulExecutions,
|
||||
result.Duration.Milliseconds(),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Store) getAllServiceKeys(tx *sql.Tx) (keys []string, err error) {
|
||||
@@ -477,7 +474,6 @@ func (s *Store) getAllServiceKeys(tx *sql.Tx) (keys []string, err error) {
|
||||
_ = rows.Scan(&key)
|
||||
keys = append(keys, key)
|
||||
}
|
||||
_ = rows.Close()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -546,7 +542,6 @@ func (s *Store) getEventsByServiceID(tx *sql.Tx, serviceID int64, page, pageSize
|
||||
_ = rows.Scan(&event.Type, &event.Timestamp)
|
||||
events = append(events, event)
|
||||
}
|
||||
_ = rows.Close()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -579,7 +574,6 @@ func (s *Store) getResultsByServiceID(tx *sql.Tx, serviceID int64, page, pageSiz
|
||||
results = append([]*core.Result{result}, results...)
|
||||
idResultMap[id] = result
|
||||
}
|
||||
_ = rows.Close()
|
||||
// Get condition results
|
||||
args := make([]interface{}, 0, len(idResultMap))
|
||||
query := `SELECT service_result_id, condition, success
|
||||
@@ -596,6 +590,7 @@ func (s *Store) getResultsByServiceID(tx *sql.Tx, serviceID int64, page, pageSiz
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close() // explicitly defer the close in case an error happens during the scan
|
||||
for rows.Next() {
|
||||
conditionResult := &core.ConditionResult{}
|
||||
var serviceResultID int64
|
||||
@@ -626,9 +621,7 @@ func (s *Store) getServiceUptime(tx *sql.Tx, serviceID int64, from, to time.Time
|
||||
var totalExecutions, totalSuccessfulExecutions, totalResponseTime int
|
||||
for rows.Next() {
|
||||
_ = rows.Scan(&totalExecutions, &totalSuccessfulExecutions, &totalResponseTime)
|
||||
break
|
||||
}
|
||||
_ = rows.Close()
|
||||
if totalExecutions > 0 {
|
||||
uptime = float64(totalSuccessfulExecutions) / float64(totalExecutions)
|
||||
avgResponseTime = time.Duration(float64(totalResponseTime)/float64(totalExecutions)) * time.Millisecond
|
||||
@@ -657,7 +650,6 @@ func (s *Store) getServiceAverageResponseTime(tx *sql.Tx, serviceID int64, from,
|
||||
for rows.Next() {
|
||||
_ = rows.Scan(&totalExecutions, &totalResponseTime)
|
||||
}
|
||||
_ = rows.Close()
|
||||
if totalExecutions == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
@@ -688,7 +680,6 @@ func (s *Store) getServiceHourlyAverageResponseTimes(tx *sql.Tx, serviceID int64
|
||||
_ = rows.Scan(&unixTimestampFlooredAtHour, &totalExecutions, &totalResponseTime)
|
||||
hourlyAverageResponseTimes[unixTimestampFlooredAtHour] = int(float64(totalResponseTime) / float64(totalExecutions))
|
||||
}
|
||||
_ = rows.Close()
|
||||
return hourlyAverageResponseTimes, nil
|
||||
}
|
||||
|
||||
@@ -735,9 +726,7 @@ func (s *Store) getAgeOfOldestServiceUptimeEntry(tx *sql.Tx, serviceID int64) (t
|
||||
for rows.Next() {
|
||||
_ = rows.Scan(&oldestServiceUptimeUnixTimestamp)
|
||||
found = true
|
||||
break
|
||||
}
|
||||
_ = rows.Close()
|
||||
if !found {
|
||||
return 0, errNoRowsReturned
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ func TestHandleAlerting(t *testing.T) {
|
||||
Debug: true,
|
||||
Alerting: &alerting.Config{
|
||||
Custom: &custom.AlertProvider{
|
||||
URL: "https://twinnation.org/health",
|
||||
URL: "https://twin.sh/health",
|
||||
Method: "GET",
|
||||
},
|
||||
},
|
||||
@@ -99,7 +99,7 @@ func TestHandleAlertingWhenTriggeredAlertIsAlmostResolvedButServiceStartFailingA
|
||||
Debug: true,
|
||||
Alerting: &alerting.Config{
|
||||
Custom: &custom.AlertProvider{
|
||||
URL: "https://twinnation.org/health",
|
||||
URL: "https://twin.sh/health",
|
||||
Method: "GET",
|
||||
},
|
||||
},
|
||||
@@ -133,7 +133,7 @@ func TestHandleAlertingWhenTriggeredAlertIsResolvedButSendOnResolvedIsFalse(t *t
|
||||
Debug: true,
|
||||
Alerting: &alerting.Config{
|
||||
Custom: &custom.AlertProvider{
|
||||
URL: "https://twinnation.org/health",
|
||||
URL: "https://twin.sh/health",
|
||||
Method: "GET",
|
||||
},
|
||||
},
|
||||
@@ -202,7 +202,7 @@ func TestHandleAlertingWithProviderThatReturnsAnError(t *testing.T) {
|
||||
Debug: true,
|
||||
Alerting: &alerting.Config{
|
||||
Custom: &custom.AlertProvider{
|
||||
URL: "https://twinnation.org/health",
|
||||
URL: "https://twin.sh/health",
|
||||
Method: "GET",
|
||||
},
|
||||
},
|
||||
@@ -260,7 +260,7 @@ func TestHandleAlertingWithProviderThatOnlyReturnsErrorOnResolve(t *testing.T) {
|
||||
Debug: true,
|
||||
Alerting: &alerting.Config{
|
||||
Custom: &custom.AlertProvider{
|
||||
URL: "https://twinnation.org/health",
|
||||
URL: "https://twin.sh/health",
|
||||
Method: "GET",
|
||||
},
|
||||
},
|
||||
|
||||
+18
-9
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/TwinProduction/gatus/alerting"
|
||||
"github.com/TwinProduction/gatus/config"
|
||||
"github.com/TwinProduction/gatus/config/maintenance"
|
||||
"github.com/TwinProduction/gatus/core"
|
||||
"github.com/TwinProduction/gatus/metric"
|
||||
"github.com/TwinProduction/gatus/storage"
|
||||
@@ -26,16 +27,18 @@ var (
|
||||
func Monitor(cfg *config.Config) {
|
||||
ctx, cancelFunc = context.WithCancel(context.Background())
|
||||
for _, service := range cfg.Services {
|
||||
// To prevent multiple requests from running at the same time, we'll wait for a little bit before each iteration
|
||||
time.Sleep(1111 * time.Millisecond)
|
||||
go monitor(service, cfg.Alerting, cfg.DisableMonitoringLock, cfg.Metrics, cfg.Debug, ctx)
|
||||
if service.IsEnabled() {
|
||||
// To prevent multiple requests from running at the same time, we'll wait for a little before each iteration
|
||||
time.Sleep(1111 * time.Millisecond)
|
||||
go monitor(service, cfg.Alerting, cfg.Maintenance, cfg.DisableMonitoringLock, cfg.Metrics, cfg.Debug, ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// monitor monitors a single service in a loop
|
||||
func monitor(service *core.Service, alertingConfig *alerting.Config, disableMonitoringLock, enabledMetrics, debug bool, ctx context.Context) {
|
||||
func monitor(service *core.Service, alertingConfig *alerting.Config, maintenanceConfig *maintenance.Config, disableMonitoringLock, enabledMetrics, debug bool, ctx context.Context) {
|
||||
// Run it immediately on start
|
||||
execute(service, alertingConfig, disableMonitoringLock, enabledMetrics, debug)
|
||||
execute(service, alertingConfig, maintenanceConfig, disableMonitoringLock, enabledMetrics, debug)
|
||||
// Loop for the next executions
|
||||
for {
|
||||
select {
|
||||
@@ -43,12 +46,12 @@ func monitor(service *core.Service, alertingConfig *alerting.Config, disableMoni
|
||||
log.Printf("[watchdog][monitor] Canceling current execution of group=%s; service=%s", service.Group, service.Name)
|
||||
return
|
||||
case <-time.After(service.Interval):
|
||||
execute(service, alertingConfig, disableMonitoringLock, enabledMetrics, debug)
|
||||
execute(service, alertingConfig, maintenanceConfig, disableMonitoringLock, enabledMetrics, debug)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func execute(service *core.Service, alertingConfig *alerting.Config, disableMonitoringLock, enabledMetrics, debug bool) {
|
||||
func execute(service *core.Service, alertingConfig *alerting.Config, maintenanceConfig *maintenance.Config, disableMonitoringLock, enabledMetrics, debug bool) {
|
||||
if !disableMonitoringLock {
|
||||
// By placing the lock here, we prevent multiple services from being monitored at the exact same time, which
|
||||
// could cause performance issues and return inaccurate results
|
||||
@@ -70,7 +73,11 @@ func execute(service *core.Service, alertingConfig *alerting.Config, disableMoni
|
||||
len(result.Errors),
|
||||
result.Duration.Round(time.Millisecond),
|
||||
)
|
||||
HandleAlerting(service, result, alertingConfig, debug)
|
||||
if !maintenanceConfig.IsUnderMaintenance() {
|
||||
HandleAlerting(service, result, alertingConfig, debug)
|
||||
} else if debug {
|
||||
log.Println("[watchdog][execute] Not handling alerting because currently in the maintenance window")
|
||||
}
|
||||
if debug {
|
||||
log.Printf("[watchdog][execute] Waiting for interval=%s before monitoring group=%s service=%s again", service.Interval, service.Group, service.Name)
|
||||
}
|
||||
@@ -81,7 +88,9 @@ func execute(service *core.Service, alertingConfig *alerting.Config, disableMoni
|
||||
|
||||
// UpdateServiceStatuses updates the slice of service statuses
|
||||
func UpdateServiceStatuses(service *core.Service, result *core.Result) {
|
||||
storage.Get().Insert(service, result)
|
||||
if err := storage.Get().Insert(service, result); err != nil {
|
||||
log.Println("[watchdog][UpdateServiceStatuses] Failed to insert data in storage:", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Shutdown stops monitoring all services
|
||||
|
||||
Generated
+3142
-6884
File diff suppressed because it is too large.
Load diff
+10
-10
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gatus",
|
||||
"version": "2.0.0",
|
||||
"version": "3.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve --mode development",
|
||||
@@ -8,22 +8,22 @@
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": "^3.16.1",
|
||||
"vue": "^3.2.2",
|
||||
"core-js": "^3.17.3",
|
||||
"vue": "^3.2.11",
|
||||
"vue-router": "^4.0.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^5.0.0-beta.0",
|
||||
"@vue/cli-plugin-eslint": "^5.0.0-beta.0",
|
||||
"@vue/cli-plugin-router": "^5.0.0-beta.0",
|
||||
"@vue/cli-plugin-babel": "^5.0.0-beta.3",
|
||||
"@vue/cli-plugin-eslint": "^5.0.0-beta.3",
|
||||
"@vue/cli-plugin-router": "^5.0.0-beta.3",
|
||||
"@vue/cli-service": "^5.0.0-beta.3",
|
||||
"@vue/compiler-sfc": "^3.2.2",
|
||||
"autoprefixer": "^10.3.1",
|
||||
"@vue/compiler-sfc": "^3.2.11",
|
||||
"autoprefixer": "^10.3.4",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-vue": "^7.16.0",
|
||||
"eslint-plugin-vue": "^7.17.0",
|
||||
"postcss": "^8.3.6",
|
||||
"tailwindcss": "^2.2.7"
|
||||
"tailwindcss": "^2.2.15"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<script type="text/javascript">
|
||||
window.config = {
|
||||
logo: "{{ .Logo }}"
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
window.config = {
|
||||
logo: "{{ .Logo }}"
|
||||
};
|
||||
</script>
|
||||
<title>{{ .Title }}</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
|
||||
@@ -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}
|
||||
|
||||
/*! tailwindcss v2.2.7 | MIT License | https://tailwindcss.com */
|
||||
/*! tailwindcss v2.2.15 | MIT License | https://tailwindcss.com */
|
||||
|
||||
/*! modern-normalize v1.1.0 | MIT License | https://github.com/sindresorhus/modern-normalize */html{-moz-tab-size:4;-o-tab-size:4;tab-size:4;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}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:currentColor}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}[hidden]{display:none}*,:after,:before{--tw-border-opacity:1;border-color:rgba(229,231,235,var(--tw-border-opacity))}.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}.absolute{position:absolute}.relative{position:relative}.top-2{top:.5rem}.bottom-2{bottom:.5rem}.left-2{left:.5rem}.float-right{float:right}.mx-auto{margin-left:auto;margin-right:auto}.my-4{margin-top:1rem;margin-bottom:1rem}.my-auto{margin-top:auto;margin-bottom:auto}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.mt-12{margin-top:3rem}.mr-2{margin-right:.5rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.mb-10{margin-bottom:2.5rem}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.table{display:table}.hidden{display:none}.w-1\/2{width:50%}.w-1\/4{width:25%}.w-3\/4{width:75%}.flex-1{flex:1 1 0%}@-webkit-keyframes spin{to{transform:rotate(1turn)}}@keyframes spin{to{transform:rotate(1turn)}}@-webkit-keyframes ping{75%,to{transform:scale(2);opacity:0}}@keyframes ping{75%,to{transform:scale(2);opacity:0}}@-webkit-keyframes pulse{50%{opacity:.5}}@keyframes pulse{50%{opacity:.5}}@-webkit-keyframes bounce{0%,to{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}@keyframes bounce{0%,to{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.justify-end{justify-content:flex-end}.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)))}.overflow-x-hidden{overflow-x:hidden}.rounded-none{border-radius:0}.rounded{border-radius:.25rem}.rounded-xl{border-radius:.75rem}.rounded-full{border-radius:9999px}.border{border-width:1px}.border-t{border-top-width:1px}.border-r{border-right-width:1px}.border-l{border-left-width:1px}.border-dashed{border-style:dashed}.border-gray-200{Line truncated
|
||||
@@ -1,3 +1,3 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><script>window.config = {
|
||||
logo: "{{ .Logo }}"
|
||||
};</script><title>{{ .Title }}</title><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"><script defer="defer" src="/js/chunk-vendors.js" type="module"></script><script defer="defer" src="/js/app.js" type="module"></script><link href="/css/app.css" rel="stylesheet"><script defer="defer" src="/js/chunk-vendors-legacy.js" nomodule></script><script defer="defer" src="/js/app-legacy.js" nomodule></script></head><body class="dark:bg-gray-900"><noscript><strong>Enable JavaScript to view this page.</strong></noscript><div id="app"></div></body></html>
|
||||
logo: "{{ .Logo }}"
|
||||
};</script><title>{{ .Title }}</title><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"><script defer="defer" src="/js/chunk-vendors.js" type="module"></script><script defer="defer" src="/js/app.js" type="module"></script><link href="/css/app.css" rel="stylesheet"><script defer="defer" src="/js/chunk-vendors-legacy.js" nomodule></script><script defer="defer" src="/js/app-legacy.js" nomodule></script></head><body class="dark:bg-gray-900"><noscript><strong>Enable JavaScript to view this page.</strong></noscript><div id="app"></div></body></html>
|
||||
@@ -1 +1 @@
|
||||
(function(){"use strict";var e={8584:function(e,t,s){s.d(t,{L:function(){return Pt}});s(6992),s(8674),s(9601),s(7727);var r=s(9963),n=s(6252),o=s.p+"img/logo.png",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={class:"mb-2"},l={class:"flex flex-wrap"},g=(0,n._)("div",{class:"w-3/4 text-left my-auto"},[(0,n._)("div",{class:"text-3xl xl:text-5xl lg:text-4xl font-light"},"Health Status")],-1),u={class:"w-1/4 flex justify-end"},c=["src"],h={key:1,src:o,alt:"Gatus",class:"object-scale-down",style:{"max-width":"100px","min-width":"50px","min-height":"50px"}};function d(e,t,s,r,o,d){var m=(0,n.up)("router-view"),v=(0,n.up)("Tooltip"),p=(0,n.up)("Social");return(0,n.wg)(),(0,n.iD)(n.HY,null,[(0,n._)("div",a,[(0,n._)("div",i,[(0,n._)("div",l,[g,(0,n._)("div",u,[d.getLogo?((0,n.wg)(),(0,n.iD)("img",{key:0,src:d.getLogo,alt:"Gatus",class:"object-scale-down",style:{"max-width":"100px","min-width":"50px","min-height":"50px"}},null,8,c)):(0,n.kq)("",!0),d.getLogo?(0,n.kq)("",!0):((0,n.wg)(),(0,n.iD)("img",h))])])]),(0,n.Wm)(m,{onShowTooltip:d.showTooltip},null,8,["onShowTooltip"])]),(0,n.Wm)(v,{result:o.tooltip.result,event:o.tooltip.event},null,8,["result","event"]),(0,n.Wm)(p)],64)}var m=s.p+"img/github.png";(0,n.dD)("data-v-1cbbc992");var v={id:"social"},p=(0,n._)("a",{href:"https://github.com/TwinProduction/gatus",target:"_blank",title:"Gatus on GitHub"},[(0,n._)("img",{src:m,alt:"GitHub",width:"32",height:"auto"})],-1),f=[p];function A(e,t,s,r,o,a){return(0,n.wg)(),(0,n.iD)("div",v,f)}(0,n.Cn)();var w={name:"Social"};w.render=A,w.__scopeId="data-v-1cbbc992";var y=w,x=(s(6977),s(3577)),T=(0,n._)("div",{class:"tooltip-title"},"Timestamp:",-1),k={id:"tooltip-timestamp"},b=(0,n._)("div",{class:"tooltip-title"},"Response time:",-1),I={id:"tooltip-response-time"},R=(0,n._)("div",{class:"tooltip-title"},"Conditions:",-1),S={id:"tooltip-conditions"},D=(0,n._)("br",null,null,-1),C={key:0,id:"tooltip-errors-container"},B=(0,n._)("div",{class:"tooltip-title"},"Errors:",-1),P={id:"tooltip-errors"},E=(0,n._)("br",null,null,-1);function H(e,t,s,r,o,a){return(0,n.wg)(),(0,n.iD)("div",{id:"tooltip",ref:"tooltip",class:(0,x.C_)(o.hidden?"invisible":""),style:(0,x.j5)("top:"+o.top+"px; left:"+o.left+"px")},[s.result?(0,n.WI)(e.$slots,"default",{key:0},(function(){return[T,(0,n._)("code",k,(0,x.zw)(a.prettifyTimestamp(s.result.timestamp)),1),b,(0,n._)("code",I,(0,x.zw)((s.result.duration/1e6).toFixed(0))+"ms",1),R,(0,n._)("code",S,[((0,n.wg)(!0),(0,n.iD)(n.HY,null,(0,n.Ko)(s.result.conditionResults,(function(t){return(0,n.WI)(e.$slots,"default",{key:t},(function(){return[(0,n.Uk)((0,x.zw)(t.success?"✓":"X")+" ~ "+(0,x.zw)(t.condition),1),D]}))})),128))]),s.result.errors&&s.result.errors.length?((0,n.wg)(),(0,n.iD)("div",C,[B,(0,n._)("code",P,[((0,n.wg)(!0),(0,n.iD)(n.HY,null,(0,n.Ko)(s.result.errors,(function(t){return(0,n.WI)(e.$slots,"default",{key:t},(function(){return[(0,n.Uk)(" - "+(0,x.zw)(t),1),E]}))})),128))])])):(0,n.kq)("",!0)]})):(0,n.kq)("",!0)],6)}s(4916),s(5306);var O={name:"Services",props:{event:Event,result:Object},methods:{prettifyTimestamp:function(e){var t=new Date(e),s=t.getFullYear(),r=(t.getMonth()+1<10?"0":"")+(t.getMonth()+1),n=(t.getDate()<10?"0":"")+t.getDate(),o=(t.getHours()<10?"0":"")+t.getHours(),a=(t.getMinutes()<10?"0":"")+t.getMinutes(),i=(t.getSeconds()<10?"0":"")+t.getSeconds();return s+"-"+r+"-"+n+" "+o+":"+a+":"+i},htmlEntities:function(e){return String(e).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")},reposition:function(){if(this.event&&this.event.type)if("mouseenter"===this.event.type){var 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:function(){this.reposition()},created:function(){this.reposition()},data:function(){return{hidden:!0,top:0,left:0}}};O.render=H;var U=O,z={name:"App",components:{Social:y,Tooltip:U},methods:{showTooltip:function(e,t){this.tooltip={result:e,event:t}}},computed:{getLogo:function(){return window.config&&window.config.logo&&"{{ .Logo }}"!==window.config.logo?window.config.logo:""}},data:function(){return{tooltip:{}}}};z.render=d;var M=z,Q=s(2119);function W(e,t,s,r,o,a){var i=(0,n.up)("Line truncated
|
||||
(function(){"use strict";var e={8584:function(e,t,s){s.d(t,{L:function(){return Ft}});s(6992),s(8674),s(9601),s(7727);var n=s(9963),r=s(6252),o=s.p+"img/logo.png",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={class:"mb-2"},l={class:"flex flex-wrap"},u=(0,r._)("div",{class:"w-3/4 text-left my-auto"},[(0,r._)("div",{class:"text-3xl xl:text-5xl lg:text-4xl font-light"},"Health Status")],-1),c={class:"w-1/4 flex justify-end"},g=["src"],h={key:1,src:o,alt:"Gatus",class:"object-scale-down",style:{"max-width":"100px","min-width":"50px","min-height":"50px"}};function d(e,t,s,n,o,d){var f=(0,r.up)("router-view"),m=(0,r.up)("Tooltip"),v=(0,r.up)("Social");return(0,r.wg)(),(0,r.iD)(r.HY,null,[(0,r._)("div",a,[(0,r._)("div",i,[(0,r._)("div",l,[u,(0,r._)("div",c,[d.getLogo?((0,r.wg)(),(0,r.iD)("img",{key:0,src:d.getLogo,alt:"Gatus",class:"object-scale-down",style:{"max-width":"100px","min-width":"50px","min-height":"50px"}},null,8,g)):(0,r.kq)("",!0),d.getLogo?(0,r.kq)("",!0):((0,r.wg)(),(0,r.iD)("img",h))])])]),(0,r.Wm)(f,{onShowTooltip:d.showTooltip},null,8,["onShowTooltip"])]),(0,r.Wm)(m,{result:o.tooltip.result,event:o.tooltip.event},null,8,["result","event"]),(0,r.Wm)(v)],64)}var f=s.p+"img/github.png",m=function(e){return(0,r.dD)("data-v-1cbbc992"),e=e(),(0,r.Cn)(),e},v={id:"social"},p=m((function(){return(0,r._)("a",{href:"https://github.com/TwinProduction/gatus",target:"_blank",title:"Gatus on GitHub"},[(0,r._)("img",{src:f,alt:"GitHub",width:"32",height:"auto"})],-1)})),A=[p];function w(e,t,s,n,o,a){return(0,r.wg)(),(0,r.iD)("div",v,A)}var y={name:"Social"},x=s(3744);const T=(0,x.Z)(y,[["render",w],["__scopeId","data-v-1cbbc992"]]);var k=T,b=(s(6977),s(3577)),I=(0,r._)("div",{class:"tooltip-title"},"Timestamp:",-1),R={id:"tooltip-timestamp"},S=(0,r._)("div",{class:"tooltip-title"},"Response time:",-1),D={id:"tooltip-response-time"},C=(0,r._)("div",{class:"tooltip-title"},"Conditions:",-1),B={id:"tooltip-conditions"},P=(0,r._)("br",null,null,-1),E={key:0,id:"tooltip-errors-container"},H=(0,r._)("div",{class:"tooltip-title"},"Errors:",-1),O={id:"tooltip-errors"},U=(0,r._)("br",null,null,-1);function z(e,t,s,n,o,a){return(0,r.wg)(),(0,r.iD)("div",{id:"tooltip",ref:"tooltip",class:(0,b.C_)(o.hidden?"invisible":""),style:(0,b.j5)("top:"+o.top+"px; left:"+o.left+"px")},[s.result?(0,r.WI)(e.$slots,"default",{key:0},(function(){return[I,(0,r._)("code",R,(0,b.zw)(a.prettifyTimestamp(s.result.timestamp)),1),S,(0,r._)("code",D,(0,b.zw)((s.result.duration/1e6).toFixed(0))+"ms",1),C,(0,r._)("code",B,[((0,r.wg)(!0),(0,r.iD)(r.HY,null,(0,r.Ko)(s.result.conditionResults,(function(t){return(0,r.WI)(e.$slots,"default",{key:t},(function(){return[(0,r.Uk)((0,b.zw)(t.success?"✓":"X")+" ~ "+(0,b.zw)(t.condition),1),P]}))})),128))]),s.result.errors&&s.result.errors.length?((0,r.wg)(),(0,r.iD)("div",E,[H,(0,r._)("code",O,[((0,r.wg)(!0),(0,r.iD)(r.HY,null,(0,r.Ko)(s.result.errors,(function(t){return(0,r.WI)(e.$slots,"default",{key:t},(function(){return[(0,r.Uk)(" - "+(0,b.zw)(t),1),U]}))})),128))])])):(0,r.kq)("",!0)]})):(0,r.kq)("",!0)],6)}s(4916),s(5306);var M={name:"Services",props:{event:Event,result:Object},methods:{prettifyTimestamp:function(e){var t=new Date(e),s=t.getFullYear(),n=(t.getMonth()+1<10?"0":"")+(t.getMonth()+1),r=(t.getDate()<10?"0":"")+t.getDate(),o=(t.getHours()<10?"0":"")+t.getHours(),a=(t.getMinutes()<10?"0":"")+t.getMinutes(),i=(t.getSeconds()<10?"0":"")+t.getSeconds();return s+"-"+n+"-"+r+" "+o+":"+a+":"+i},htmlEntities:function(e){return String(e).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")},reposition:function(){if(this.event&&this.event.type)if("mouseenter"===this.event.type){var 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:function(){this.reposition()},created:function(){this.reposition()},data:function(){return{hidden:!0,top:0,left:0}}};const Q=(0,x.Z)(M,[["render",z]]);var W=Q,X={name:"App",components:{Social:k,Tooltip:W},methods:{showTooltip:function(e,t){this.tooltip={result:e,event:t}}},computed:{getLogo:function(){return window.config&&window.config.logo&&"{{ .Logo }}"!==window.config.logo?window.config.logo:""}},Line truncated
|
||||
@@ -1 +1 @@
|
||||
(function(){"use strict";var e={584:function(e,t,s){s.d(t,{L:function(){return Pt}});var r=s(963),a=s(252),o=s.p+"img/logo.png";const i={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"},n={class:"mb-2"},l={class:"flex flex-wrap"},g=(0,a._)("div",{class:"w-3/4 text-left my-auto"},[(0,a._)("div",{class:"text-3xl xl:text-5xl lg:text-4xl font-light"},"Health Status")],-1),h={class:"w-1/4 flex justify-end"},c=["src"],u={key:1,src:o,alt:"Gatus",class:"object-scale-down",style:{"max-width":"100px","min-width":"50px","min-height":"50px"}};function d(e,t,s,r,o,d){const m=(0,a.up)("router-view"),p=(0,a.up)("Tooltip"),A=(0,a.up)("Social");return(0,a.wg)(),(0,a.iD)(a.HY,null,[(0,a._)("div",i,[(0,a._)("div",n,[(0,a._)("div",l,[g,(0,a._)("div",h,[d.getLogo?((0,a.wg)(),(0,a.iD)("img",{key:0,src:d.getLogo,alt:"Gatus",class:"object-scale-down",style:{"max-width":"100px","min-width":"50px","min-height":"50px"}},null,8,c)):(0,a.kq)("",!0),d.getLogo?(0,a.kq)("",!0):((0,a.wg)(),(0,a.iD)("img",u))])])]),(0,a.Wm)(m,{onShowTooltip:d.showTooltip},null,8,["onShowTooltip"])]),(0,a.Wm)(p,{result:o.tooltip.result,event:o.tooltip.event},null,8,["result","event"]),(0,a.Wm)(A)],64)}var m=s.p+"img/github.png";(0,a.dD)("data-v-1cbbc992");const p={id:"social"},A=(0,a._)("a",{href:"https://github.com/TwinProduction/gatus",target:"_blank",title:"Gatus on GitHub"},[(0,a._)("img",{src:m,alt:"GitHub",width:"32",height:"auto"})],-1),v=[A];function f(e,t,s,r,o,i){return(0,a.wg)(),(0,a.iD)("div",p,v)}(0,a.Cn)();var w={name:"Social"};w.render=f,w.__scopeId="data-v-1cbbc992";var y=w,x=s(577);const T=(0,a._)("div",{class:"tooltip-title"},"Timestamp:",-1),k={id:"tooltip-timestamp"},b=(0,a._)("div",{class:"tooltip-title"},"Response time:",-1),I={id:"tooltip-response-time"},R=(0,a._)("div",{class:"tooltip-title"},"Conditions:",-1),S={id:"tooltip-conditions"},D=(0,a._)("br",null,null,-1),C={key:0,id:"tooltip-errors-container"},B=(0,a._)("div",{class:"tooltip-title"},"Errors:",-1),P={id:"tooltip-errors"},E=(0,a._)("br",null,null,-1);function H(e,t,s,r,o,i){return(0,a.wg)(),(0,a.iD)("div",{id:"tooltip",ref:"tooltip",class:(0,x.C_)(o.hidden?"invisible":""),style:(0,x.j5)("top:"+o.top+"px; left:"+o.left+"px")},[s.result?(0,a.WI)(e.$slots,"default",{key:0},(()=>[T,(0,a._)("code",k,(0,x.zw)(i.prettifyTimestamp(s.result.timestamp)),1),b,(0,a._)("code",I,(0,x.zw)((s.result.duration/1e6).toFixed(0))+"ms",1),R,(0,a._)("code",S,[((0,a.wg)(!0),(0,a.iD)(a.HY,null,(0,a.Ko)(s.result.conditionResults,(t=>(0,a.WI)(e.$slots,"default",{key:t},(()=>[(0,a.Uk)((0,x.zw)(t.success?"✓":"X")+" ~ "+(0,x.zw)(t.condition),1),D])))),128))]),s.result.errors&&s.result.errors.length?((0,a.wg)(),(0,a.iD)("div",C,[B,(0,a._)("code",P,[((0,a.wg)(!0),(0,a.iD)(a.HY,null,(0,a.Ko)(s.result.errors,(t=>(0,a.WI)(e.$slots,"default",{key:t},(()=>[(0,a.Uk)(" - "+(0,x.zw)(t),1),E])))),128))])])):(0,a.kq)("",!0)])):(0,a.kq)("",!0)],6)}var O={name:"Services",props:{event:Event,result:Object},methods:{prettifyTimestamp(e){let t=new Date(e),s=t.getFullYear(),r=(t.getMonth()+1<10?"0":"")+(t.getMonth()+1),a=(t.getDate()<10?"0":"")+t.getDate(),o=(t.getHours()<10?"0":"")+t.getHours(),i=(t.getMinutes()<10?"0":"")+t.getMinutes(),n=(t.getSeconds()<10?"0":"")+t.getSeconds();return s+"-"+r+"-"+a+" "+o+":"+i+":"+n},htmlEntities(e){return String(e).replace(/&/g,"&").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}}};O.render=H;var U=O,z={name:"App",components:{Social:y,Tooltip:U},methods:{showTooltip(e,t){this.tooltip={result:e,event:t}}},computed:{getLogo(){return window.config&&window.config.logo&&"{{ .Logo }}"!==window.config.logo?window.config.logo:""}},data(){return{tooltip:{}}}};z.render=d;var M=z,Q=s(119);function W(e,t,s,r,o,i){const n=(0,a.up)("Services"),l=(0,a.up)("Pagination"),g=(0,a.up)("Settings");return(0,a.wg)(),(0,a.iD)(a.HY,null,[(0,a.Wm)(n,{serviceStatuses:o.serviceStatuses,showStatusOnHover:!0,onShowTooltip:i.showTooltip,onToggleSLine truncated
|
||||
(function(){"use strict";var e={584:function(e,t,s){s.d(t,{L:function(){return Ft}});var r=s(963),a=s(252),o=s.p+"img/logo.png";const n={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={class:"mb-2"},l={class:"flex flex-wrap"},g=(0,a._)("div",{class:"w-3/4 text-left my-auto"},[(0,a._)("div",{class:"text-3xl xl:text-5xl lg:text-4xl font-light"},"Health Status")],-1),h={class:"w-1/4 flex justify-end"},c=["src"],u={key:1,src:o,alt:"Gatus",class:"object-scale-down",style:{"max-width":"100px","min-width":"50px","min-height":"50px"}};function d(e,t,s,r,o,d){const m=(0,a.up)("router-view"),p=(0,a.up)("Tooltip"),A=(0,a.up)("Social");return(0,a.wg)(),(0,a.iD)(a.HY,null,[(0,a._)("div",n,[(0,a._)("div",i,[(0,a._)("div",l,[g,(0,a._)("div",h,[d.getLogo?((0,a.wg)(),(0,a.iD)("img",{key:0,src:d.getLogo,alt:"Gatus",class:"object-scale-down",style:{"max-width":"100px","min-width":"50px","min-height":"50px"}},null,8,c)):(0,a.kq)("",!0),d.getLogo?(0,a.kq)("",!0):((0,a.wg)(),(0,a.iD)("img",u))])])]),(0,a.Wm)(m,{onShowTooltip:d.showTooltip},null,8,["onShowTooltip"])]),(0,a.Wm)(p,{result:o.tooltip.result,event:o.tooltip.event},null,8,["result","event"]),(0,a.Wm)(A)],64)}var m=s.p+"img/github.png";const p=e=>((0,a.dD)("data-v-1cbbc992"),e=e(),(0,a.Cn)(),e),A={id:"social"},v=p((()=>(0,a._)("a",{href:"https://github.com/TwinProduction/gatus",target:"_blank",title:"Gatus on GitHub"},[(0,a._)("img",{src:m,alt:"GitHub",width:"32",height:"auto"})],-1))),f=[v];function w(e,t,s,r,o,n){return(0,a.wg)(),(0,a.iD)("div",A,f)}var y={name:"Social"},x=s(744);const T=(0,x.Z)(y,[["render",w],["__scopeId","data-v-1cbbc992"]]);var k=T,b=s(577);const I=(0,a._)("div",{class:"tooltip-title"},"Timestamp:",-1),R={id:"tooltip-timestamp"},S=(0,a._)("div",{class:"tooltip-title"},"Response time:",-1),D={id:"tooltip-response-time"},C=(0,a._)("div",{class:"tooltip-title"},"Conditions:",-1),B={id:"tooltip-conditions"},P=(0,a._)("br",null,null,-1),E={key:0,id:"tooltip-errors-container"},H=(0,a._)("div",{class:"tooltip-title"},"Errors:",-1),O={id:"tooltip-errors"},U=(0,a._)("br",null,null,-1);function z(e,t,s,r,o,n){return(0,a.wg)(),(0,a.iD)("div",{id:"tooltip",ref:"tooltip",class:(0,b.C_)(o.hidden?"invisible":""),style:(0,b.j5)("top:"+o.top+"px; left:"+o.left+"px")},[s.result?(0,a.WI)(e.$slots,"default",{key:0},(()=>[I,(0,a._)("code",R,(0,b.zw)(n.prettifyTimestamp(s.result.timestamp)),1),S,(0,a._)("code",D,(0,b.zw)((s.result.duration/1e6).toFixed(0))+"ms",1),C,(0,a._)("code",B,[((0,a.wg)(!0),(0,a.iD)(a.HY,null,(0,a.Ko)(s.result.conditionResults,(t=>(0,a.WI)(e.$slots,"default",{key:t},(()=>[(0,a.Uk)((0,b.zw)(t.success?"✓":"X")+" ~ "+(0,b.zw)(t.condition),1),P])))),128))]),s.result.errors&&s.result.errors.length?((0,a.wg)(),(0,a.iD)("div",E,[H,(0,a._)("code",O,[((0,a.wg)(!0),(0,a.iD)(a.HY,null,(0,a.Ko)(s.result.errors,(t=>(0,a.WI)(e.$slots,"default",{key:t},(()=>[(0,a.Uk)(" - "+(0,b.zw)(t),1),U])))),128))])])):(0,a.kq)("",!0)])):(0,a.kq)("",!0)],6)}var M={name:"Services",props:{event:Event,result:Object},methods:{prettifyTimestamp(e){let t=new Date(e),s=t.getFullYear(),r=(t.getMonth()+1<10?"0":"")+(t.getMonth()+1),a=(t.getDate()<10?"0":"")+t.getDate(),o=(t.getHours()<10?"0":"")+t.getHours(),n=(t.getMinutes()<10?"0":"")+t.getMinutes(),i=(t.getSeconds()<10?"0":"")+t.getSeconds();return s+"-"+r+"-"+a+" "+o+":"+n+":"+i},htmlEntities(e){return String(e).replace(/&/g,"&").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 Q=(0,x.Z)(M,[["render",z]]);var W=Q,X={name:"App",components:{Social:k,Tooltip:W},methods:{showTooltip(e,t){this.tooltip={result:e,event:t}}},computed:{getLogo(){return window.config&&window.config.logo&&"{{ .Logo }}"!==window.config.logo?window.config.logo:""}},data(){return{tooltip:{}}}};const q=(0,x.Z)(X,[["render",d]]);var K=q,Y=s(119);function F(e,t,s,r,o,n){const i=(0,a.up)("Services"),l=(0,a.up)("Pagination"),g=(0,a.up)("Settings");return(0,a.wg)(),(0,a.iD)(a.HY,null,[Line truncated
|
||||
@@ -1,7 +1,7 @@
|
||||
(self["webpackChunkgatus"]=self["webpackChunkgatus"]||[]).push([[998],{2262:function(e,t,n){"use strict";n.d(t,{Bj:function(){return c},qq:function(){return w},Fl:function(){return We},X3:function(){return Ie},PG:function(){return Re},dq:function(){return $e},Xl:function(){return Pe},Jd:function(){return S},WL:function(){return Ve},qj:function(){return Se},iH:function(){return Ue},lk:function(){return O},Um:function(){return Ce},XI:function(){return Le},IU:function(){return Te},j:function(){return A},X$:function(){return I},SU:function(){return Be}});var r=n(3577);let o;const i=[];class c{constructor(e=!1){this.active=!0,this.effects=[],this.cleanups=[],!e&&o&&(this.parent=o,this.index=(o.scopes||(o.scopes=[])).push(this)-1)}run(e){if(this.active)try{return this.on(),e()}finally{this.off()}else 0}on(){this.active&&(i.push(this),o=this)}off(){this.active&&(i.pop(),o=i[i.length-1])}stop(e){if(this.active){if(this.effects.forEach((e=>e.stop())),this.cleanups.forEach((e=>e())),this.scopes&&this.scopes.forEach((e=>e.stop(!0))),this.parent&&!e){const e=this.parent.scopes.pop();e&&e!==this&&(this.parent.scopes[this.index]=e,e.index=this.index)}this.active=!1}}}function s(e,t){t=t||o,t&&t.active&&t.effects.push(e)}const u=e=>{const t=new Set(e);return t.w=0,t.n=0,t},l=e=>(e.w&v)>0,a=e=>(e.n&v)>0,f=({deps:e})=>{if(e.length)for(let t=0;t<e.length;t++)e[t].w|=v},p=e=>{const{deps:t}=e;if(t.length){let n=0;for(let r=0;r<t.length;r++){const o=t[r];l(o)&&!a(o)?o.delete(e):t[n++]=o,o.w&=~v,o.n&=~v}t.length=n}},d=new WeakMap;let h=0,v=1;const g=30,m=[];let y;const b=Symbol(""),x=Symbol("");class w{constructor(e,t=null,n){this.fn=e,this.scheduler=t,this.active=!0,this.deps=[],s(this,n)}run(){if(!this.active)return this.fn();if(!m.includes(this))try{return m.push(y=this),C(),v=1<<++h,h<=g?f(this):_(this),this.fn()}finally{h<=g&&p(this),v=1<<--h,O(),m.pop();const e=m.length;y=e>0?m[e-1]:void 0}}stop(){this.active&&(_(this),this.onStop&&this.onStop(),this.active=!1)}}function _(e){const{deps:t}=e;if(t.length){for(let n=0;n<t.length;n++)t[n].delete(e);t.length=0}}let k=!0;const E=[];function S(){E.push(k),k=!1}function C(){E.push(k),k=!0}function O(){const e=E.pop();k=void 0===e||e}function A(e,t,n){if(!R())return;let r=d.get(e);r||d.set(e,r=new Map);let o=r.get(n);o||r.set(n,o=u());const i=void 0;j(o,i)}function R(){return k&&void 0!==y}function j(e,t){let n=!1;h<=g?a(e)||(e.n|=v,n=!l(e)):n=!e.has(y),n&&(e.add(y),y.deps.push(e))}function I(e,t,n,o,i,c){const s=d.get(e);if(!s)return;let l=[];if("clear"===t)l=[...s.values()];else if("length"===n&&(0,r.kJ)(e))s.forEach(((e,t)=>{("length"===t||t>=o)&&l.push(e)}));else switch(void 0!==n&&l.push(s.get(n)),t){case"add":(0,r.kJ)(e)?(0,r.S0)(n)&&l.push(s.get("length")):(l.push(s.get(b)),(0,r._N)(e)&&l.push(s.get(x)));break;case"delete":(0,r.kJ)(e)||(l.push(s.get(b)),(0,r._N)(e)&&l.push(s.get(x)));break;case"set":(0,r._N)(e)&&l.push(s.get(b));break}if(1===l.length)l[0]&&T(l[0]);else{const e=[];for(const t of l)t&&e.push(...t);T(u(e))}}function T(e,t){for(const n of(0,r.kJ)(e)?e:[...e])(n!==y||n.allowRecurse)&&(n.scheduler?n.scheduler():n.run())}const P=(0,r.fY)("__proto__,__v_isRef,__isVue"),F=new Set(Object.getOwnPropertyNames(Symbol).map((e=>Symbol[e])).filter(r.yk)),N=J(),M=J(!1,!0),$=J(!0),U=L();function L(){const e={};return["includes","indexOf","lastIndexOf"].forEach((t=>{e[t]=function(...e){const n=Te(this);for(let t=0,o=this.length;t<o;t++)A(n,"get",t+"");const r=n[t](...e);return-1===r||!1===r?n[t](...e.map(Te)):r}})),["push","pop","shift","unshift","splice"].forEach((t=>{e[t]=function(...e){S();const n=Te(this)[t].apply(this,e);return O(),n}})),e}function J(e=!1,t=!1){return function(n,o,i){if("__v_isReactive"===o)return!e;if("__v_isReadonly"===o)return e;if("__v_raw"===o&&i===(e?t?_e:we:t?xe:be).get(n))return n;const c=(0,r.kJ)(n);if(!e&&c&&(0,r.RI)(U,o))return Reflect.get(U,o,i);const s=Reflect.get(n,o,i);if((0,r.yk)(o)?F.has(o):P(o))return s;if(e||A(n,"get",o),t)return s;if($e(s)){const e=!c||!(0,r.S0)(o);return e?s.value:s}return(0,r.Kn)(s)?e?Oe(s):Se(s):s}}const D=q(),B=q(!0);function q(e=!1){return function(t,n,o,i){let c=t[n];if(!e&&(o=Te(o),c=Te(c),!(0,r.kJ)(t)&&$e(c)&&!$e(o)))return c.value=o,!0;const s=(0,r.kJ)(t)&&(0,r.S0)(n)?Number(n)<t.length:(0,r.RI)(t,n),u=Reflect.set(t,n,o,i);return t===Te(i)&&(s?(0,r.aU)(o,c)&&I(t,"set",n,o,c):I(t,"add",n,o)),u}}function V(e,t){const n=(0,r.RI)(e,t),o=e[t],i=Reflect.deleteProperty(e,t);return i&&n&&I(e,"delete",t,void 0,o),i}function G(e,t){const n=Reflect.has(e,t);return(0,r.yk)(t)&&F.has(t)||A(e,"has",t),n}function W(e){return A(e,"iterate",(0,r.kJ)(e)?"length":b),Reflect.ownKeys(e)}const H={get:N,set:D,deleteProperty:V,has:G,ownKeys:W},K={get:$,set(e,t){return!0},deleteProperty(e,t){return!0}},z=(0,r.l7)({},H,{get:M,set:B}),Y=e=>(0,r.Kn)(e)?Se(e):e,X=e=>(0,r.Kn)(e)?Oe(e):e,Z=e=>e,Q=e=>Reflect.getPrototypeOf(e);function ee(e,t,n=!1,r=!1){e=e["__v_raw"];const o=Te(e),i=Te(t);t!==i&&!n&&A(o,"get",t),!n&&A(o,"get",i);const{has:Line truncated
|
||||
(self["webpackChunkgatus"]=self["webpackChunkgatus"]||[]).push([[998],{2262:function(e,t,n){"use strict";n.d(t,{Bj:function(){return c},qq:function(){return w},Fl:function(){return Ve},X3:function(){return Re},PG:function(){return Ce},dq:function(){return Me},Xl:function(){return je},Jd:function(){return S},WL:function(){return Ge},qj:function(){return ke},iH:function(){return $e},lk:function(){return C},Um:function(){return Ee},XI:function(){return Ue},IU:function(){return Ie},j:function(){return A},X$:function(){return j},SU:function(){return Je}});var r=n(3577);let o;const i=[];class c{constructor(e=!1){this.active=!0,this.effects=[],this.cleanups=[],!e&&o&&(this.parent=o,this.index=(o.scopes||(o.scopes=[])).push(this)-1)}run(e){if(this.active)try{return this.on(),e()}finally{this.off()}else 0}on(){this.active&&(i.push(this),o=this)}off(){this.active&&(i.pop(),o=i[i.length-1])}stop(e){if(this.active){if(this.effects.forEach((e=>e.stop())),this.cleanups.forEach((e=>e())),this.scopes&&this.scopes.forEach((e=>e.stop(!0))),this.parent&&!e){const e=this.parent.scopes.pop();e&&e!==this&&(this.parent.scopes[this.index]=e,e.index=this.index)}this.active=!1}}}function s(e,t){t=t||o,t&&t.active&&t.effects.push(e)}const u=e=>{const t=new Set(e);return t.w=0,t.n=0,t},l=e=>(e.w&v)>0,a=e=>(e.n&v)>0,f=({deps:e})=>{if(e.length)for(let t=0;t<e.length;t++)e[t].w|=v},p=e=>{const{deps:t}=e;if(t.length){let n=0;for(let r=0;r<t.length;r++){const o=t[r];l(o)&&!a(o)?o.delete(e):t[n++]=o,o.w&=~v,o.n&=~v}t.length=n}},d=new WeakMap;let h=0,v=1;const g=30,m=[];let y;const b=Symbol(""),x=Symbol("");class w{constructor(e,t=null,n){this.fn=e,this.scheduler=t,this.active=!0,this.deps=[],s(this,n)}run(){if(!this.active)return this.fn();if(!m.includes(this))try{return m.push(y=this),O(),v=1<<++h,h<=g?f(this):_(this),this.fn()}finally{h<=g&&p(this),v=1<<--h,C(),m.pop();const e=m.length;y=e>0?m[e-1]:void 0}}stop(){this.active&&(_(this),this.onStop&&this.onStop(),this.active=!1)}}function _(e){const{deps:t}=e;if(t.length){for(let n=0;n<t.length;n++)t[n].delete(e);t.length=0}}let k=!0;const E=[];function S(){E.push(k),k=!1}function O(){E.push(k),k=!0}function C(){const e=E.pop();k=void 0===e||e}function A(e,t,n){if(!R())return;let r=d.get(e);r||d.set(e,r=new Map);let o=r.get(n);o||r.set(n,o=u());const i=void 0;I(o,i)}function R(){return k&&void 0!==y}function I(e,t){let n=!1;h<=g?a(e)||(e.n|=v,n=!l(e)):n=!e.has(y),n&&(e.add(y),y.deps.push(e))}function j(e,t,n,o,i,c){const s=d.get(e);if(!s)return;let l=[];if("clear"===t)l=[...s.values()];else if("length"===n&&(0,r.kJ)(e))s.forEach(((e,t)=>{("length"===t||t>=o)&&l.push(e)}));else switch(void 0!==n&&l.push(s.get(n)),t){case"add":(0,r.kJ)(e)?(0,r.S0)(n)&&l.push(s.get("length")):(l.push(s.get(b)),(0,r._N)(e)&&l.push(s.get(x)));break;case"delete":(0,r.kJ)(e)||(l.push(s.get(b)),(0,r._N)(e)&&l.push(s.get(x)));break;case"set":(0,r._N)(e)&&l.push(s.get(b));break}if(1===l.length)l[0]&&T(l[0]);else{const e=[];for(const t of l)t&&e.push(...t);T(u(e))}}function T(e,t){for(const n of(0,r.kJ)(e)?e:[...e])(n!==y||n.allowRecurse)&&(n.scheduler?n.scheduler():n.run())}const P=(0,r.fY)("__proto__,__v_isRef,__isVue"),F=new Set(Object.getOwnPropertyNames(Symbol).map((e=>Symbol[e])).filter(r.yk)),N=D(),M=D(!1,!0),$=D(!0),U=L();function L(){const e={};return["includes","indexOf","lastIndexOf"].forEach((t=>{e[t]=function(...e){const n=Ie(this);for(let t=0,o=this.length;t<o;t++)A(n,"get",t+"");const r=n[t](...e);return-1===r||!1===r?n[t](...e.map(Ie)):r}})),["push","pop","shift","unshift","splice"].forEach((t=>{e[t]=function(...e){S();const n=Ie(this)[t].apply(this,e);return C(),n}})),e}function D(e=!1,t=!1){return function(n,o,i){if("__v_isReactive"===o)return!e;if("__v_isReadonly"===o)return e;if("__v_raw"===o&&i===(e?t?xe:be:t?ye:me).get(n))return n;const c=(0,r.kJ)(n);if(!e&&c&&(0,r.RI)(U,o))return Reflect.get(U,o,i);const s=Reflect.get(n,o,i);if((0,r.yk)(o)?F.has(o):P(o))return s;if(e||A(n,"get",o),t)return s;if(Me(s)){const e=!c||!(0,r.S0)(o);return e?s.value:s}return(0,r.Kn)(s)?e?Se(s):ke(s):s}}const J=G(),B=G(!0);function G(e=!1){return function(t,n,o,i){let c=t[n];if(!e&&(o=Ie(o),c=Ie(c),!(0,r.kJ)(t)&&Me(c)&&!Me(o)))return c.value=o,!0;const s=(0,r.kJ)(t)&&(0,r.S0)(n)?Number(n)<t.length:(0,r.RI)(t,n),u=Reflect.set(t,n,o,i);return t===Ie(i)&&(s?(0,r.aU)(o,c)&&j(t,"set",n,o,c):j(t,"add",n,o)),u}}function q(e,t){const n=(0,r.RI)(e,t),o=e[t],i=Reflect.deleteProperty(e,t);return i&&n&&j(e,"delete",t,void 0,o),i}function V(e,t){const n=Reflect.has(e,t);return(0,r.yk)(t)&&F.has(t)||A(e,"has",t),n}function W(e){return A(e,"iterate",(0,r.kJ)(e)?"length":b),Reflect.ownKeys(e)}const H={get:N,set:J,deleteProperty:q,has:V,ownKeys:W},K={get:$,set(e,t){return!0},deleteProperty(e,t){return!0}},z=(0,r.l7)({},H,{get:M,set:B}),X=e=>e,Y=e=>Reflect.getPrototypeOf(e);function Z(e,t,n=!1,r=!1){e=e["__v_raw"];const o=Ie(e),i=Ie(t);t!==i&&!n&&A(o,"get",t),!n&&A(o,"get",i);const{has:c}=Y(o),s=r?X:n?Pe:Te;return c.call(o,t)?s(e.get(t)Line truncated
|
||||
/*!
|
||||
* vue-router v4.0.11
|
||||
* (c) 2021 Eduardo San Martin Morote
|
||||
* @license MIT
|
||||
*/
|
||||
const i="function"===typeof Symbol&&"symbol"===typeof Symbol.toStringTag,c=e=>i?Symbol(e):"_vr_"+e,s=c("rvlm"),u=c("rvd"),l=c("r"),a=c("rl"),f=c("rvl"),p="undefined"!==typeof window;function d(e){return e.__esModule||i&&"Module"===e[Symbol.toStringTag]}const h=Object.assign;function v(e,t){const n={};for(const r in t){const o=t[r];n[r]=Array.isArray(o)?o.map(e):e(o)}return n}const g=()=>{};const m=/\/$/,y=e=>e.replace(m,"");function b(e,t,n="/"){let r,o={},i="",c="";const s=t.indexOf("?"),u=t.indexOf("#",s>-1?s:0);return s>-1&&(r=t.slice(0,s),i=t.slice(s+1,u>-1?u:t.length),o=e(i)),u>-1&&(r=r||t.slice(0,u),c=t.slice(u,t.length)),r=O(null!=r?r:t,n),{fullPath:r+(i&&"?")+i+c,path:r,query:o,hash:c}}function x(e,t){const n=t.query?e(t.query):"";return t.path+(n&&"?")+n+(t.hash||"")}function w(e,t){return t&&e.toLowerCase().startsWith(t.toLowerCase())?e.slice(t.length)||"/":e}function _(e,t,n){const r=t.matched.length-1,o=n.matched.length-1;return r>-1&&r===o&&k(t.matched[r],n.matched[o])&&E(t.params,n.params)&&e(t.query)===e(n.query)&&t.hash===n.hash}function k(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function E(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(const n in e)if(!S(e[n],t[n]))return!1;return!0}function S(e,t){return Array.isArray(e)?C(e,t):Array.isArray(t)?C(t,e):e===t}function C(e,t){return Array.isArray(t)?e.length===t.length&&e.every(((e,n)=>e===t[n])):1===e.length&&e[0]===t}function O(e,t){if(e.startsWith("/"))return e;if(!e)return t;const n=t.split("/"),r=e.split("/");let o,i,c=n.length-1;for(o=0;o<r.length;o++)if(i=r[o],1!==c&&"."!==i){if(".."!==i)break;c--}return n.slice(0,c).join("/")+"/"+r.slice(o-(o===r.length?1:0)).join("/")}var A,R;(function(e){e["pop"]="pop",e["push"]="push"})(A||(A={})),function(e){e["back"]="back",e["forward"]="forward",e["unknown"]=""}(R||(R={}));function j(e){if(!e)if(p){const t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return"/"!==e[0]&&"#"!==e[0]&&(e="/"+e),y(e)}const I=/^[^#]+#/;function T(e,t){return e.replace(I,"#")+t}function P(e,t){const n=document.documentElement.getBoundingClientRect(),r=e.getBoundingClientRect();return{behavior:t.behavior,left:r.left-n.left-(t.left||0),top:r.top-n.top-(t.top||0)}}const F=()=>({left:window.pageXOffset,top:window.pageYOffset});function N(e){let t;if("el"in e){const n=e.el,r="string"===typeof n&&n.startsWith("#");0;const o="string"===typeof n?r?document.getElementById(n.slice(1)):document.querySelector(n):n;if(!o)return;t=P(o,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(null!=t.left?t.left:window.pageXOffset,null!=t.top?t.top:window.pageYOffset)}function M(e,t){const n=history.state?history.state.position-t:-1;return n+e}const $=new Map;function U(e,t){$.set(e,t)}function L(e){const t=$.get(e);return $.delete(e),t}let J=()=>location.protocol+"//"+location.host;function D(e,t){const{pathname:n,search:r,hash:o}=t,i=e.indexOf("#");if(i>-1){let t=o.includes(e.slice(i))?e.slice(i).length:1,n=o.slice(t);return"/"!==n[0]&&(n="/"+n),w(n,"")}const c=w(n,e);return c+r+o}function B(e,t,n,r){let o=[],i=[],c=null;const s=({state:i})=>{const s=D(e,location),u=n.value,l=t.value;let a=0;if(i){if(n.value=s,t.value=i,c&&c===u)return void(c=null);a=l?i.position-l.position:0}else r(s);o.forEach((e=>{e(n.value,u,{delta:a,type:A.pop,direction:a?a>0?R.forward:R.back:R.unknown})}))};function u(){c=n.value}function l(e){o.push(e);const t=()=>{const t=o.indexOf(e);t>-1&&o.splice(t,1)};return i.push(t),t}function a(){const{history:e}=window;e.state&&e.replaceState(h({},e.state,{scroll:F()}),"")}function f(){for(const e of i)e();i=[],window.removeEventListener("popstate",s),window.removeEventListener("beforeunload",a)}return window.addEventListener("popstate",s),window.addEventListener("beforeunload",a),{pauseListeners:u,listen:l,destroy:f}}function q(e,t,n,r=!1,o=!1){return{back:e,current:t,forward:n,replaced:r,position:window.history.length,scroll:o?F():null}}function V(e){const{history:t,location:n}=window,r={value:D(e,n)},o={value:t.state};function i(r,i,c){const s=e.indexOf("#"),u=s>-1?(n.host&&document.querySelector("base")?e:e.slice(s))+r:J()+e+r;try{t[c?"replaceState":"pushState"](i,"",u),o.value=i}catch(l){console.error(l),n[c?"replace":"assign"](u)}}function c(e,n){const c=h({},t.state,q(o.value.back,e,o.value.forward,!0),n,{position:o.value.position});i(e,c,!0),r.value=e}function s(e,n){const c=h({},o.value,t.state,{forward:e,scroll:F()});i(c.current,c,!0);const s=h({},q(r.value,e,null),{position:c.position+1},n);i(e,s,!1),r.value=e}return o.value||i(r.value,{back:null,current:r.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0),{location:r,state:o,push:s,replace:c}}function G(e){e=j(e);const t=V(e),n=B(e,t.state,t.location,t.replace);function r(e,t=!0){t||n.pauseListeners(),history.go(e)}const o=h({location:"",base:e,go:r,createHref:T.bind(null,e)},t,n);return Object.defineProperty(o,"loLine truncated
|
||||
const i="function"===typeof Symbol&&"symbol"===typeof Symbol.toStringTag,c=e=>i?Symbol(e):"_vr_"+e,s=c("rvlm"),u=c("rvd"),l=c("r"),a=c("rl"),f=c("rvl"),p="undefined"!==typeof window;function d(e){return e.__esModule||i&&"Module"===e[Symbol.toStringTag]}const h=Object.assign;function v(e,t){const n={};for(const r in t){const o=t[r];n[r]=Array.isArray(o)?o.map(e):e(o)}return n}const g=()=>{};const m=/\/$/,y=e=>e.replace(m,"");function b(e,t,n="/"){let r,o={},i="",c="";const s=t.indexOf("?"),u=t.indexOf("#",s>-1?s:0);return s>-1&&(r=t.slice(0,s),i=t.slice(s+1,u>-1?u:t.length),o=e(i)),u>-1&&(r=r||t.slice(0,u),c=t.slice(u,t.length)),r=C(null!=r?r:t,n),{fullPath:r+(i&&"?")+i+c,path:r,query:o,hash:c}}function x(e,t){const n=t.query?e(t.query):"";return t.path+(n&&"?")+n+(t.hash||"")}function w(e,t){return t&&e.toLowerCase().startsWith(t.toLowerCase())?e.slice(t.length)||"/":e}function _(e,t,n){const r=t.matched.length-1,o=n.matched.length-1;return r>-1&&r===o&&k(t.matched[r],n.matched[o])&&E(t.params,n.params)&&e(t.query)===e(n.query)&&t.hash===n.hash}function k(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function E(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(const n in e)if(!S(e[n],t[n]))return!1;return!0}function S(e,t){return Array.isArray(e)?O(e,t):Array.isArray(t)?O(t,e):e===t}function O(e,t){return Array.isArray(t)?e.length===t.length&&e.every(((e,n)=>e===t[n])):1===e.length&&e[0]===t}function C(e,t){if(e.startsWith("/"))return e;if(!e)return t;const n=t.split("/"),r=e.split("/");let o,i,c=n.length-1;for(o=0;o<r.length;o++)if(i=r[o],1!==c&&"."!==i){if(".."!==i)break;c--}return n.slice(0,c).join("/")+"/"+r.slice(o-(o===r.length?1:0)).join("/")}var A,R;(function(e){e["pop"]="pop",e["push"]="push"})(A||(A={})),function(e){e["back"]="back",e["forward"]="forward",e["unknown"]=""}(R||(R={}));function I(e){if(!e)if(p){const t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return"/"!==e[0]&&"#"!==e[0]&&(e="/"+e),y(e)}const j=/^[^#]+#/;function T(e,t){return e.replace(j,"#")+t}function P(e,t){const n=document.documentElement.getBoundingClientRect(),r=e.getBoundingClientRect();return{behavior:t.behavior,left:r.left-n.left-(t.left||0),top:r.top-n.top-(t.top||0)}}const F=()=>({left:window.pageXOffset,top:window.pageYOffset});function N(e){let t;if("el"in e){const n=e.el,r="string"===typeof n&&n.startsWith("#");0;const o="string"===typeof n?r?document.getElementById(n.slice(1)):document.querySelector(n):n;if(!o)return;t=P(o,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(null!=t.left?t.left:window.pageXOffset,null!=t.top?t.top:window.pageYOffset)}function M(e,t){const n=history.state?history.state.position-t:-1;return n+e}const $=new Map;function U(e,t){$.set(e,t)}function L(e){const t=$.get(e);return $.delete(e),t}let D=()=>location.protocol+"//"+location.host;function J(e,t){const{pathname:n,search:r,hash:o}=t,i=e.indexOf("#");if(i>-1){let t=o.includes(e.slice(i))?e.slice(i).length:1,n=o.slice(t);return"/"!==n[0]&&(n="/"+n),w(n,"")}const c=w(n,e);return c+r+o}function B(e,t,n,r){let o=[],i=[],c=null;const s=({state:i})=>{const s=J(e,location),u=n.value,l=t.value;let a=0;if(i){if(n.value=s,t.value=i,c&&c===u)return void(c=null);a=l?i.position-l.position:0}else r(s);o.forEach((e=>{e(n.value,u,{delta:a,type:A.pop,direction:a?a>0?R.forward:R.back:R.unknown})}))};function u(){c=n.value}function l(e){o.push(e);const t=()=>{const t=o.indexOf(e);t>-1&&o.splice(t,1)};return i.push(t),t}function a(){const{history:e}=window;e.state&&e.replaceState(h({},e.state,{scroll:F()}),"")}function f(){for(const e of i)e();i=[],window.removeEventListener("popstate",s),window.removeEventListener("beforeunload",a)}return window.addEventListener("popstate",s),window.addEventListener("beforeunload",a),{pauseListeners:u,listen:l,destroy:f}}function G(e,t,n,r=!1,o=!1){return{back:e,current:t,forward:n,replaced:r,position:window.history.length,scroll:o?F():null}}function q(e){const{history:t,location:n}=window,r={value:J(e,n)},o={value:t.state};function i(r,i,c){const s=e.indexOf("#"),u=s>-1?(n.host&&document.querySelector("base")?e:e.slice(s))+r:D()+e+r;try{t[c?"replaceState":"pushState"](i,"",u),o.value=i}catch(l){console.error(l),n[c?"replace":"assign"](u)}}function c(e,n){const c=h({},t.state,G(o.value.back,e,o.value.forward,!0),n,{position:o.value.position});i(e,c,!0),r.value=e}function s(e,n){const c=h({},o.value,t.state,{forward:e,scroll:F()});i(c.current,c,!0);const s=h({},G(r.value,e,null),{position:c.position+1},n);i(e,s,!1),r.value=e}return o.value||i(r.value,{back:null,current:r.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0),{location:r,state:o,push:s,replace:c}}function V(e){e=I(e);const t=q(e),n=B(e,t.state,t.location,t.replace);function r(e,t=!0){t||n.pauseListeners(),history.go(e)}const o=h({location:"",base:e,go:r,createHref:T.bind(null,e)},t,n);return Object.defineProperty(o,"loLine truncated
|
||||
@@ -1,7 +1,7 @@
|
||||
(self["webpackChunkgatus"]=self["webpackChunkgatus"]||[]).push([[998],{262:function(e,t,n){"use strict";n.d(t,{Bj:function(){return i},qq:function(){return w},Fl:function(){return We},X3:function(){return Ie},PG:function(){return Oe},dq:function(){return Le},Xl:function(){return Te},Jd:function(){return S},WL:function(){return Ve},qj:function(){return Se},iH:function(){return Ue},lk:function(){return E},Um:function(){return Re},XI:function(){return Ne},IU:function(){return Pe},j:function(){return A},X$:function(){return I},SU:function(){return De}});var r=n(577);let o;const s=[];class i{constructor(e=!1){this.active=!0,this.effects=[],this.cleanups=[],!e&&o&&(this.parent=o,this.index=(o.scopes||(o.scopes=[])).push(this)-1)}run(e){if(this.active)try{return this.on(),e()}finally{this.off()}else 0}on(){this.active&&(s.push(this),o=this)}off(){this.active&&(s.pop(),o=s[s.length-1])}stop(e){if(this.active){if(this.effects.forEach((e=>e.stop())),this.cleanups.forEach((e=>e())),this.scopes&&this.scopes.forEach((e=>e.stop(!0))),this.parent&&!e){const e=this.parent.scopes.pop();e&&e!==this&&(this.parent.scopes[this.index]=e,e.index=this.index)}this.active=!1}}}function l(e,t){t=t||o,t&&t.active&&t.effects.push(e)}const c=e=>{const t=new Set(e);return t.w=0,t.n=0,t},u=e=>(e.w&m)>0,a=e=>(e.n&m)>0,f=({deps:e})=>{if(e.length)for(let t=0;t<e.length;t++)e[t].w|=m},p=e=>{const{deps:t}=e;if(t.length){let n=0;for(let r=0;r<t.length;r++){const o=t[r];u(o)&&!a(o)?o.delete(e):t[n++]=o,o.w&=~m,o.n&=~m}t.length=n}},d=new WeakMap;let h=0,m=1;const g=30,v=[];let y;const b=Symbol(""),_=Symbol("");class w{constructor(e,t=null,n){this.fn=e,this.scheduler=t,this.active=!0,this.deps=[],l(this,n)}run(){if(!this.active)return this.fn();if(!v.includes(this))try{return v.push(y=this),R(),m=1<<++h,h<=g?f(this):k(this),this.fn()}finally{h<=g&&p(this),m=1<<--h,E(),v.pop();const e=v.length;y=e>0?v[e-1]:void 0}}stop(){this.active&&(k(this),this.onStop&&this.onStop(),this.active=!1)}}function k(e){const{deps:t}=e;if(t.length){for(let n=0;n<t.length;n++)t[n].delete(e);t.length=0}}let C=!0;const x=[];function S(){x.push(C),C=!1}function R(){x.push(C),C=!0}function E(){const e=x.pop();C=void 0===e||e}function A(e,t,n){if(!O())return;let r=d.get(e);r||d.set(e,r=new Map);let o=r.get(n);o||r.set(n,o=c());const s=void 0;F(o,s)}function O(){return C&&void 0!==y}function F(e,t){let n=!1;h<=g?a(e)||(e.n|=m,n=!u(e)):n=!e.has(y),n&&(e.add(y),y.deps.push(e))}function I(e,t,n,o,s,i){const l=d.get(e);if(!l)return;let u=[];if("clear"===t)u=[...l.values()];else if("length"===n&&(0,r.kJ)(e))l.forEach(((e,t)=>{("length"===t||t>=o)&&u.push(e)}));else switch(void 0!==n&&u.push(l.get(n)),t){case"add":(0,r.kJ)(e)?(0,r.S0)(n)&&u.push(l.get("length")):(u.push(l.get(b)),(0,r._N)(e)&&u.push(l.get(_)));break;case"delete":(0,r.kJ)(e)||(u.push(l.get(b)),(0,r._N)(e)&&u.push(l.get(_)));break;case"set":(0,r._N)(e)&&u.push(l.get(b));break}if(1===u.length)u[0]&&P(u[0]);else{const e=[];for(const t of u)t&&e.push(...t);P(c(e))}}function P(e,t){for(const n of(0,r.kJ)(e)?e:[...e])(n!==y||n.allowRecurse)&&(n.scheduler?n.scheduler():n.run())}const T=(0,r.fY)("__proto__,__v_isRef,__isVue"),j=new Set(Object.getOwnPropertyNames(Symbol).map((e=>Symbol[e])).filter(r.yk)),$=J(),M=J(!1,!0),L=J(!0),U=N();function N(){const e={};return["includes","indexOf","lastIndexOf"].forEach((t=>{e[t]=function(...e){const n=Pe(this);for(let t=0,o=this.length;t<o;t++)A(n,"get",t+"");const r=n[t](...e);return-1===r||!1===r?n[t](...e.map(Pe)):r}})),["push","pop","shift","unshift","splice"].forEach((t=>{e[t]=function(...e){S();const n=Pe(this)[t].apply(this,e);return E(),n}})),e}function J(e=!1,t=!1){return function(n,o,s){if("__v_isReactive"===o)return!e;if("__v_isReadonly"===o)return e;if("__v_raw"===o&&s===(e?t?ke:we:t?_e:be).get(n))return n;const i=(0,r.kJ)(n);if(!e&&i&&(0,r.RI)(U,o))return Reflect.get(U,o,s);const l=Reflect.get(n,o,s);if((0,r.yk)(o)?j.has(o):T(o))return l;if(e||A(n,"get",o),t)return l;if(Le(l)){const e=!i||!(0,r.S0)(o);return e?l.value:l}return(0,r.Kn)(l)?e?Ee(l):Se(l):l}}const B=q(),D=q(!0);function q(e=!1){return function(t,n,o,s){let i=t[n];if(!e&&(o=Pe(o),i=Pe(i),!(0,r.kJ)(t)&&Le(i)&&!Le(o)))return i.value=o,!0;const l=(0,r.kJ)(t)&&(0,r.S0)(n)?Number(n)<t.length:(0,r.RI)(t,n),c=Reflect.set(t,n,o,s);return t===Pe(s)&&(l?(0,r.aU)(o,i)&&I(t,"set",n,o,i):I(t,"add",n,o)),c}}function V(e,t){const n=(0,r.RI)(e,t),o=e[t],s=Reflect.deleteProperty(e,t);return s&&n&&I(e,"delete",t,void 0,o),s}function H(e,t){const n=Reflect.has(e,t);return(0,r.yk)(t)&&j.has(t)||A(e,"has",t),n}function W(e){return A(e,"iterate",(0,r.kJ)(e)?"length":b),Reflect.ownKeys(e)}const G={get:$,set:B,deleteProperty:V,has:H,ownKeys:W},K={get:L,set(e,t){return!0},deleteProperty(e,t){return!0}},z=(0,r.l7)({},G,{get:M,set:D}),X=e=>(0,r.Kn)(e)?Se(e):e,Z=e=>(0,r.Kn)(e)?Ee(e):e,Y=e=>e,Q=e=>Reflect.getPrototypeOf(e);function ee(e,t,n=!1,r=!1){e=e["__v_raw"];const o=Pe(e),s=Pe(t);t!==s&&!n&&A(o,"get",t),!n&&A(o,"get",s);const{has:i}Line truncated
|
||||
"use strict";(self["webpackChunkgatus"]=self["webpackChunkgatus"]||[]).push([[998],{262:function(e,t,n){n.d(t,{Bj:function(){return i},qq:function(){return w},Fl:function(){return He},X3:function(){return Oe},PG:function(){return Ee},dq:function(){return Me},Xl:function(){return Fe},Jd:function(){return R},WL:function(){return qe},qj:function(){return Ce},iH:function(){return Ue},lk:function(){return E},Um:function(){return xe},XI:function(){return Le},IU:function(){return Ie},j:function(){return A},X$:function(){return F},SU:function(){return Be}});var r=n(577);let o;const s=[];class i{constructor(e=!1){this.active=!0,this.effects=[],this.cleanups=[],!e&&o&&(this.parent=o,this.index=(o.scopes||(o.scopes=[])).push(this)-1)}run(e){if(this.active)try{return this.on(),e()}finally{this.off()}else 0}on(){this.active&&(s.push(this),o=this)}off(){this.active&&(s.pop(),o=s[s.length-1])}stop(e){if(this.active){if(this.effects.forEach((e=>e.stop())),this.cleanups.forEach((e=>e())),this.scopes&&this.scopes.forEach((e=>e.stop(!0))),this.parent&&!e){const e=this.parent.scopes.pop();e&&e!==this&&(this.parent.scopes[this.index]=e,e.index=this.index)}this.active=!1}}}function l(e,t){t=t||o,t&&t.active&&t.effects.push(e)}const c=e=>{const t=new Set(e);return t.w=0,t.n=0,t},u=e=>(e.w&m)>0,a=e=>(e.n&m)>0,f=({deps:e})=>{if(e.length)for(let t=0;t<e.length;t++)e[t].w|=m},p=e=>{const{deps:t}=e;if(t.length){let n=0;for(let r=0;r<t.length;r++){const o=t[r];u(o)&&!a(o)?o.delete(e):t[n++]=o,o.w&=~m,o.n&=~m}t.length=n}},d=new WeakMap;let h=0,m=1;const g=30,v=[];let y;const b=Symbol(""),_=Symbol("");class w{constructor(e,t=null,n){this.fn=e,this.scheduler=t,this.active=!0,this.deps=[],l(this,n)}run(){if(!this.active)return this.fn();if(!v.includes(this))try{return v.push(y=this),S(),m=1<<++h,h<=g?f(this):k(this),this.fn()}finally{h<=g&&p(this),m=1<<--h,E(),v.pop();const e=v.length;y=e>0?v[e-1]:void 0}}stop(){this.active&&(k(this),this.onStop&&this.onStop(),this.active=!1)}}function k(e){const{deps:t}=e;if(t.length){for(let n=0;n<t.length;n++)t[n].delete(e);t.length=0}}let C=!0;const x=[];function R(){x.push(C),C=!1}function S(){x.push(C),C=!0}function E(){const e=x.pop();C=void 0===e||e}function A(e,t,n){if(!O())return;let r=d.get(e);r||d.set(e,r=new Map);let o=r.get(n);o||r.set(n,o=c());const s=void 0;I(o,s)}function O(){return C&&void 0!==y}function I(e,t){let n=!1;h<=g?a(e)||(e.n|=m,n=!u(e)):n=!e.has(y),n&&(e.add(y),y.deps.push(e))}function F(e,t,n,o,s,i){const l=d.get(e);if(!l)return;let u=[];if("clear"===t)u=[...l.values()];else if("length"===n&&(0,r.kJ)(e))l.forEach(((e,t)=>{("length"===t||t>=o)&&u.push(e)}));else switch(void 0!==n&&u.push(l.get(n)),t){case"add":(0,r.kJ)(e)?(0,r.S0)(n)&&u.push(l.get("length")):(u.push(l.get(b)),(0,r._N)(e)&&u.push(l.get(_)));break;case"delete":(0,r.kJ)(e)||(u.push(l.get(b)),(0,r._N)(e)&&u.push(l.get(_)));break;case"set":(0,r._N)(e)&&u.push(l.get(b));break}if(1===u.length)u[0]&&T(u[0]);else{const e=[];for(const t of u)t&&e.push(...t);T(c(e))}}function T(e,t){for(const n of(0,r.kJ)(e)?e:[...e])(n!==y||n.allowRecurse)&&(n.scheduler?n.scheduler():n.run())}const P=(0,r.fY)("__proto__,__v_isRef,__isVue"),j=new Set(Object.getOwnPropertyNames(Symbol).map((e=>Symbol[e])).filter(r.yk)),$=J(),M=J(!1,!0),U=J(!0),L=N();function N(){const e={};return["includes","indexOf","lastIndexOf"].forEach((t=>{e[t]=function(...e){const n=Ie(this);for(let t=0,o=this.length;t<o;t++)A(n,"get",t+"");const r=n[t](...e);return-1===r||!1===r?n[t](...e.map(Ie)):r}})),["push","pop","shift","unshift","splice"].forEach((t=>{e[t]=function(...e){R();const n=Ie(this)[t].apply(this,e);return E(),n}})),e}function J(e=!1,t=!1){return function(n,o,s){if("__v_isReactive"===o)return!e;if("__v_isReadonly"===o)return e;if("__v_raw"===o&&s===(e?t?_e:be:t?ye:ve).get(n))return n;const i=(0,r.kJ)(n);if(!e&&i&&(0,r.RI)(L,o))return Reflect.get(L,o,s);const l=Reflect.get(n,o,s);if((0,r.yk)(o)?j.has(o):P(o))return l;if(e||A(n,"get",o),t)return l;if(Me(l)){const e=!i||!(0,r.S0)(o);return e?l.value:l}return(0,r.Kn)(l)?e?Re(l):Ce(l):l}}const B=q(),D=q(!0);function q(e=!1){return function(t,n,o,s){let i=t[n];if(!e&&(o=Ie(o),i=Ie(i),!(0,r.kJ)(t)&&Me(i)&&!Me(o)))return i.value=o,!0;const l=(0,r.kJ)(t)&&(0,r.S0)(n)?Number(n)<t.length:(0,r.RI)(t,n),c=Reflect.set(t,n,o,s);return t===Ie(s)&&(l?(0,r.aU)(o,i)&&F(t,"set",n,o,i):F(t,"add",n,o)),c}}function V(e,t){const n=(0,r.RI)(e,t),o=e[t],s=Reflect.deleteProperty(e,t);return s&&n&&F(e,"delete",t,void 0,o),s}function H(e,t){const n=Reflect.has(e,t);return(0,r.yk)(t)&&j.has(t)||A(e,"has",t),n}function G(e){return A(e,"iterate",(0,r.kJ)(e)?"length":b),Reflect.ownKeys(e)}const W={get:$,set:B,deleteProperty:V,has:H,ownKeys:G},K={get:U,set(e,t){return!0},deleteProperty(e,t){return!0}},z=(0,r.l7)({},W,{get:M,set:D}),X=e=>e,Z=e=>Reflect.getPrototypeOf(e);function Y(e,t,n=!1,r=!1){e=e["__v_raw"];const o=Ie(e),s=Ie(t);t!==s&&!n&&A(o,"get",t),!n&&A(o,"get",s);const{has:i}=Z(o),l=r?X:n?Pe:Te;return i.call(o,t)?l(e.get(t)):Line truncated
|
||||
/*!
|
||||
* vue-router v4.0.11
|
||||
* (c) 2021 Eduardo San Martin Morote
|
||||
* @license MIT
|
||||
*/
|
||||
const s="function"===typeof Symbol&&"symbol"===typeof Symbol.toStringTag,i=e=>s?Symbol(e):"_vr_"+e,l=i("rvlm"),c=i("rvd"),u=i("r"),a=i("rl"),f=i("rvl"),p="undefined"!==typeof window;function d(e){return e.__esModule||s&&"Module"===e[Symbol.toStringTag]}const h=Object.assign;function m(e,t){const n={};for(const r in t){const o=t[r];n[r]=Array.isArray(o)?o.map(e):e(o)}return n}const g=()=>{};const v=/\/$/,y=e=>e.replace(v,"");function b(e,t,n="/"){let r,o={},s="",i="";const l=t.indexOf("?"),c=t.indexOf("#",l>-1?l:0);return l>-1&&(r=t.slice(0,l),s=t.slice(l+1,c>-1?c:t.length),o=e(s)),c>-1&&(r=r||t.slice(0,c),i=t.slice(c,t.length)),r=E(null!=r?r:t,n),{fullPath:r+(s&&"?")+s+i,path:r,query:o,hash:i}}function _(e,t){const n=t.query?e(t.query):"";return t.path+(n&&"?")+n+(t.hash||"")}function w(e,t){return t&&e.toLowerCase().startsWith(t.toLowerCase())?e.slice(t.length)||"/":e}function k(e,t,n){const r=t.matched.length-1,o=n.matched.length-1;return r>-1&&r===o&&C(t.matched[r],n.matched[o])&&x(t.params,n.params)&&e(t.query)===e(n.query)&&t.hash===n.hash}function C(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function x(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(const n in e)if(!S(e[n],t[n]))return!1;return!0}function S(e,t){return Array.isArray(e)?R(e,t):Array.isArray(t)?R(t,e):e===t}function R(e,t){return Array.isArray(t)?e.length===t.length&&e.every(((e,n)=>e===t[n])):1===e.length&&e[0]===t}function E(e,t){if(e.startsWith("/"))return e;if(!e)return t;const n=t.split("/"),r=e.split("/");let o,s,i=n.length-1;for(o=0;o<r.length;o++)if(s=r[o],1!==i&&"."!==s){if(".."!==s)break;i--}return n.slice(0,i).join("/")+"/"+r.slice(o-(o===r.length?1:0)).join("/")}var A,O;(function(e){e["pop"]="pop",e["push"]="push"})(A||(A={})),function(e){e["back"]="back",e["forward"]="forward",e["unknown"]=""}(O||(O={}));function F(e){if(!e)if(p){const t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return"/"!==e[0]&&"#"!==e[0]&&(e="/"+e),y(e)}const I=/^[^#]+#/;function P(e,t){return e.replace(I,"#")+t}function T(e,t){const n=document.documentElement.getBoundingClientRect(),r=e.getBoundingClientRect();return{behavior:t.behavior,left:r.left-n.left-(t.left||0),top:r.top-n.top-(t.top||0)}}const j=()=>({left:window.pageXOffset,top:window.pageYOffset});function $(e){let t;if("el"in e){const n=e.el,r="string"===typeof n&&n.startsWith("#");0;const o="string"===typeof n?r?document.getElementById(n.slice(1)):document.querySelector(n):n;if(!o)return;t=T(o,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(null!=t.left?t.left:window.pageXOffset,null!=t.top?t.top:window.pageYOffset)}function M(e,t){const n=history.state?history.state.position-t:-1;return n+e}const L=new Map;function U(e,t){L.set(e,t)}function N(e){const t=L.get(e);return L.delete(e),t}let J=()=>location.protocol+"//"+location.host;function B(e,t){const{pathname:n,search:r,hash:o}=t,s=e.indexOf("#");if(s>-1){let t=o.includes(e.slice(s))?e.slice(s).length:1,n=o.slice(t);return"/"!==n[0]&&(n="/"+n),w(n,"")}const i=w(n,e);return i+r+o}function D(e,t,n,r){let o=[],s=[],i=null;const l=({state:s})=>{const l=B(e,location),c=n.value,u=t.value;let a=0;if(s){if(n.value=l,t.value=s,i&&i===c)return void(i=null);a=u?s.position-u.position:0}else r(l);o.forEach((e=>{e(n.value,c,{delta:a,type:A.pop,direction:a?a>0?O.forward:O.back:O.unknown})}))};function c(){i=n.value}function u(e){o.push(e);const t=()=>{const t=o.indexOf(e);t>-1&&o.splice(t,1)};return s.push(t),t}function a(){const{history:e}=window;e.state&&e.replaceState(h({},e.state,{scroll:j()}),"")}function f(){for(const e of s)e();s=[],window.removeEventListener("popstate",l),window.removeEventListener("beforeunload",a)}return window.addEventListener("popstate",l),window.addEventListener("beforeunload",a),{pauseListeners:c,listen:u,destroy:f}}function q(e,t,n,r=!1,o=!1){return{back:e,current:t,forward:n,replaced:r,position:window.history.length,scroll:o?j():null}}function V(e){const{history:t,location:n}=window,r={value:B(e,n)},o={value:t.state};function s(r,s,i){const l=e.indexOf("#"),c=l>-1?(n.host&&document.querySelector("base")?e:e.slice(l))+r:J()+e+r;try{t[i?"replaceState":"pushState"](s,"",c),o.value=s}catch(u){console.error(u),n[i?"replace":"assign"](c)}}function i(e,n){const i=h({},t.state,q(o.value.back,e,o.value.forward,!0),n,{position:o.value.position});s(e,i,!0),r.value=e}function l(e,n){const i=h({},o.value,t.state,{forward:e,scroll:j()});s(i.current,i,!0);const l=h({},q(r.value,e,null),{position:i.position+1},n);s(e,l,!1),r.value=e}return o.value||s(r.value,{back:null,current:r.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0),{location:r,state:o,push:l,replace:i}}function H(e){e=F(e);const t=V(e),n=D(e,t.state,t.location,t.replace);function r(e,t=!0){t||n.pauseListeners(),history.go(e)}const o=h({location:"",base:e,go:r,createHref:P.bind(null,e)},t,n);return Object.defineProperty(o,"loLine truncated
|
||||
const s="function"===typeof Symbol&&"symbol"===typeof Symbol.toStringTag,i=e=>s?Symbol(e):"_vr_"+e,l=i("rvlm"),c=i("rvd"),u=i("r"),a=i("rl"),f=i("rvl"),p="undefined"!==typeof window;function d(e){return e.__esModule||s&&"Module"===e[Symbol.toStringTag]}const h=Object.assign;function m(e,t){const n={};for(const r in t){const o=t[r];n[r]=Array.isArray(o)?o.map(e):e(o)}return n}const g=()=>{};const v=/\/$/,y=e=>e.replace(v,"");function b(e,t,n="/"){let r,o={},s="",i="";const l=t.indexOf("?"),c=t.indexOf("#",l>-1?l:0);return l>-1&&(r=t.slice(0,l),s=t.slice(l+1,c>-1?c:t.length),o=e(s)),c>-1&&(r=r||t.slice(0,c),i=t.slice(c,t.length)),r=E(null!=r?r:t,n),{fullPath:r+(s&&"?")+s+i,path:r,query:o,hash:i}}function _(e,t){const n=t.query?e(t.query):"";return t.path+(n&&"?")+n+(t.hash||"")}function w(e,t){return t&&e.toLowerCase().startsWith(t.toLowerCase())?e.slice(t.length)||"/":e}function k(e,t,n){const r=t.matched.length-1,o=n.matched.length-1;return r>-1&&r===o&&C(t.matched[r],n.matched[o])&&x(t.params,n.params)&&e(t.query)===e(n.query)&&t.hash===n.hash}function C(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function x(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(const n in e)if(!R(e[n],t[n]))return!1;return!0}function R(e,t){return Array.isArray(e)?S(e,t):Array.isArray(t)?S(t,e):e===t}function S(e,t){return Array.isArray(t)?e.length===t.length&&e.every(((e,n)=>e===t[n])):1===e.length&&e[0]===t}function E(e,t){if(e.startsWith("/"))return e;if(!e)return t;const n=t.split("/"),r=e.split("/");let o,s,i=n.length-1;for(o=0;o<r.length;o++)if(s=r[o],1!==i&&"."!==s){if(".."!==s)break;i--}return n.slice(0,i).join("/")+"/"+r.slice(o-(o===r.length?1:0)).join("/")}var A,O;(function(e){e["pop"]="pop",e["push"]="push"})(A||(A={})),function(e){e["back"]="back",e["forward"]="forward",e["unknown"]=""}(O||(O={}));function I(e){if(!e)if(p){const t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return"/"!==e[0]&&"#"!==e[0]&&(e="/"+e),y(e)}const F=/^[^#]+#/;function T(e,t){return e.replace(F,"#")+t}function P(e,t){const n=document.documentElement.getBoundingClientRect(),r=e.getBoundingClientRect();return{behavior:t.behavior,left:r.left-n.left-(t.left||0),top:r.top-n.top-(t.top||0)}}const j=()=>({left:window.pageXOffset,top:window.pageYOffset});function $(e){let t;if("el"in e){const n=e.el,r="string"===typeof n&&n.startsWith("#");0;const o="string"===typeof n?r?document.getElementById(n.slice(1)):document.querySelector(n):n;if(!o)return;t=P(o,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(null!=t.left?t.left:window.pageXOffset,null!=t.top?t.top:window.pageYOffset)}function M(e,t){const n=history.state?history.state.position-t:-1;return n+e}const U=new Map;function L(e,t){U.set(e,t)}function N(e){const t=U.get(e);return U.delete(e),t}let J=()=>location.protocol+"//"+location.host;function B(e,t){const{pathname:n,search:r,hash:o}=t,s=e.indexOf("#");if(s>-1){let t=o.includes(e.slice(s))?e.slice(s).length:1,n=o.slice(t);return"/"!==n[0]&&(n="/"+n),w(n,"")}const i=w(n,e);return i+r+o}function D(e,t,n,r){let o=[],s=[],i=null;const l=({state:s})=>{const l=B(e,location),c=n.value,u=t.value;let a=0;if(s){if(n.value=l,t.value=s,i&&i===c)return void(i=null);a=u?s.position-u.position:0}else r(l);o.forEach((e=>{e(n.value,c,{delta:a,type:A.pop,direction:a?a>0?O.forward:O.back:O.unknown})}))};function c(){i=n.value}function u(e){o.push(e);const t=()=>{const t=o.indexOf(e);t>-1&&o.splice(t,1)};return s.push(t),t}function a(){const{history:e}=window;e.state&&e.replaceState(h({},e.state,{scroll:j()}),"")}function f(){for(const e of s)e();s=[],window.removeEventListener("popstate",l),window.removeEventListener("beforeunload",a)}return window.addEventListener("popstate",l),window.addEventListener("beforeunload",a),{pauseListeners:c,listen:u,destroy:f}}function q(e,t,n,r=!1,o=!1){return{back:e,current:t,forward:n,replaced:r,position:window.history.length,scroll:o?j():null}}function V(e){const{history:t,location:n}=window,r={value:B(e,n)},o={value:t.state};function s(r,s,i){const l=e.indexOf("#"),c=l>-1?(n.host&&document.querySelector("base")?e:e.slice(l))+r:J()+e+r;try{t[i?"replaceState":"pushState"](s,"",c),o.value=s}catch(u){console.error(u),n[i?"replace":"assign"](c)}}function i(e,n){const i=h({},t.state,q(o.value.back,e,o.value.forward,!0),n,{position:o.value.position});s(e,i,!0),r.value=e}function l(e,n){const i=h({},o.value,t.state,{forward:e,scroll:j()});s(i.current,i,!0);const l=h({},q(r.value,e,null),{position:i.position+1},n);s(e,l,!1),r.value=e}return o.value||s(r.value,{back:null,current:r.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0),{location:r,state:o,push:l,replace:i}}function H(e){e=I(e);const t=V(e),n=D(e,t.state,t.location,t.replace);function r(e,t=!0){t||n.pauseListeners(),history.go(e)}const o=h({location:"",base:e,go:r,createHref:T.bind(null,e)},t,n);return Object.defineProperty(o,"loLine truncated
|
||||
Reference in new issue
Block a user