Full Code of TwiN/gatus for AI

master 3a6ad14cba36 cached
325 files
2.6 MB
690.7k tokens
3373 symbols
1 requests
Download .txt
Showing preview only (2,756K chars total). Download the full file or copy to clipboard to get everything.
Repository: TwiN/gatus
Branch: master
Commit: 3a6ad14cba36
Files: 325
Total size: 2.6 MB

Directory structure:
gitextract_9_t34r5f/

├── .dockerignore
├── .examples/
│   ├── docker-compose/
│   │   └── compose.yaml
│   ├── docker-compose-grafana-prometheus/
│   │   ├── README.md
│   │   ├── compose.yaml
│   │   ├── grafana/
│   │   │   ├── grafana.ini
│   │   │   └── provisioning/
│   │   │       ├── dashboards/
│   │   │       │   ├── dashboard.yml
│   │   │       │   └── gatus.json
│   │   │       └── datasources/
│   │   │           └── prometheus.yml
│   │   └── prometheus/
│   │       └── prometheus.yml
│   ├── docker-compose-mattermost/
│   │   └── compose.yaml
│   ├── docker-compose-mtls/
│   │   ├── certs/
│   │   │   ├── client/
│   │   │   │   ├── client.crt
│   │   │   │   └── client.key
│   │   │   └── server/
│   │   │       ├── ca.crt
│   │   │       ├── server.crt
│   │   │       └── server.key
│   │   ├── compose.yaml
│   │   └── nginx/
│   │       └── default.conf
│   ├── docker-compose-multiple-config-files/
│   │   ├── compose.yaml
│   │   └── config/
│   │       ├── backend.yaml
│   │       ├── frontend.yaml
│   │       └── global.yaml
│   ├── docker-compose-postgres-storage/
│   │   └── compose.yaml
│   ├── docker-compose-sqlite-storage/
│   │   ├── compose.yaml
│   │   └── data/
│   │       └── .gitkeep
│   ├── docker-minimal/
│   │   └── Dockerfile
│   ├── kubernetes/
│   │   └── gatus.yaml
│   └── nixos/
│       ├── README.md
│       └── gatus.nix
├── .gitattributes
├── .github/
│   ├── FUNDING.yml
│   ├── assets/
│   │   └── gatus-diagram.drawio
│   ├── codecov.yml
│   ├── dependabot.yml
│   └── workflows/
│       ├── benchmark.yml
│       ├── labeler.yml
│       ├── publish-custom.yml
│       ├── publish-experimental.yml
│       ├── publish-latest.yml
│       ├── publish-release.yml
│       ├── regenerate-static-assets.yml
│       ├── test-ui.yml
│       └── test.yml
├── .gitignore
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── alerting/
│   ├── alert/
│   │   ├── alert.go
│   │   ├── alert_test.go
│   │   └── type.go
│   ├── config.go
│   └── provider/
│       ├── awsses/
│       │   ├── awsses.go
│       │   └── awsses_test.go
│       ├── clickup/
│       │   ├── clickup.go
│       │   └── clickup_test.go
│       ├── custom/
│       │   ├── custom.go
│       │   └── custom_test.go
│       ├── datadog/
│       │   ├── datadog.go
│       │   └── datadog_test.go
│       ├── discord/
│       │   ├── discord.go
│       │   └── discord_test.go
│       ├── email/
│       │   ├── email.go
│       │   └── email_test.go
│       ├── gitea/
│       │   ├── gitea.go
│       │   └── gitea_test.go
│       ├── github/
│       │   ├── github.go
│       │   └── github_test.go
│       ├── gitlab/
│       │   ├── gitlab.go
│       │   └── gitlab_test.go
│       ├── googlechat/
│       │   ├── googlechat.go
│       │   └── googlechat_test.go
│       ├── gotify/
│       │   ├── gotify.go
│       │   └── gotify_test.go
│       ├── homeassistant/
│       │   ├── homeassistant.go
│       │   └── homeassistant_test.go
│       ├── ifttt/
│       │   ├── ifttt.go
│       │   └── ifttt_test.go
│       ├── ilert/
│       │   ├── ilert.go
│       │   └── ilert_test.go
│       ├── incidentio/
│       │   ├── dedup.go
│       │   ├── incidentio.go
│       │   └── incidentio_test.go
│       ├── line/
│       │   ├── line.go
│       │   └── line_test.go
│       ├── matrix/
│       │   ├── matrix.go
│       │   └── matrix_test.go
│       ├── mattermost/
│       │   ├── mattermost.go
│       │   └── mattermost_test.go
│       ├── messagebird/
│       │   ├── messagebird.go
│       │   └── messagebird_test.go
│       ├── n8n/
│       │   ├── n8n.go
│       │   └── n8n_test.go
│       ├── newrelic/
│       │   ├── newrelic.go
│       │   └── newrelic_test.go
│       ├── ntfy/
│       │   ├── ntfy.go
│       │   └── ntfy_test.go
│       ├── opsgenie/
│       │   ├── opsgenie.go
│       │   └── opsgenie_test.go
│       ├── pagerduty/
│       │   ├── pagerduty.go
│       │   └── pagerduty_test.go
│       ├── plivo/
│       │   ├── plivo.go
│       │   └── plivo_test.go
│       ├── provider.go
│       ├── provider_test.go
│       ├── pushover/
│       │   ├── pushover.go
│       │   └── pushover_test.go
│       ├── rocketchat/
│       │   ├── rocketchat.go
│       │   └── rocketchat_test.go
│       ├── sendgrid/
│       │   ├── sendgrid.go
│       │   └── sendgrid_test.go
│       ├── signal/
│       │   ├── signal.go
│       │   └── signal_test.go
│       ├── signl4/
│       │   ├── signl4.go
│       │   └── signl4_test.go
│       ├── slack/
│       │   ├── slack.go
│       │   └── slack_test.go
│       ├── splunk/
│       │   ├── splunk.go
│       │   └── splunk_test.go
│       ├── squadcast/
│       │   ├── squadcast.go
│       │   └── squadcast_test.go
│       ├── teams/
│       │   ├── teams.go
│       │   └── teams_test.go
│       ├── teamsworkflows/
│       │   ├── teamsworkflows.go
│       │   └── teamsworkflows_test.go
│       ├── telegram/
│       │   ├── telegram.go
│       │   └── telegram_test.go
│       ├── twilio/
│       │   ├── twilio.go
│       │   └── twilio_test.go
│       ├── vonage/
│       │   ├── vonage.go
│       │   └── vonage_test.go
│       ├── webex/
│       │   ├── webex.go
│       │   └── webex_test.go
│       ├── zapier/
│       │   ├── zapier.go
│       │   └── zapier_test.go
│       └── zulip/
│           ├── zulip.go
│           └── zulip_test.go
├── api/
│   ├── api.go
│   ├── api_test.go
│   ├── badge.go
│   ├── badge_test.go
│   ├── cache.go
│   ├── chart.go
│   ├── chart_test.go
│   ├── config.go
│   ├── config_test.go
│   ├── custom_css.go
│   ├── endpoint_status.go
│   ├── endpoint_status_test.go
│   ├── external_endpoint.go
│   ├── external_endpoint_test.go
│   ├── raw.go
│   ├── raw_test.go
│   ├── spa.go
│   ├── spa_test.go
│   ├── suite_status.go
│   ├── suite_status_test.go
│   ├── util.go
│   └── util_test.go
├── client/
│   ├── client.go
│   ├── client_test.go
│   ├── config.go
│   ├── config_test.go
│   └── grpc.go
├── config/
│   ├── announcement/
│   │   ├── announcement.go
│   │   └── announcement_test.go
│   ├── config.go
│   ├── config_test.go
│   ├── connectivity/
│   │   ├── connectivity.go
│   │   └── connectivity_test.go
│   ├── endpoint/
│   │   ├── common.go
│   │   ├── common_test.go
│   │   ├── condition.go
│   │   ├── condition_bench_test.go
│   │   ├── condition_result.go
│   │   ├── condition_test.go
│   │   ├── dns/
│   │   │   ├── dns.go
│   │   │   └── dns_test.go
│   │   ├── endpoint.go
│   │   ├── endpoint_test.go
│   │   ├── event.go
│   │   ├── event_test.go
│   │   ├── external_endpoint.go
│   │   ├── external_endpoint_test.go
│   │   ├── heartbeat/
│   │   │   └── heartbeat.go
│   │   ├── placeholder.go
│   │   ├── placeholder_test.go
│   │   ├── result.go
│   │   ├── result_test.go
│   │   ├── ssh/
│   │   │   ├── ssh.go
│   │   │   └── ssh_test.go
│   │   ├── status.go
│   │   ├── status_test.go
│   │   ├── ui/
│   │   │   ├── ui.go
│   │   │   └── ui_test.go
│   │   └── uptime.go
│   ├── gontext/
│   │   ├── gontext.go
│   │   └── gontext_test.go
│   ├── key/
│   │   ├── key.go
│   │   ├── key_bench_test.go
│   │   └── key_test.go
│   ├── maintenance/
│   │   ├── maintenance.go
│   │   └── maintenance_test.go
│   ├── remote/
│   │   └── remote.go
│   ├── suite/
│   │   ├── result.go
│   │   ├── suite.go
│   │   ├── suite_status.go
│   │   └── suite_test.go
│   ├── tunneling/
│   │   ├── sshtunnel/
│   │   │   ├── sshtunnel.go
│   │   │   └── sshtunnel_test.go
│   │   ├── tunneling.go
│   │   └── tunneling_test.go
│   ├── ui/
│   │   ├── ui.go
│   │   └── ui_test.go
│   ├── util.go
│   └── web/
│       ├── web.go
│       └── web_test.go
├── controller/
│   ├── controller.go
│   └── controller_test.go
├── docs/
│   └── pagerduty-integration-guide.md
├── go.mod
├── go.sum
├── jsonpath/
│   ├── jsonpath.go
│   ├── jsonpath_bench_test.go
│   └── jsonpath_test.go
├── main.go
├── metrics/
│   ├── metrics.go
│   └── metrics_test.go
├── pattern/
│   ├── pattern.go
│   ├── pattern_bench_test.go
│   └── pattern_test.go
├── security/
│   ├── basic.go
│   ├── basic_test.go
│   ├── config.go
│   ├── config_test.go
│   ├── oidc.go
│   ├── oidc_test.go
│   └── sessions.go
├── storage/
│   ├── config.go
│   ├── store/
│   │   ├── common/
│   │   │   ├── errors.go
│   │   │   └── paging/
│   │   │       ├── endpoint_status_params.go
│   │   │       ├── endpoint_status_params_test.go
│   │   │       ├── suite_status_params.go
│   │   │       └── suite_status_params_test.go
│   │   ├── memory/
│   │   │   ├── memory.go
│   │   │   ├── memory_test.go
│   │   │   ├── uptime.go
│   │   │   ├── uptime_bench_test.go
│   │   │   ├── uptime_test.go
│   │   │   ├── util.go
│   │   │   ├── util_bench_test.go
│   │   │   └── util_test.go
│   │   ├── sql/
│   │   │   ├── specific_postgres.go
│   │   │   ├── specific_sqlite.go
│   │   │   ├── sql.go
│   │   │   └── sql_test.go
│   │   ├── store.go
│   │   ├── store_bench_test.go
│   │   └── store_test.go
│   └── type.go
├── test/
│   └── mock.go
├── testdata/
│   ├── badcert.key
│   ├── badcert.pem
│   ├── cert.key
│   └── cert.pem
├── watchdog/
│   ├── alerting.go
│   ├── alerting_test.go
│   ├── endpoint.go
│   ├── external_endpoint.go
│   ├── suite.go
│   └── watchdog.go
└── web/
    ├── app/
    │   ├── .gitignore
    │   ├── README.md
    │   ├── babel.config.js
    │   ├── package.json
    │   ├── postcss.config.js
    │   ├── public/
    │   │   ├── index.html
    │   │   └── manifest.json
    │   ├── src/
    │   │   ├── App.vue
    │   │   ├── components/
    │   │   │   ├── AnnouncementBanner.vue
    │   │   │   ├── EndpointCard.vue
    │   │   │   ├── FlowStep.vue
    │   │   │   ├── Loading.vue
    │   │   │   ├── Pagination.vue
    │   │   │   ├── PastAnnouncements.vue
    │   │   │   ├── ResponseTimeChart.vue
    │   │   │   ├── SearchBar.vue
    │   │   │   ├── SequentialFlowDiagram.vue
    │   │   │   ├── Settings.vue
    │   │   │   ├── Social.vue
    │   │   │   ├── StatusBadge.vue
    │   │   │   ├── StepDetailsModal.vue
    │   │   │   ├── SuiteCard.vue
    │   │   │   ├── Tooltip.vue
    │   │   │   └── ui/
    │   │   │       ├── badge/
    │   │   │       │   ├── Badge.vue
    │   │   │       │   └── index.js
    │   │   │       ├── button/
    │   │   │       │   ├── Button.vue
    │   │   │       │   └── index.js
    │   │   │       ├── card/
    │   │   │       │   ├── Card.vue
    │   │   │       │   ├── CardContent.vue
    │   │   │       │   ├── CardHeader.vue
    │   │   │       │   ├── CardTitle.vue
    │   │   │       │   └── index.js
    │   │   │       ├── input/
    │   │   │       │   ├── Input.vue
    │   │   │       │   └── index.js
    │   │   │       └── select/
    │   │   │           ├── Select.vue
    │   │   │           └── index.js
    │   │   ├── index.css
    │   │   ├── main.js
    │   │   ├── router/
    │   │   │   └── index.js
    │   │   ├── utils/
    │   │   │   ├── format.js
    │   │   │   ├── markdown.js
    │   │   │   ├── misc.js
    │   │   │   └── time.js
    │   │   └── views/
    │   │       ├── EndpointDetails.vue
    │   │       ├── Home.vue
    │   │       └── SuiteDetails.vue
    │   ├── tailwind.config.js
    │   └── vue.config.js
    ├── static/
    │   ├── css/
    │   │   └── app.css
    │   ├── index.html
    │   ├── js/
    │   │   ├── app.js
    │   │   └── chunk-vendors.js
    │   └── manifest.json
    ├── static.go
    └── static_test.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .dockerignore
================================================
.examples
Dockerfile
.github
.idea
.git
web/app
*.db
testdata

================================================
FILE: .examples/docker-compose/compose.yaml
================================================
services:
  gatus:
    image: twinproduction/gatus:latest
    ports:
      - 8080:8080
    volumes:
      - ./config:/config


================================================
FILE: .examples/docker-compose-grafana-prometheus/README.md
================================================
## Usage
Gatus exposes Prometheus metrics at `/metrics` if the `metrics` configuration option is set to `true`.

To run this example, all you need to do is execute the following command:
```console
docker-compose up
```
Once you've done the above, you should be able to access the Grafana dashboard at `http://localhost:3000`.

![Gatus Grafana dashboard](../../.github/assets/grafana-dashboard.png)


## Queries
By default, this example has a Grafana dashboard with some panels, but for the sake of verbosity, you'll find
a list of simple queries below. Those make use of the `key` parameter, which is a concatenation of the endpoint's
group and name.

### Success rate
```
sum(rate(gatus_results_total{success="true"}[30s])) by (key) / sum(rate(gatus_results_total[30s])) by (key)
```

### Response time
```
gatus_results_duration_seconds
```

### Total results per minute
```
sum(rate(gatus_results_total[5m])*60) by (key)
```

### Total successful results per minute
```
sum(rate(gatus_results_total{success="true"}[5m])*60) by (key)
```

### Total unsuccessful results per minute
```
sum(rate(gatus_results_total{success="false"}[5m])*60) by (key)
```


================================================
FILE: .examples/docker-compose-grafana-prometheus/compose.yaml
================================================
services:
  gatus:
    container_name: gatus
    image: twinproduction/gatus
    restart: always
    ports:
      - "8080:8080"
    volumes:
      - ./config:/config
    networks:
      - metrics

  prometheus:
    container_name: prometheus
    image: prom/prometheus:v3.5.0
    restart: always
    command: --config.file=/etc/prometheus/prometheus.yml
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
    networks:
      - metrics

  grafana:
    container_name: grafana
    image: grafana/grafana:12.1.0
    restart: always
    environment:
      GF_SECURITY_ADMIN_PASSWORD: secret
    ports:
      - "3000:3000"
    volumes:
      - ./grafana/grafana.ini/:/etc/grafana/grafana.ini:ro
      - ./grafana/provisioning/:/etc/grafana/provisioning/:ro
    networks:
      - metrics

networks:
  metrics:
    driver: bridge


================================================
FILE: .examples/docker-compose-grafana-prometheus/grafana/grafana.ini
================================================
[paths]

[server]

[database]

[session]

[dataproxy]

[analytics]
reporting_enabled = false

[security]

[snapshots]

[dashboards]

[users]
allow_sign_up = false
default_theme = light

[auth]

[auth.anonymous]
enabled = true
org_name = Main Org.
org_role = Admin

[auth.github]

[auth.google]

[auth.generic_oauth]

[auth.grafana_com]

[auth.proxy]

[auth.basic]

[auth.ldap]

[smtp]

[emails]

[log]
mode = console

[log.console]

[log.file]

[log.syslog]

[alerting]

[explore]

[metrics]
enabled = true

[metrics.graphite]

[tracing.jaeger]

[grafana_com]

[external_image_storage]

[external_image_storage.s3]

[external_image_storage.webdav]

[external_image_storage.gcs]

[external_image_storage.azure_blob]

[external_image_storage.local]

[rendering]

[enterprise]


================================================
FILE: .examples/docker-compose-grafana-prometheus/grafana/provisioning/dashboards/dashboard.yml
================================================
apiVersion: 1

providers:
  - name: 'Prometheus'
    orgId: 1
    folder: ''
    type: file
    disableDeletion: false
    editable: true
    options:
      path: /etc/grafana/provisioning/dashboards

================================================
FILE: .examples/docker-compose-grafana-prometheus/grafana/provisioning/dashboards/gatus.json
================================================
{
  "annotations": {
    "list": [
      {
        "builtIn": 1,
        "datasource": {
          "type": "grafana",
          "uid": "-- Grafana --"
        },
        "enable": true,
        "hide": true,
        "iconColor": "rgba(0, 211, 255, 1)",
        "name": "Annotations & Alerts",
        "type": "dashboard"
      }
    ]
  },
  "description": "Monitoring dashboard for service uptime monitoring using Gatus metrics",
  "editable": true,
  "fiscalYearStartMonth": 0,
  "graphTooltip": 0,
  "id": 41,
  "links": [],
  "panels": [
    {
      "datasource": {
        "type": "prometheus",
        "uid": "$datasource"
      },
      "description": "Services with certificate expiration warnings",
      "fieldConfig": {
        "defaults": {
          "color": {
            "mode": "thresholds"
          },
          "mappings": [],
          "noValue": "0",
          "thresholds": {
            "mode": "absolute",
            "steps": [
              {
                "color": "green",
                "value": 0
              },
              {
                "color": "yellow",
                "value": 30
              },
              {
                "color": "red",
                "value": 7
              }
            ]
          },
          "unit": "short"
        },
        "overrides": []
      },
      "gridPos": {
        "h": 8,
        "w": 4,
        "x": 0,
        "y": 0
      },
      "id": 8,
      "options": {
        "colorMode": "background",
        "graphMode": "area",
        "justifyMode": "auto",
        "orientation": "auto",
        "percentChangeColorMode": "standard",
        "reduceOptions": {
          "calcs": [
            "lastNotNull"
          ],
          "fields": "",
          "values": false
        },
        "showPercentChange": false,
        "textMode": "auto",
        "wideLayout": true
      },
      "pluginVersion": "12.1.0",
      "targets": [
        {
          "datasource": {
            "type": "prometheus",
            "uid": "$datasource"
          },
          "expr": "count(gatus_results_certificate_expiration_seconds < 2592000)",
          "hide": false,
          "legendFormat": "Expiring < 30 days",
          "refId": "A"
        },
        {
          "datasource": {
            "type": "prometheus",
            "uid": "$datasource"
          },
          "expr": "count(gatus_results_certificate_expiration_seconds < 604800)",
          "legendFormat": "Expiring < 7 days",
          "refId": "B"
        }
      ],
      "title": "Certificate Warnings",
      "type": "stat"
    },
    {
      "datasource": {
        "type": "prometheus",
        "uid": "$datasource"
      },
      "description": "Overall service availability",
      "fieldConfig": {
        "defaults": {
          "color": {
            "mode": "thresholds"
          },
          "mappings": [],
          "max": 100,
          "min": 0,
          "thresholds": {
            "mode": "absolute",
            "steps": [
              {
                "color": "red",
                "value": 0
              },
              {
                "color": "yellow",
                "value": 95
              },
              {
                "color": "green",
                "value": 99
              }
            ]
          },
          "unit": "percent"
        },
        "overrides": []
      },
      "gridPos": {
        "h": 8,
        "w": 4,
        "x": 4,
        "y": 0
      },
      "id": 9,
      "options": {
        "colorMode": "background",
        "graphMode": "area",
        "justifyMode": "auto",
        "orientation": "auto",
        "percentChangeColorMode": "standard",
        "reduceOptions": {
          "calcs": [
            "lastNotNull"
          ],
          "fields": "",
          "values": false
        },
        "showPercentChange": false,
        "textMode": "auto",
        "wideLayout": true
      },
      "pluginVersion": "12.1.0",
      "targets": [
        {
          "datasource": {
            "type": "prometheus",
            "uid": "$datasource"
          },
          "expr": "(sum(gatus_results_endpoint_success) / count(gatus_results_endpoint_success)) * 100",
          "legendFormat": "Overall Availability",
          "refId": "A"
        }
      ],
      "title": "Overall Availability",
      "type": "stat"
    },
    {
      "datasource": {
        "type": "prometheus",
        "uid": "$datasource"
      },
      "description": "Number of services being monitored",
      "fieldConfig": {
        "defaults": {
          "color": {
            "mode": "thresholds"
          },
          "mappings": [],
          "thresholds": {
            "mode": "absolute",
            "steps": [
              {
                "color": "green",
                "value": 0
              }
            ]
          },
          "unit": "short"
        },
        "overrides": []
      },
      "gridPos": {
        "h": 8,
        "w": 4,
        "x": 8,
        "y": 0
      },
      "id": 10,
      "options": {
        "colorMode": "background",
        "graphMode": "area",
        "justifyMode": "auto",
        "orientation": "auto",
        "percentChangeColorMode": "standard",
        "reduceOptions": {
          "calcs": [
            "lastNotNull"
          ],
          "fields": "",
          "values": false
        },
        "showPercentChange": false,
        "textMode": "auto",
        "wideLayout": true
      },
      "pluginVersion": "12.1.0",
      "targets": [
        {
          "datasource": {
            "type": "prometheus",
            "uid": "$datasource"
          },
          "expr": "count(gatus_results_endpoint_success)",
          "legendFormat": "Total Services",
          "refId": "A"
        }
      ],
      "title": "Monitored Services",
      "type": "stat"
    },
    {
      "datasource": {
        "type": "prometheus",
        "uid": "$datasource"
      },
      "description": "Overview of service health status across all groups",
      "fieldConfig": {
        "defaults": {
          "color": {
            "mode": "palette-classic"
          },
          "custom": {
            "hideFrom": {
              "legend": false,
              "tooltip": false,
              "vis": false,
              "viz": false
            }
          },
          "mappings": [],
          "unit": "short"
        },
        "overrides": []
      },
      "gridPos": {
        "h": 8,
        "w": 12,
        "x": 12,
        "y": 0
      },
      "id": 1,
      "options": {
        "legend": {
          "displayMode": "list",
          "placement": "bottom",
          "showLegend": true,
          "values": []
        },
        "pieType": "pie",
        "reduceOptions": {
          "calcs": [
            "lastNotNull"
          ],
          "fields": "",
          "values": false
        },
        "tooltip": {
          "hideZeros": false,
          "mode": "single",
          "sort": "none"
        }
      },
      "pluginVersion": "12.1.0",
      "targets": [
        {
          "datasource": {
            "type": "prometheus",
            "uid": "$datasource"
          },
          "expr": "sum(gatus_results_endpoint_success) by (group)",
          "legendFormat": "{{group}} - UP",
          "refId": "A"
        },
        {
          "datasource": {
            "type": "prometheus",
            "uid": "$datasource"
          },
          "expr": "sum(1 - gatus_results_endpoint_success) by (group)",
          "legendFormat": "{{group}} - DOWN",
          "refId": "B"
        }
      ],
      "title": "Service Health by Group",
      "type": "piechart"
    },
    {
      "datasource": {
        "type": "prometheus",
        "uid": "$datasource"
      },
      "description": "Domain expiration times for all domains",
      "fieldConfig": {
        "defaults": {
          "color": {
            "mode": "thresholds"
          },
          "custom": {
            "align": "auto",
            "cellOptions": {
              "type": "auto"
            },
            "inspect": false
          },
          "mappings": [],
          "thresholds": {
            "mode": "absolute",
            "steps": [
              {
                "color": "red",
                "value": 0
              },
              {
                "color": "#EAB839",
                "value": 172800
              },
              {
                "color": "green",
                "value": 604800
              }
            ]
          },
          "unit": "dtdurations"
        },
        "overrides": [
          {
            "matcher": {
              "id": "byName",
              "options": "Time Until Expiry"
            },
            "properties": [
              {
                "id": "custom.cellOptions",
                "value": {
                  "applyToRow": false,
                  "type": "color-background"
                }
              },
              {
                "id": "unit",
                "value": "s"
              }
            ]
          }
        ]
      },
      "gridPos": {
        "h": 8,
        "w": 12,
        "x": 0,
        "y": 8
      },
      "id": 3,
      "options": {
        "cellHeight": "sm",
        "footer": {
          "countRows": false,
          "fields": "",
          "reducer": [
            "sum"
          ],
          "show": false
        },
        "showHeader": true,
        "sortBy": []
      },
      "pluginVersion": "12.1.0",
      "targets": [
        {
          "datasource": {
            "type": "prometheus",
            "uid": "$datasource"
          },
          "editorMode": "code",
          "expr": "gatus_results_domain_expiration_seconds",
          "format": "table",
          "instant": true,
          "legendFormat": "__auto",
          "refId": "A"
        }
      ],
      "title": "Domain Expiration",
      "transformations": [
        {
          "id": "organize",
          "options": {
            "excludeByName": {
              "Time": true,
              "Value": false,
              "__name__": true,
              "app_kubernetes_io_instance": true,
              "app_kubernetes_io_managed_by": true,
              "app_kubernetes_io_name": true,
              "app_kubernetes_io_service": true,
              "helm_sh_chart": true,
              "instance": true,
              "job": true,
              "key": true,
              "type": true
            },
            "includeByName": {},
            "indexByName": {
              "Value": 2,
              "group": 0,
              "name": 1
            },
            "renameByName": {
              "Value": "Time Until Expiry",
              "group": "Group",
              "name": "Service"
            }
          }
        }
      ],
      "type": "table"
    },
    {
      "datasource": {
        "type": "prometheus",
        "uid": "$datasource"
      },
      "description": "SSL certificate expiration times for all services",
      "fieldConfig": {
        "defaults": {
          "color": {
            "mode": "thresholds"
          },
          "custom": {
            "align": "auto",
            "cellOptions": {
              "type": "auto"
            },
            "inspect": false
          },
          "mappings": [],
          "thresholds": {
            "mode": "absolute",
            "steps": [
              {
                "color": "red",
                "value": 0
              },
              {
                "color": "#EAB839",
                "value": 172800
              },
              {
                "color": "green",
                "value": 604800
              }
            ]
          },
          "unit": "dtdurations"
        },
        "overrides": [
          {
            "matcher": {
              "id": "byName",
              "options": "Time Until Expiry"
            },
            "properties": [
              {
                "id": "custom.cellOptions",
                "value": {
                  "applyToRow": false,
                  "type": "color-background"
                }
              },
              {
                "id": "unit",
                "value": "s"
              }
            ]
          }
        ]
      },
      "gridPos": {
        "h": 8,
        "w": 12,
        "x": 12,
        "y": 8
      },
      "id": 11,
      "options": {
        "cellHeight": "sm",
        "footer": {
          "countRows": false,
          "fields": "",
          "reducer": [
            "sum"
          ],
          "show": false
        },
        "showHeader": true,
        "sortBy": []
      },
      "pluginVersion": "12.1.0",
      "targets": [
        {
          "datasource": {
            "type": "prometheus",
            "uid": "$datasource"
          },
          "expr": "gatus_results_certificate_expiration_seconds",
          "format": "table",
          "instant": true,
          "legendFormat": "__auto",
          "refId": "A"
        }
      ],
      "title": "SSL Certificate Expiration",
      "transformations": [
        {
          "id": "organize",
          "options": {
            "excludeByName": {
              "Time": true,
              "Value": false,
              "__name__": true,
              "app_kubernetes_io_instance": true,
              "app_kubernetes_io_managed_by": true,
              "app_kubernetes_io_name": true,
              "app_kubernetes_io_service": true,
              "helm_sh_chart": true,
              "instance": true,
              "job": true,
              "key": true,
              "type": true
            },
            "includeByName": {},
            "indexByName": {
              "Value": 2,
              "group": 0,
              "name": 1
            },
            "renameByName": {
              "Value": "Time Until Expiry",
              "group": "Group",
              "name": "Service"
            }
          }
        }
      ],
      "type": "table"
    },
    {
      "datasource": {
        "type": "prometheus",
        "uid": "$datasource"
      },
      "description": "Current status distribution across all services",
      "fieldConfig": {
        "defaults": {
          "color": {
            "mode": "palette-classic"
          },
          "custom": {
            "axisBorderShow": false,
            "axisCenteredZero": false,
            "axisColorMode": "text",
            "axisLabel": "",
            "axisPlacement": "auto",
            "barAlignment": 0,
            "barWidthFactor": 0.6,
            "drawStyle": "line",
            "fillOpacity": 10,
            "gradientMode": "none",
            "hideFrom": {
              "legend": false,
              "tooltip": false,
              "vis": false,
              "viz": false
            },
            "insertNulls": false,
            "lineInterpolation": "linear",
            "lineWidth": 1,
            "pointSize": 5,
            "scaleDistribution": {
              "type": "linear"
            },
            "showPoints": "never",
            "spanNulls": false,
            "stacking": {
              "group": "A",
              "mode": "none"
            },
            "thresholdsStyle": {
              "mode": "off"
            }
          },
          "mappings": [],
          "thresholds": {
            "mode": "absolute",
            "steps": [
              {
                "color": "green",
                "value": 0
              },
              {
                "color": "red",
                "value": 80
              }
            ]
          },
          "unit": "short"
        },
        "overrides": []
      },
      "gridPos": {
        "h": 8,
        "w": 12,
        "x": 0,
        "y": 16
      },
      "id": 5,
      "options": {
        "legend": {
          "calcs": [],
          "displayMode": "list",
          "placement": "bottom",
          "showLegend": true
        },
        "tooltip": {
          "hideZeros": false,
          "mode": "single",
          "sort": "none"
        }
      },
      "pluginVersion": "12.1.0",
      "targets": [
        {
          "datasource": {
            "type": "prometheus",
            "uid": "$datasource"
          },
          "expr": "sum(gatus_results_endpoint_success)",
          "legendFormat": "Services UP",
          "refId": "A"
        },
        {
          "datasource": {
            "type": "prometheus",
            "uid": "$datasource"
          },
          "expr": "sum(1 - gatus_results_endpoint_success)",
          "legendFormat": "Services DOWN",
          "refId": "B"
        }
      ],
      "title": "Service Status Distribution",
      "type": "timeseries"
    },
    {
      "datasource": {
        "type": "prometheus",
        "uid": "$datasource"
      },
      "description": "Current status of all monitored services",
      "fieldConfig": {
        "defaults": {
          "color": {
            "mode": "thresholds"
          },
          "custom": {
            "align": "auto",
            "cellOptions": {
              "type": "auto"
            },
            "inspect": false
          },
          "mappings": [
            {
              "options": {
                "0": {
                  "color": "red",
                  "index": 0,
                  "text": "DOWN"
                },
                "1": {
                  "color": "green",
                  "index": 1,
                  "text": "UP"
                }
              },
              "type": "value"
            }
          ],
          "thresholds": {
            "mode": "absolute",
            "steps": [
              {
                "color": "red",
                "value": 0
              },
              {
                "color": "green",
                "value": 1
              }
            ]
          }
        },
        "overrides": [
          {
            "matcher": {
              "id": "byName",
              "options": "Status"
            },
            "properties": [
              {
                "id": "custom.cellOptions",
                "value": {
                  "type": "color-background"
                }
              }
            ]
          },
          {
            "matcher": {
              "id": "byName",
              "options": "Response Time"
            },
            "properties": [
              {
                "id": "unit",
                "value": "s"
              }
            ]
          }
        ]
      },
      "gridPos": {
        "h": 8,
        "w": 12,
        "x": 12,
        "y": 16
      },
      "id": 2,
      "options": {
        "cellHeight": "sm",
        "footer": {
          "countRows": false,
          "fields": "",
          "reducer": [
            "sum"
          ],
          "show": false
        },
        "showHeader": true,
        "sortBy": [
          {
            "desc": false,
            "displayName": "Status"
          }
        ]
      },
      "pluginVersion": "12.1.0",
      "targets": [
        {
          "datasource": {
            "type": "prometheus",
            "uid": "$datasource"
          },
          "expr": "gatus_results_endpoint_success",
          "format": "table",
          "instant": true,
          "legendFormat": "__auto",
          "refId": "A"
        },
        {
          "datasource": {
            "type": "prometheus",
            "uid": "$datasource"
          },
          "expr": "gatus_results_duration_seconds",
          "format": "table",
          "instant": true,
          "legendFormat": "__auto",
          "refId": "B"
        }
      ],
      "title": "Service Status Overview",
      "transformations": [
        {
          "id": "joinByField",
          "options": {
            "byField": "name",
            "mode": "outer"
          }
        },
        {
          "id": "organize",
          "options": {
            "excludeByName": {
              "Time": true,
              "__name__": true,
              "app_kubernetes_io_instance": true,
              "app_kubernetes_io_managed_by": true,
              "app_kubernetes_io_name": true,
              "app_kubernetes_io_service": true,
              "group 2": true,
              "helm_sh_chart": true,
              "instance": true,
              "job": true,
              "key": true,
              "type": true
            },
            "includeByName": {},
            "indexByName": {
              "Time 1": 4,
              "Time 2": 15,
              "Value #A": 2,
              "Value #B": 3,
              "__name__ 1": 5,
              "__name__ 2": 16,
              "app_kubernetes_io_instance 1": 6,
              "app_kubernetes_io_instance 2": 17,
              "app_kubernetes_io_managed_by 1": 7,
              "app_kubernetes_io_managed_by 2": 18,
              "app_kubernetes_io_name 1": 8,
              "app_kubernetes_io_name 2": 19,
              "app_kubernetes_io_service 1": 9,
              "app_kubernetes_io_service 2": 20,
              "group 1": 0,
              "group 2": 21,
              "helm_sh_chart 1": 10,
              "helm_sh_chart 2": 22,
              "instance 1": 11,
              "instance 2": 23,
              "job 1": 12,
              "job 2": 24,
              "key 1": 13,
              "key 2": 25,
              "name": 1,
              "type 1": 14,
              "type 2": 26
            },
            "renameByName": {
              "Value #A": "Status",
              "Value #B": "Response Time",
              "group": "Group",
              "group 1": "Group",
              "name": "Service"
            }
          }
        }
      ],
      "type": "table"
    },
    {
      "datasource": {
        "type": "prometheus",
        "uid": "$datasource"
      },
      "description": "Response times for all monitored services",
      "fieldConfig": {
        "defaults": {
          "color": {
            "mode": "palette-classic"
          },
          "custom": {
            "axisBorderShow": false,
            "axisCenteredZero": false,
            "axisColorMode": "text",
            "axisLabel": "",
            "axisPlacement": "auto",
            "barAlignment": 0,
            "barWidthFactor": 0.6,
            "drawStyle": "line",
            "fillOpacity": 10,
            "gradientMode": "none",
            "hideFrom": {
              "legend": false,
              "tooltip": false,
              "vis": false,
              "viz": false
            },
            "insertNulls": false,
            "lineInterpolation": "linear",
            "lineWidth": 1,
            "pointSize": 5,
            "scaleDistribution": {
              "type": "linear"
            },
            "showPoints": "never",
            "spanNulls": false,
            "stacking": {
              "group": "A",
              "mode": "none"
            },
            "thresholdsStyle": {
              "mode": "off"
            }
          },
          "mappings": [],
          "thresholds": {
            "mode": "absolute",
            "steps": [
              {
                "color": "green",
                "value": 0
              },
              {
                "color": "red",
                "value": 80
              }
            ]
          },
          "unit": "s"
        },
        "overrides": []
      },
      "gridPos": {
        "h": 8,
        "w": 24,
        "x": 0,
        "y": 24
      },
      "id": 4,
      "options": {
        "legend": {
          "calcs": [],
          "displayMode": "list",
          "placement": "bottom",
          "showLegend": true
        },
        "tooltip": {
          "hideZeros": false,
          "mode": "single",
          "sort": "none"
        }
      },
      "pluginVersion": "12.1.0",
      "targets": [
        {
          "datasource": {
            "type": "prometheus",
            "uid": "$datasource"
          },
          "expr": "gatus_results_duration_seconds",
          "legendFormat": "{{name}} ({{group}})",
          "refId": "A"
        }
      ],
      "title": "Response Times",
      "type": "timeseries"
    }
  ],
  "preload": false,
  "refresh": "30s",
  "schemaVersion": 41,
  "tags": [
    "gatus",
    "monitoring",
    "uptime"
  ],
  "templating": {
    "list": [
      {
        "current": {
          "text": "prometheus",
          "value": "cedv077q7bbwgd"
        },
        "description": "Select your Prometheus datasource",
        "includeAll": false,
        "label": "Datasource",
        "name": "datasource",
        "options": [],
        "query": "prometheus",
        "refresh": 1,
        "regex": "",
        "type": "datasource"
      }
    ]
  },
  "time": {
    "from": "now-1h",
    "to": "now"
  },
  "timepicker": {},
  "timezone": "",
  "title": "Gatus - Service Monitoring Dashboard",
  "uid": "4ea25b6f-2edc-416c-8282-a1164f95537a",
  "version": 1
}

================================================
FILE: .examples/docker-compose-grafana-prometheus/grafana/provisioning/datasources/prometheus.yml
================================================
apiVersion: 1

datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://prometheus:9090
    isDefault: true
    version: 1
    editable: false


================================================
FILE: .examples/docker-compose-grafana-prometheus/prometheus/prometheus.yml
================================================
scrape_configs:
  - job_name: gatus
    scrape_interval: 10s
    static_configs:
      - targets:
          - gatus:8080


================================================
FILE: .examples/docker-compose-mattermost/compose.yaml
================================================
services:
  gatus:
    container_name: gatus
    image: twinproduction/gatus:latest
    ports:
      - "8080:8080"
    volumes:
      - ./config:/config
    networks:
      - default

  mattermost:
    container_name: mattermost
    image: mattermost/mattermost-preview:5.26.0
    ports:
      - "8065:8065"
    networks:
      - default

networks:
  default:
    driver: bridge


================================================
FILE: .examples/docker-compose-mtls/certs/client/client.crt
================================================
-----BEGIN CERTIFICATE-----
MIIFBjCCAu6gAwIBAgIUHJXHAqywj2v25AgX7pDSZ+LX4iAwDQYJKoZIhvcNAQEL
BQAwEjEQMA4GA1UEAwwHZXhhbXBsZTAeFw0yNDA0MjUwMTQ1MDFaFw0yOTA0MjQw
MTQ1MDFaMBExDzANBgNVBAMMBmNsaWVudDCCAiIwDQYJKoZIhvcNAQEBBQADggIP
ADCCAgoCggIBANTmRlS5BNG82mOdrhtRPIBD5U40nEW4CVFm85ZJ4Bge4Ty86juf
aoCnI6AEfwpVnJhXPzjUsMBxJFMbiCB+QTJRpxTphtK7orpbwRHjaDZNaLr1MrUO
ieADGiHw93zVDikD8FP5vG+2XWWA56hY84Ac0TR9GqPjsW0nobMgBNgsRtbYUD0B
T5QOItK180xQRn4jbys5jRnr161S+Sbg6mglz1LBFBCLmZnhZFZ8FAn87gumbnWN
etSnu9kX6iOXBIaB+3nuHOL4xmAan8tAyen6mPfkXrE5ogovjqFFMTUJOKQoJVp3
zzm/0XYANxoItFGtdjGMTl5IgI220/6kfpn6PYN7y1kYn5EI+UbobD/CuAhd94p6
aQwOXU53/l+eNH/XnTsL/32QQ6qdq8sYqevlslk1M39kKNewWYCeRzYlCVscQk14
O3fkyXrtRkz30xrzfjvJQ/VzMi+e5UlemsCuCXTVZ5YyBnuWyY+mI6lZICltZSSX
VinKzpz+t4Jl7glhKiGHaNAkBX2oLddyf280zw4Cx7nDMPs4uOHONYpm90IxEOJe
zgJ9YxPK9aaKv2AoYLbvhYyKrVT+TFqoEsbQk4vK0t0Gc1j5z4dET31CSOuxVnnU
LYwtbILFc0uZrbuOAbEbXtjPpw2OGqWagD0QpkE8TjN0Hd0ibyXyUuz5AgMBAAGj
VTBTMBEGA1UdEQQKMAiCBmNsaWVudDAdBgNVHQ4EFgQUleILTHG5lT2RhSe9H4fV
xUh0bNUwHwYDVR0jBBgwFoAUbh9Tg4oxxnHJTSaa0WLBTesYwxEwDQYJKoZIhvcN
AQELBQADggIBABq8zjRrDaljl867MXAlmbV7eJkSnaWRFct+N//jCVNnKMYaxyQm
+UG12xYP0U9Zr9vhsqwyTZTQFx/ZFiiz2zfXPtUAppV3AjE67IlKRbec3qmUhj0H
Rv20eNNWXTl1XTX5WDV5887TF+HLZm/4W2ZSBbS3V89cFhBLosy7HnBGrP0hACne
ZbdQWnnLHJMDKXkZey1H1ZLQQCQdAKGS147firj29M8uzSRHgrR6pvsNQnRT0zDL
TlTJoxyGTMaoj+1IZvRsAYMZCRb8Yct/v2i/ukIykFWUJZ+1Z3UZhGrX+gdhLfZM
jAP4VQ+vFgwD6NEXAA2DatoRqxbN1ZGJQkvnobWJdZDiYu4hBCs8ugKUTE+0iXWt
hSyrAVUspFCIeDN4xsXT5b0j2Ps4bpSAiGx+aDDTPUnd881I6JGCiIavgvdFMLCW
yOXJOZvXcNQwsndkob5fZAEqetjrARsHhQuygEq/LnPc6lWsO8O6UzYArEiKWTMx
N/5hx12Pb7aaQd1f4P3gmmHMb/YiCQK1Qy5d4v68POeqyrLvAHbvCwEMhBAbnLvw
gne3psql8s5wxhnzwYltcBUmmAw1t33CwzRBGEKifRdLGtA9pbua4G/tomcDDjVS
ChsHGebJvNxOnsQqoGgozqM2x8ScxmJzIflGxrKmEA8ybHpU0d02Xp3b
-----END CERTIFICATE-----


================================================
FILE: .examples/docker-compose-mtls/certs/client/client.key
================================================
-----BEGIN RSA PRIVATE KEY-----
MIIJKQIBAAKCAgEA1OZGVLkE0bzaY52uG1E8gEPlTjScRbgJUWbzlkngGB7hPLzq
O59qgKcjoAR/ClWcmFc/ONSwwHEkUxuIIH5BMlGnFOmG0ruiulvBEeNoNk1ouvUy
tQ6J4AMaIfD3fNUOKQPwU/m8b7ZdZYDnqFjzgBzRNH0ao+OxbSehsyAE2CxG1thQ
PQFPlA4i0rXzTFBGfiNvKzmNGevXrVL5JuDqaCXPUsEUEIuZmeFkVnwUCfzuC6Zu
dY161Ke72RfqI5cEhoH7ee4c4vjGYBqfy0DJ6fqY9+ResTmiCi+OoUUxNQk4pCgl
WnfPOb/RdgA3Ggi0Ua12MYxOXkiAjbbT/qR+mfo9g3vLWRifkQj5RuhsP8K4CF33
inppDA5dTnf+X540f9edOwv/fZBDqp2ryxip6+WyWTUzf2Qo17BZgJ5HNiUJWxxC
TXg7d+TJeu1GTPfTGvN+O8lD9XMyL57lSV6awK4JdNVnljIGe5bJj6YjqVkgKW1l
JJdWKcrOnP63gmXuCWEqIYdo0CQFfagt13J/bzTPDgLHucMw+zi44c41imb3QjEQ
4l7OAn1jE8r1poq/YChgtu+FjIqtVP5MWqgSxtCTi8rS3QZzWPnPh0RPfUJI67FW
edQtjC1sgsVzS5mtu44BsRte2M+nDY4apZqAPRCmQTxOM3Qd3SJvJfJS7PkCAwEA
AQKCAgAPwAALUStib3aMkLlfpfve1VGyc8FChcySrBYbKS3zOt2Y27T3DOJuesRE
7fA5Yyn+5H1129jo87XR5s3ZnDLV4SUw2THd3H8RCwFWgcdPinHUBZhnEpial5V9
q1DzzY3gSj1OSRcVVfLE3pYaEIflvhFasQ1L0JLAq4I9OSzX5+FPEEOnWmB5Ey6k
/fbuJLDXsLwPAOadDfiFBwgNm0KxdRKdtvugBGPW9s4Fzo9rnxLmjmfKOdmQv96Y
FI/Vat0Cgmfd661RZpbDvKnTpIsLdzw3zTpAIYOzqImvCT+3AmP2qPhSdV3sPMeR
047qqyLZOVxEFXLQFiGvL4uxYUPy8k0ZI9xkgOfZ/uASozMWsHkaD04+UDi1+kw5
nfasZLvOWBW/WE/E1Rfz8IiYTeZbgTnY4CraiLrIRc0LGgD1Df4gNr25+P+LKLyK
/WW89dl6/397HOFnA7CHi7DaA8+9uZAjOWhoCNDdqAVa3QpDD/3/iRiih26bjJfH
2+sarxU8GovDZFxWd59BUP3jkukCFH+CliQy72JtLXiuPNPAWeGV9UXxtIu40sRX
Sax/TQytYi2J9NJFZFMTwVueIfzsWc8dyM+IPAYJQxN94xYKQU4+Rb/wqqHgUfjT
1ZQJb8Cmg56IDY/0EPJWQ0qgnE7TZbY2BOEYbpOzdccwUbcEjQKCAQEA8kVyw4Hw
nqcDWXjzMhOOoRoF8CNwXBvE2KBzpuAioivGcSkjkm8vLGfQYAbDOVMPFt3xlZS0
0lQm894176Kk8BiMqtyPRWWOsv4vYMBTqbehKn09Kbh6lM7d7jO7sh5iWf4jt3Bw
Sk4XhZ9oQ/kpnEKiHPymHQY3pVYEyFCGJ8mdS6g/TWiYmjMjkQDVFA4xkiyJ0S5J
NGYxI+YXtHVTVNSePKvY0h51EqTxsexAphGjXnQ3xoe6e3tVGBkeEkcZlESFD/91
0iqdc5VtKQOwy6Tj4Awk7oK5/u3tfpyIyo31LQIqreTqMO534838lpyp3CbRdvCF
QdCNpKFX1gZgmwKCAQEA4Pa9VKO3Aw95fpp0T81xNi+Js/NhdsvQyv9NI9xOKKQU
hiWxmYmyyna3zliDGlqtlw113JFTNQYl1k1yi4JQPu2gnj8te9nB0yv0RVxvbTOq
u8K1j9Xmj8XVpcKftusQsZ2xu52ONj3ZOOf22wE4Y6mdQcps+rN6XTHRBn7a5b0v
ZCvWf4CIttdIh51pZUIbZKHTU51uU7AhTCY/wEUtiHwYTT9Wiy9Lmay5Lh2s2PCz
yPE5Y970nOzlSCUl3bVgY1t0xbQtaO5AJ/iuw/vNw+YAiAIPNDUcbcK5njb//+0E
uTEtDA6SHeYfsNXGDzxipueKXFHfJLCTXnnT5/1v+wKCAQEA0pF78uNAQJSGe8B9
F3waDnmwyYvzv4q/J00l19edIniLrJUF/uM2DBFa8etOyMchKU3UCJ9MHjbX+EOd
e19QngGoWWUD/VwMkBQPF7dxv+QDZwudGmLl3+qAx+Uc8O4pq3AQmQJYBq0jEpd/
Jv0rpk3f2vPYaQebW8+MrpIWWASK+1QLWPtdD0D9W61uhVTkzth5HF9vbuSXN01o
Mwd6WxPFSJRQCihAtui3zV26vtw7sv+t7pbPhT2nsx85nMdBOzXmtQXi4Lz7RpeM
XgaAJi91g6jqfIcQo7smHVJuLib9/pWQhL2estLBTzUcocced2Mh0Y+xMofSZFF7
J2E5mwKCAQAO9npbUdRPYM0c7ZsE385C42COVobKBv5pMhfoZbPRIjC3R3SLmMwK
iWDqWZrGuvdGz79iH0xgf3suyNHwk4dQ2C9RtzQIQ9CPgiHqJx7GLaSSfn3jBkAi
me7+6nYDDZl7pth2eSFHXE/BaDRUFr2wa0ypXpRnDF78Kd8URoW6uB2Z1QycSGlP
d/w8AO1Mrdvykozix9rZuCJO1VByMme350EaijbwZQHrQ8DBX3nqp//dQqYljWPJ
uDv703S0TWcO1LtslvJaQ1aDEhhVsr7Z48dvRGvMdifg6Q29hzz5wcMJqkqrvaBc
Wr0K3v0gcEzDey0JvOxRnWj/5KyChqnXAoIBAQDq6Dsks6BjVP4Y1HaA/NWcZxUU
EZfNCTA19jIHSUiPbWzWHNdndrUq33HkPorNmFaEIrTqd/viqahr2nXpYiY/7E+V
cpn9eSxot5J8DB4VI92UG9kixxY4K7QTMKvV43Rt6BLosW/cHxW5XTNhB4JDK+TO
NlHH48fUp2qJh7/qwSikDG130RVHKwK/5Fv3NQyXTw1/n9bhnaC4eSvV39CNSeb5
rWNEZcnc9zHT2z1UespzVTxVy4hscrkssXxcCq4bOF4bnDFjfblE43o/KrVr2/Ub
jzpXQrAwXNq7pAkIpin0v40lCeTMosSgQLFqMWmtmlCpBVkyEAc9ZYXc3Vs0
-----END RSA PRIVATE KEY-----


================================================
FILE: .examples/docker-compose-mtls/certs/server/ca.crt
================================================
-----BEGIN CERTIFICATE-----
MIIE9DCCAtygAwIBAgIUCXgA3IbeA2mn8DQ0E5IxaKBLtf8wDQYJKoZIhvcNAQEL
BQAwEjEQMA4GA1UEAwwHZXhhbXBsZTAeFw0yNDA0MjUwMTE5MzRaFw0zNDA0MjMw
MTE5MzRaMBIxEDAOBgNVBAMMB2V4YW1wbGUwggIiMA0GCSqGSIb3DQEBAQUAA4IC
DwAwggIKAoICAQDLE4aTrVJrAVYksFJt5fIVhEJT5T0cLqvtDRf9hXA5Gowremsl
VJPBm4qbdImzJZCfCcbVjFEBw8h9xID1JUqRWjJ8BfTnpa4qc1e+xRtnvC+OsUeT
CCgZvK3TZ5vFsaEbRoNGuiaNq9WSTfjLwTxkK6C3Xogm9uDx73PdRob1TNK5A9mE
Ws3ZyV91+g1phKdlNMRaK+wUrjUjEMLgr0t5A5t6WKefsGrFUDaT3sye3ZxDYuEa
ljt+F8hLVyvkDBAhh6B4S5dQILjp7L3VgOsG7Hx9py1TwCbpWXZEuee/1/2OD8tA
ALsxkvRE1w4AZzLPYRL/dOMllLjROQ4VugU8GVpNU7saK5SeWBw3XHyJ9m8vne3R
cPWaZTfkwfj8NjCgi9BzBPW8/uw7XZMmQFyTj494OKM3T5JQ5jZ5XD97ONm9h+C/
oOmkcWHz6IwEUu7XV5IESxiFlrq8ByAYF98XPhn2wMMrm2OvHMOwrfw2+5U8je5C
z70p9kpiGK8qCyjbOl9im975jwFCbl7LSj3Y+0+vRlTG/JA4jNZhXsMJcAxeJpvr
pmm/IzN+uXNQzmKzBHVDw+mTUMPziRsUq4q6WrcuQFZa6kQFGNYWI/eWV8o4AAvp
HtrOGdSyU19w0QqPW0wHmhsV2XFcn6H/E1Qg6sxWpl45YWJFhNaITxm1EQIDAQAB
o0IwQDAOBgNVHQ8BAf8EBAMCAgQwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
bh9Tg4oxxnHJTSaa0WLBTesYwxEwDQYJKoZIhvcNAQELBQADggIBAKvOh81Gag0r
0ipYS9aK6rp58b6jPpF6shr3xFiJQVovgSvxNS3aWolh+ZupTCC3H2Q1ZUgatak0
VyEJVO4a7Tz+1XlA6KErhnORC6HB/fgr5KEGraO3Q1uWonPal5QU8xHFStbRaXfx
hl/k4LLhIdJqcJE+XX/AL8ekZ3NPDtf9+k4V+RBuarLGuKgOtBB8+1qjSpClmW2B
DaWPlrLPOr2Sd29WOeWHifwVc6kBGpwM3g5VGdDsNX4Ba5eIG3lX2kUzJ8wNGEf0
bZxcVbTBY+D4JaV4WXoeFmajjK3EdizRpJRZw3fM0ZIeqVYysByNu/TovYLJnBPs
5AybnO4RzYONKJtZ1GtQgJyG+80/VffDJeBmHKEiYvE6mvOFEBAcU4VLU6sfwfT1
y1dZq5G9Km72Fg5kCuYDXTT+PB5VAV3Z6k819tG3TyI4hPlEphpoidRbZ+QS9tK5
RgHah9EJoM7tDAN/mUVHJHQhhLJDBn+iCBYgSJVLwoE+F39NO9oFPD/ZxhJkbk9b
LkFnpjrVbwD1CNnawX3I2Eytg1IbbzyviQIbpSAEpotk9pCLMAxTR3a08wrVMwst
2XVSrgK0uUKsZhCIc+q21k98aeNIINor15humizngyBWYOk8SqV84ZNcD6VlM3Qv
ShSKoAkdKxcGG1+MKPt5b7zqvTo8BBPM
-----END CERTIFICATE-----


================================================
FILE: .examples/docker-compose-mtls/certs/server/server.crt
================================================
-----BEGIN CERTIFICATE-----
MIIFDjCCAvagAwIBAgITc5Ejz7RzBJ2/PcUMsVhj41RtQDANBgkqhkiG9w0BAQsF
ADASMRAwDgYDVQQDDAdleGFtcGxlMB4XDTI0MDQyNTAxNDQ1N1oXDTI5MDQyNDAx
NDQ1N1owEDEOMAwGA1UEAwwFbmdpbngwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
ggIKAoICAQCgbLBnVrBdRkBF2XmJgDTiRqWFPQledzCrkHF4eiUvtEytJhkpoRv2
+SiRPsjCo3XjwcgQIgSy1sHUV8Sazn7V5ux/XBRovhdhUivzI8JSRYj6qwqdUnOy
dG1ZEy/VRLsIVfoFB0jKJrZCXMT256xkYTlsgPePDsduO7IPPrTN0/I/qBvINFet
zgWCl2qlZgF4c/MHljo2TR1KlBv0RJUZbfXPwemUazyMrh/MfQHaHE5pfrmMWFGA
6yLYHEhG+fy5d3F/1+4J24D2j7deIFmmuJMPSlAPt1UjDm7M/bmoTxDG+1MRXSnN
647EzzS0TFZspHe2+yBbw6j0MMiWMzNZX2iXGVcswXwrphe7ro6OITynM76gDTuM
ISYXKYHayqW0rHFRlKxMcnmrpf5tBuK7XKyoQv/LbFKI1e+j1bNVe7OZtC88EWRc
SD8WDLqo/3rsxJkRXRW/49hO1nynHrknXJEpZeRnTyglS+VCzXYD0XzwzPKN7CyN
CHpYpOcWrAMF+EJnE4WRVyJAAt4C1pGhiwn0yCvLEGXXedI/rR5zmUBKitSe7oMT
J82H/VaGtwH0lOD9Jjsv9cb+s1c3tChPDKvgGGDaFnlehKg9TM7p+xc9mnEsitfv
ovSGzYHk29nQu/S4QrPfWuCNwM2vP9OQ+VJyzDzSyH8iuPPmkfmK5wIDAQABo18w
XTAbBgNVHREEFDASggVuZ2lueIIJbG9jYWxob3N0MB0GA1UdDgQWBBT89oboWPBC
oNsSbaNquzrjTza6xDAfBgNVHSMEGDAWgBRuH1ODijHGcclNJprRYsFN6xjDETAN
BgkqhkiG9w0BAQsFAAOCAgEAeg8QwBTne1IGZMDvIGgs95lifzuTXGVQWEid7VVp
MmXGRYsweb0MwTUq3gSUc+3OPibR0i5HCJRR04H4U+cIjR6em1foIV/bW6nTaSls
xQAj92eMmzOo/KtOYqMnk//+Da5NvY0myWa/8FgJ7rK1tOZYiTZqFOlIsaiQMHgp
/PEkZBP5V57h0PY7T7tEj4SCw3DJ6qzzIdpD8T3+9kXd9dcrrjbivBkkJ23agcG5
wBcI862ELNJOD7p7+OFsv7IRsoXXYrydaDg8OJQovh4RccRqVEQu3hZdi7cPb8xJ
G7Gxn8SfSVcPg/UObiggydMl8E8QwqWAzJHvl1KUECd5QG6eq984JTR7zQB2iGb6
1qq+/d9uciuB2YY2h/0rl3Fjy6J6k3fpQK577TlJjZc0F4WH8fW5bcsyGTszxQLI
jQ6FuSOr55lZ9O3R3+95tAdJTrWsxX7j7xMIAXSYrfNt5HM91XNhqISF4SIZOBB6
enVrrJ/oCFqVSbYf6RVQz3XmPEEMh+k9KdwvIvwoS9NivLD3QH0RjhTyzHbf+LlR
rWM46XhmBwajlpnIuuMp6jZcXnbhTO1SheoRVMdijcnW+zrmx5oyn3peCfPqOVLz
95YfJUIFCt+0p/87/0Mm76uVemK6kFKZJQPnfbAdsKF7igPZfUQx6wZZP1qK9ZEU
eOk=
-----END CERTIFICATE-----


================================================
FILE: .examples/docker-compose-mtls/certs/server/server.key
================================================
-----BEGIN RSA PRIVATE KEY-----
MIIJKQIBAAKCAgEAoGywZ1awXUZARdl5iYA04kalhT0JXncwq5BxeHolL7RMrSYZ
KaEb9vkokT7IwqN148HIECIEstbB1FfEms5+1ebsf1wUaL4XYVIr8yPCUkWI+qsK
nVJzsnRtWRMv1US7CFX6BQdIyia2QlzE9uesZGE5bID3jw7HbjuyDz60zdPyP6gb
yDRXrc4FgpdqpWYBeHPzB5Y6Nk0dSpQb9ESVGW31z8HplGs8jK4fzH0B2hxOaX65
jFhRgOsi2BxIRvn8uXdxf9fuCduA9o+3XiBZpriTD0pQD7dVIw5uzP25qE8QxvtT
EV0pzeuOxM80tExWbKR3tvsgW8Oo9DDIljMzWV9olxlXLMF8K6YXu66OjiE8pzO+
oA07jCEmFymB2sqltKxxUZSsTHJ5q6X+bQbiu1ysqEL/y2xSiNXvo9WzVXuzmbQv
PBFkXEg/Fgy6qP967MSZEV0Vv+PYTtZ8px65J1yRKWXkZ08oJUvlQs12A9F88Mzy
jewsjQh6WKTnFqwDBfhCZxOFkVciQALeAtaRoYsJ9MgryxBl13nSP60ec5lASorU
nu6DEyfNh/1WhrcB9JTg/SY7L/XG/rNXN7QoTwyr4Bhg2hZ5XoSoPUzO6fsXPZpx
LIrX76L0hs2B5NvZ0Lv0uEKz31rgjcDNrz/TkPlScsw80sh/Irjz5pH5iucCAwEA
AQKCAgADiEEeFV+OvjQ+FXrCl0sSzGFqnJxvMwqkTGrjLzVQZpTlnxggvYZjGrtU
71/2QSkgWazxBf66fVYJOeF/Uxqh1RLR/xIH+F+FagzDrr7hltxcQJXcPuuDO2MI
+g4skPXZSiNWJwHoSY/ryCUiFpnKIAXmqLRKtxWXDMNv6H6MpaUI18e80cI4dnfS
l0jm2Wcg4tSwDxO7DFmfwcEX0MbDp5Mo/ukIto+/vTnAA+Sdi9ACLKMjPvKUdxju
TzkcLvbskn+yQ+ve1bFyPFnaPbYboKbESGuY3P2H5xJzewayeQMyjmgW0slP2mbr
WHCdo6ynebuVENR2kMlQjx5riDcSMMX5TLGPgNL7ZBf2b52mUgFyQb27eO2WXeyH
YLtInlKA44bdi76sDK+s8zYywZnxsUy7xrKhHE5rqz964EfoLRcY/fCm7XnMo6uK
VviBtdPebsMqkZOUKSaYSRpUgXILTud5FD+m68FeVjUvQFQqHYEa3gx+rAIjKBIn
082NzfDZSHVsvG+iB5q+37R8C0/YUzSb3TXys5pA82YsjIFeQiVE4hrV1yeNIZf6
2iaPD/r5H3vt0rFEDINZafC+6bTTRQoq8TOCZFh/Lu+ynXKOPrVUF8/y3sd8+T2v
kRDOL37reUotjE1lbO4RhLgHbeWHlT/PPnF7RDKCe6/erg2MqQKCAQEAy3f8B6I8
7CP4CZmMDWwHWsjMS/HGZgvPPbmWhaeZZmFyYi7I8MruJPhlhlw6YoUIV9Vvp8zE
eLtDvZ5WXuL38aRElWzNyrhrU1/vH4pkaFk+OgRcaleGUof+go0lE8BIYnWoWovo
/F7lQMQmHY4SuwF4oj6dpus7jMm41PQqDTsjofdLgwVAGy30LIkVt8qYha77sL8N
0ohXomDGik0nVa+i2mOJ0UuooGYF8WhujzVcELcerYvvg9kFDqJaEXdfTx4DRwiz
6f5gSbZHME7moqEkcJRtwj8TXSJYRHTI8ngS0xzyV0u2RL3FOxTcgikJIkmU6W3L
IcbP6XVlrCdoswKCAQEAydfBcsYcS2mMqCOdKkGVj6zBriT78/5dtPYeId9WkrnX
1vz6ErjHQ8vZkduvCm3KkijQvva+DFV0sv24qTyA2BIoDUJdk7cY962nR4Q9FHTX
Dkn1kgeKg4TtNdgo2KsIUn7bCibKASCExo6rO3PWiQyF+jTJVDD3rXx7+7N7WJaz
zTVt6BNOWoIjTufdXfRWt3wi0H6sSkqvRWoIAaguXkKXH7oBx0gKs+oAVovFvg7A
LLEtTszsv2LmbpGWaiT3Ny215mA0ZGI9T4utK7oUgd+DlV0+vj5tFfsye4COpCyG
V/ZQ7CBbxHDDak3R3fYy5pOwmh6814wHMyKKfdGm/QKCAQEAiW4Pk3BnyfA5lvJZ
gK9ZAF7kbt9tbHvJjR2Pp9Meb+KeCecj3lCTLfGBUZF19hl5GyqU8jgC9LE3/hm2
qPyREGwtzufg0G5kP7pqn1kwnLK6ryFG8qUPmys0IyYGxyJ3QdnKzu31fpDyNB7I
x+mwiRNjUeMNRTNZ06xk5aHNzYYGeV25aVPgivstE++79ZooDxOz+Rvy0CM7XfgT
4lJeoSeyzeOxsOZzjXObzAUHuD8IYlntpLcCHoI1Qj8yqt2ASMYy3IXqT8B7dQ5j
YyPH8Ez7efcnc656+8s453QiTnP/8wx4O7Jt+FxdnZxnnJrvCnO82zZHoBbTVBLx
i6hKtQKCAQA0j3SWmLRBhwjTuAJzQITb1xbQbF0X2oM4XmbWVzxKFQ75swLD4U4y
f2D2tIhOZOy9RtelAsfWmmI7QgrWNyUuHvxDB6cqkiF0Tcoju3HUY+CknenOzxvo
x7KltNZeJZuTL+mGKTetN3Sb6Ab7Al05bwNsdlZ/EAlPKf13O/PAy+2iYGlwZ6ad
twnOwF5K2xfBzBecx3/CENS3dLcFB3CbpyeHYX6ZEE+JLkRMRTWHGnw8px6vSHnW
FMEAxfSvS1T9D3Awv5ilE1f34N2FZ31znGq9eHygOc1aTgGFW6LJabbKLSBBfOOo
sdyRUBZ4gGYc2RTB7YMrdhFh5Xq+7NtZAoIBAQCOJ3CLecp/rS+lGy7oyx4f6QDd
zH/30Y/uvXLPUj+Ljg9bMTG9chjaKfyApXv6rcQI0d6wrqAunNl1b3opBQjsGCSt
bpBV/rGg3sl752og6KU1PCZ2KkVYPjugNhqPGonNh8tlw+1xFyBdt0c68g/auIHq
WaT5tWVfP01Ri43RjyCgNtJ2TJUzbA40BteDHPWKeM1lZ6e92fJTp5IjQ/Okc41u
Elr7p22fx/N04JTX9G6oGdxM7Gh2Uf4i4PnNOi+C3xqLrtUEi/OLof2UHlatypt9
pix0bXJtZE7WfFfesQIxGffVBhgN3UgqhAf2wquHgm1O17JXrmkR6JSYNpKc
-----END RSA PRIVATE KEY-----


================================================
FILE: .examples/docker-compose-mtls/compose.yaml
================================================
services:
  nginx:
    image: nginx:stable
    volumes:
      - ./certs/server:/etc/nginx/certs
      - ./nginx:/etc/nginx/conf.d
    ports:
      - "8443:443"
    networks:
      - mtls

  gatus:
    image: twinproduction/gatus:latest
    restart: always
    ports:
      - "8080:8080"
    volumes:
      - ./config:/config
      - ./certs/client:/certs
    environment:
      - GATUS_CONFIG_PATH=/config
    networks:
      - mtls

networks:
  mtls:


================================================
FILE: .examples/docker-compose-mtls/nginx/default.conf
================================================
server {
    listen                  443 ssl;

    ssl_certificate         /etc/nginx/certs/server.crt;
    ssl_certificate_key     /etc/nginx/certs/server.key;
    ssl_client_certificate  /etc/nginx/certs/ca.crt;
    ssl_verify_client       on;

    location / {
      if ($ssl_client_verify != SUCCESS) {
        return 403;
      }
      root   /usr/share/nginx/html;
      index  index.html index.htm;
    }
}

================================================
FILE: .examples/docker-compose-multiple-config-files/compose.yaml
================================================
services:
  gatus:
    image: twinproduction/gatus:latest
    ports:
      - "8080:8080"
    environment:
      - GATUS_CONFIG_PATH=/config
    volumes:
      - ./config:/config

================================================
FILE: .examples/docker-compose-multiple-config-files/config/backend.yaml
================================================
endpoints:
  - name: check-if-api-is-healthy
    group: backend
    url: "https://twin.sh/health"
    interval: 5m
    conditions:
      - "[STATUS] == 200"
      - "[BODY].status == UP"
      - "[RESPONSE_TIME] < 1000"

  - name: check-if-website-is-pingable
    url: "icmp://example.org"
    interval: 1m
    conditions:
      - "[CONNECTED] == true"

  - name: check-domain-expiration
    url: "https://example.org"
    interval: 6h
    conditions:
      - "[DOMAIN_EXPIRATION] > 720h"


================================================
FILE: .examples/docker-compose-multiple-config-files/config/frontend.yaml
================================================
endpoints:
  - name: make-sure-html-rendering-works
    group: frontend
    url: "https://example.org"
    interval: 5m
    conditions:
      - "[STATUS] == 200"
      - "[BODY] == pat(*<h1>Example Domain</h1>*)" # Check for header in HTML page


================================================
FILE: .examples/docker-compose-multiple-config-files/config/global.yaml
================================================
metrics: true
ui:
  header: Example Company
  link: https://example.org
  buttons:
    - name: "Home"
      link: "https://example.org"


================================================
FILE: .examples/docker-compose-postgres-storage/compose.yaml
================================================
services:
  postgres:
    image: postgres
    volumes:
      - ./data/db:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_DB=gatus
      - POSTGRES_USER=username
      - POSTGRES_PASSWORD=password
    networks:
      - web

  gatus:
    image: twinproduction/gatus:latest
    restart: always
    ports:
      - "8080:8080"
    environment:
      - POSTGRES_USER=username
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=gatus
    volumes:
      - ./config:/config
    networks:
      - web
    depends_on:
      - postgres

networks:
  web:


================================================
FILE: .examples/docker-compose-sqlite-storage/compose.yaml
================================================
services:
  gatus:
    image: twinproduction/gatus:latest
    ports:
      - "8080:8080"
    volumes:
      - ./config:/config
      - ./data:/data/


================================================
FILE: .examples/docker-compose-sqlite-storage/data/.gitkeep
================================================


================================================
FILE: .examples/docker-minimal/Dockerfile
================================================
FROM twinproduction/gatus
ADD config.yaml ./config/config.yaml

================================================
FILE: .examples/kubernetes/gatus.yaml
================================================
apiVersion: v1
kind: ConfigMap
metadata:
  name: gatus
  namespace: kube-system
data:
  config.yaml: |
    metrics: true
    endpoints:
      - name: website
        url: https://twin.sh/health
        interval: 5m
        conditions:
          - "[STATUS] == 200"
          - "[BODY].status == UP"

      - name: github
        url: https://api.github.com/healthz
        interval: 5m
        conditions:
          - "[STATUS] == 200"

      - name: cat-fact
        url: "https://cat-fact.herokuapp.com/facts/random"
        interval: 5m
        conditions:
          - "[STATUS] == 200"
          - "[BODY].deleted == false"
          - "len([BODY].text) > 0"
          - "[BODY].text == pat(*cat*)"
          - "[STATUS] == pat(2*)"
          - "[CONNECTED] == true"

      - name: example
        url: https://example.com/
        conditions:
          - "[STATUS] == 200"
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: gatus
  namespace: kube-system
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: gatus
  namespace: kube-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: gatus
  template:
    metadata:
      name: gatus
      namespace: kube-system
      labels:
        app: gatus
    spec:
      serviceAccountName: gatus
      terminationGracePeriodSeconds: 5
      containers:
        - image: twinproduction/gatus
          imagePullPolicy: IfNotPresent
          name: gatus
          ports:
            - containerPort: 8080
              name: http
              protocol: TCP
          resources:
            limits:
              cpu: 250m
              memory: 100M
            requests:
              cpu: 50m
              memory: 30M
          readinessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 10
            successThreshold: 1
            failureThreshold: 3
          livenessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 10
            periodSeconds: 10
            successThreshold: 1
            failureThreshold: 5
          volumeMounts:
            - mountPath: /config
              name: gatus-config
      volumes:
        - configMap:
            name: gatus
          name: gatus-config
---
apiVersion: v1
kind: Service
metadata:
  name: gatus
  namespace: kube-system
spec:
  ports:
    - name: http
      port: 8080
      protocol: TCP
      targetPort: 8080
  selector:
    app: gatus

================================================
FILE: .examples/nixos/README.md
================================================
# NixOS

Gatus is implemented as a NixOS module. See [gatus.nix](./gatus.nix) for example
usage.


================================================
FILE: .examples/nixos/gatus.nix
================================================
{
  services.gatus = {
    enable = true;

    settings = {
      web.port = 8080;

      endpoints = [
        {
          name = "website";
          url = "https://twin.sh/health";
          interval = "5m";

          conditions = [
            "[STATUS] == 200"
            "[BODY].status == UP"
            "[RESPONSE_TIME] < 300"
          ];
        }
      ];
    };
  };
}


================================================
FILE: .gitattributes
================================================
* text=auto eol=lf

================================================
FILE: .github/FUNDING.yml
================================================
github: [TwiN]


================================================
FILE: .github/assets/gatus-diagram.drawio
================================================
<mxfile host="app.diagrams.net" modified="2022-12-07T04:00:31.242Z" agent="5.0 (Windows)" etag="4-CttOJPoGYGt_6RMEMf" version="20.5.3" type="device"><diagram id="oCf8YAkR0GE5Fy88uv5t" name="Page-1">7Vxbc6M2FP41frQHxDWPuW13Z9JpZtKddPdNAQWrBcQKObH76ytsyVxkHOxgC6bOS+BICPl856ZzJCbWbbL8jcJs/jsJUTwBRricWHcTAEzDBfxfQVkJiu9ZG0pEcShoJeEJ/4vko4K6wCHKax0ZITHDWZ0YkDRFAavRIKXkvd7tlcT1t2YwQgrhKYCxSn3GIZsLKjCMsuErwtFcvvoKiJYEyt6CkM9hSN4rJOt+Yt1SQtjmKlneorhgn2TM5rkvLa3bmVGUsi4PfMHmwwP9av/5bDqPP9Obbw/4j6kn5sZW8hejkDNA3BLK5iQiKYzvS+oNJYs0RMWoBr8r+zwQknGiyYl/I8ZWAk24YIST5iyJRSufMF39JZ5f3/wobmaOvL1bVhvvVuLulaRMDGr6/H4z92LCrSwRpJwsaID28EHKFqQRYnv6uVvguMwjkiA+P/4cRTFk+K0+DyhkL9r2K9HhFwKgA8AS477BeCHe9D1HVEGQC1pWXC6S+DpghHJOvSHKMJfqB/iC4keSY4ZJyru8EMZIUulwHeOoaGAFlFXMyILFOEW3Wz0ztgAUz6LlfghUlokHHKEdwkA4UvHfS22zBGle0TPXOBGPry4KUQp6B4XwdSoEsBSNmAA3ZoI1NRzdXwsiG6b5mmnXvINpZ8s152Q7v4qK/zgvWLnMSM6RBcbLSo7MZ7oZfNNvp7ys9ayOMRSaFXAoEN2hcgkOw404IT49+LIer0A5Izhla9Y5NxPnrhX3HXoo3KIYrPRFVYloV4NWpZ0aM9Mz7c1YnaEWwz0WP6ccywQ1AzCV93IE8vqacwlsisp2UsdLj/t54fFbhEcOlGcwlbTv3yoSVG3oKlh1M/M+xww9ZXCtxe88/KqL21pExLNmq3x0t9Om/bGhNsE5LbWpukPKB8boDRWqG0IGi4iPcv+m1aSXVvxHte38Jt3vaNJNY7ckHKbo15TCVaWDsGKtdsB26wJmXzXC2cP684vNDHo1Gb4icjwgYpTEMaJ5DwpbAb5vBbaNoSkwcMcaa9k9Lz6MjpoJHK3Rlvd5hwn2RVs4yWKUFPHRoCOuEv0+Iy7QsrQsQy4LeE4jVuonAvMbozZGOF0EJgW/IlFPfMFa5GRGZUstX7ctNR2Fk6i4hgypfun/Gf7IhcXH8Y+t08pKbRyfV9SGl6UVr0vG6FC8Pru++FwUo/qcjAfvOGfrECTVHV34a/FgUGRo96ZZjw42zI/zO7ZfDzZAL7HGtJ7tOV+yR76pAvszZME8JNGwY41m4mWbMdcWa1haLN4pLZfV0XJZWrPdpprtvo4Lo5MOXISb4fJWXPWFy7bCyvs0LFNUw+Vl0xzo56X8DRVeMoqjaFdGbOS+dD80ltWw1KYxMwzbND3X9gzHd+zDvSUfoPZn197gnst7WpYOkz/AIBc4HV0FaImwzuMqLPuC12F4WS26fSa8nAteB+KldxGpptt6qx0XGVB0eE3YPCw22ZvE7u7zXHtomVBwdTpoEpQQ2rkscXTcCPrBxm9i43XEBpxs5ajmXnrDJiM5i2iR7R4nOralHR01kO8NnfxXzBk8WmwM3dg4ow3oSkh6CRCsrrtYbK1VHGe0AZ02vLTubXBGuxdFG15at8I7oz24oA0vTyteo61qa8NLa63BGW1VWxteWhN+cjl9wasrXo7WBJJrXvA6EC+tCVp5kPWCV2e8gFa8Rluw0oaX1l10lnqy4pGSNxzuON96dM5771G543Pe+kvwdg/nIC878z8E3vHcOvD2yQBV96cEi5ztOMo2tFRpV104WabUVktzIc4DQsPB867rzr7T8U49EosSiOPBc65r1fF0nFPPRkWERDEK5pANnn1m17OAp+Of6v8TyCheDp93tnbeqRVvzjvuWBOSj0D2tFelpdmt8C9lr6vBcw7or0qqFePig0g0XLARsE97wd0BCvvyGAb/DJ91rnbWqSsexILZbDZ83l3p5p2ruRZz/InQnnMjrlhlDfurX57qnpqLe/nZPLl2t3Zsj6KE8dVv5+1RZ16571W2o4+5uS01NDF8ccrtCtS0c9rPR42m9X36Z/yokae5sDCY496dlVtrndVT/VipqXpV8jQfFvP2e0tjBky/oTzW51RSDnO2wzKumrx7QsGC4j6D4ubZMqMVqu6hCvDqoYrbdWl7RGKb35afPt1wvvyErHX/Hw==</diagram></mxfile>

================================================
FILE: .github/codecov.yml
================================================
ignore:
  - "storage/store/sql/specific_postgres.go" # Can't test for postgres
  - "watchdog/endpoint.go"
  - "watchdog/external_endpoint.go"
  - "watchdog/suite.go"
  - "watchdog/watchdog.go"
comment: false
coverage:
  status:
    patch: off
    project:
      default:
        target: 70%
        threshold: null



================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
  - package-ecosystem: "github-actions"
    directory: "/"
    labels: ["dependencies"]
    schedule:
      interval: "daily"
  - package-ecosystem: "gomod"
    directory: "/"
    open-pull-requests-limit: 3
    labels: ["dependencies"]
    schedule:
      interval: "daily"


================================================
FILE: .github/workflows/benchmark.yml
================================================
name: benchmark
on:
  workflow_run:
    workflows: [publish-latest]
    branches: [master]
    types: [completed]
  workflow_dispatch:
    inputs:
      repository:
        description: "Repository to checkout. Useful for benchmarking a fork. Format should be <owner>/<repository>."
        required: true
        default: "TwiN/gatus"
      ref:
        description: "Branch, tag or SHA to checkout"
        required: true
        default: "master"
jobs:
  build:
    name: benchmark
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/setup-go@v6
        with:
          go-version: 1.25.5
          repository: "${{ github.event.inputs.repository || 'TwiN/gatus' }}"
          ref: "${{ github.event.inputs.ref || 'master' }}"
      - uses: actions/checkout@v5
      - name: Benchmark
        run: go test -bench=. ./storage/store


================================================
FILE: .github/workflows/labeler.yml
================================================
name: labeler
on:
  pull_request_target:
    types:
      - opened
  issues:
    types:
      - opened
jobs:
  labeler:
    runs-on: ubuntu-latest
    timeout-minutes: 5
    permissions:
      issues: write
      pull-requests: write
    steps:
      - name: Label
        continue-on-error: true
        env:
          TITLE: ${{ github.event.issue.title }}${{ github.event.pull_request.title }}
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GH_REPO: ${{ github.repository }}
          NUMBER: ${{ github.event.issue.number }}${{ github.event.pull_request.number }}
        run: |
          if [[ $TITLE == "feat"* ]]; then
            gh issue edit "$NUMBER" --add-label "feature"
          elif [[ $TITLE == "fix"* ]]; then
            gh issue edit "$NUMBER" --add-label "bug"
          elif [[ $TITLE == "docs"* ]]; then
            gh issue edit "$NUMBER" --add-label "documentation"
          fi
          if [[ $TITLE == *"alerting"* || $TITLE == *"provider"* || $TITLE == *"alert"* ]]; then
            gh issue edit "$NUMBER" --add-label "area/alerting"
          fi
          if [[ $TITLE == *"(ui)"* || $TITLE == *"ui:"* ]]; then
            gh issue edit "$NUMBER" --add-label "area/ui"
          fi
          if [[ $TITLE == *"storage"* || $TITLE == *"postgres"* || $TITLE == *"sqlite"* ]]; then
            gh issue edit "$NUMBER" --add-label "area/storage"
          fi
          if [[ $TITLE == *"security"* || $TITLE == *"oidc"* || $TITLE == *"oauth2"* ]]; then
            gh issue edit "$NUMBER" --add-label "area/security"
          fi
          if [[ $TITLE == *"metric"* || $TITLE == *"prometheus"* ]]; then
            gh issue edit "$NUMBER" --add-label "area/metrics"
          fi


================================================
FILE: .github/workflows/publish-custom.yml
================================================
name: publish-custom
run-name: "${{ inputs.tag }}"
on:
  workflow_dispatch:
    inputs:
      tag:
        description: Custom tag to publish
      platforms:
        description: Platforms to publish to (comma separated list)
        default: linux/amd64
        type: choice
        options:
          - linux/amd64
          - linux/arm/v7
          - linux/arm64

jobs:
  publish-custom:
    runs-on: ubuntu-latest
    timeout-minutes: 60
    steps:
      - uses: actions/checkout@v5
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v4
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v4
      - name: Get image repository
        run: echo GHCR_IMAGE_REPOSITORY=$(echo ghcr.io/${{ github.actor }}/${{ github.event.repository.name }} | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV
      - name: Login to GitHub Container Registry
        uses: docker/login-action@v4
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: Docker meta
        id: meta
        uses: docker/metadata-action@v6
        with:
          images: ${{ env.GHCR_IMAGE_REPOSITORY }}
          tags: |
            type=raw,value=${{ inputs.tag }}
      - name: Build and push Docker image
        uses: docker/build-push-action@v6
        with:
          platforms: ${{ inputs.platforms }}
          pull: true
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}


================================================
FILE: .github/workflows/publish-experimental.yml
================================================
name: publish-experimental
on: [workflow_dispatch]
jobs:
  publish-experimental:
    runs-on: ubuntu-latest
    timeout-minutes: 60
    steps:
      - uses: actions/checkout@v5
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v4
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v4
      - name: Get image repository
        run: echo IMAGE_REPOSITORY=$(echo ${{ secrets.DOCKER_USERNAME }}/${{ github.event.repository.name }} | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV
      - name: Login to Docker Registry
        uses: docker/login-action@v4
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}
      - name: Docker meta
        id: meta
        uses: docker/metadata-action@v6
        with:
          images: ${{ env.IMAGE_REPOSITORY }}
          tags: |
            type=raw,value=experimental
      - name: Build and push Docker image
        uses: docker/build-push-action@v6
        with:
          platforms: linux/amd64
          pull: true
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}


================================================
FILE: .github/workflows/publish-latest.yml
================================================
name: publish-latest
on:
  workflow_run:
    workflows: [test]
    branches: [master]
    types: [completed]
concurrency:
  group: ${{ github.event.workflow_run.head_repository.full_name }}::${{ github.event.workflow_run.head_branch }}::${{ github.workflow }}
  cancel-in-progress: true
jobs:
  publish-latest:
    runs-on: ubuntu-latest
    if: ${{ (github.event.workflow_run.conclusion == 'success') && (github.event.workflow_run.head_repository.full_name == github.repository) }}
    timeout-minutes: 240
    steps:
      - uses: actions/checkout@v5
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v4
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v4
      - name: Get image repository
        run: |
          echo DOCKER_IMAGE_REPOSITORY=$(echo ${{ secrets.DOCKER_USERNAME }}/${{ github.event.repository.name }} | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV
          echo GHCR_IMAGE_REPOSITORY=$(echo ghcr.io/${{ github.actor }}/${{ github.event.repository.name }} | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV
      - name: Login to Docker Registry
        uses: docker/login-action@v4
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}
      - name: Login to GitHub Container Registry
        uses: docker/login-action@v4
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: Docker meta
        id: meta
        uses: docker/metadata-action@v6
        with:
          images: |
            ${{ env.DOCKER_IMAGE_REPOSITORY }}
            ${{ env.GHCR_IMAGE_REPOSITORY }}
          tags: |
            type=raw,value=latest
      - name: Build and push Docker image
        uses: docker/build-push-action@v6
        with:
          platforms: linux/amd64,linux/arm/v7,linux/arm64
          pull: true
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}


================================================
FILE: .github/workflows/publish-release.yml
================================================
name: publish-release
on:
  release:
    types: [published]
jobs:
  publish-release:
    name: publish-release
    runs-on: ubuntu-latest
    timeout-minutes: 240
    steps:
      - uses: actions/checkout@v5
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v4
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v4
      - name: Get image repository
        run: |
          echo DOCKER_IMAGE_REPOSITORY=$(echo ${{ secrets.DOCKER_USERNAME }}/${{ github.event.repository.name }} | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV
          echo GHCR_IMAGE_REPOSITORY=$(echo ghcr.io/${{ github.actor }}/${{ github.event.repository.name }} | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV
      - name: Get the release
        run: echo RELEASE=${GITHUB_REF/refs\/tags\//} >> $GITHUB_ENV
      - name: Login to Docker Registry
        uses: docker/login-action@v4
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}
      - name: Login to GitHub Container Registry
        uses: docker/login-action@v4
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: Docker meta
        id: meta
        uses: docker/metadata-action@v6
        with:
          images: |
            ${{ env.DOCKER_IMAGE_REPOSITORY }}
            ${{ env.GHCR_IMAGE_REPOSITORY }}
          tags: |
            type=raw,value=${{ env.RELEASE }}
            type=raw,value=stable
            type=raw,value=latest
      - name: Build and push Docker image
        uses: docker/build-push-action@v6
        with:
          platforms: linux/amd64,linux/arm/v7,linux/arm64
          pull: true
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}


================================================
FILE: .github/workflows/regenerate-static-assets.yml
================================================
name: regenerate-static-assets
on:
  issue_comment:
    types: [created]

jobs:
  check-command:
    runs-on: ubuntu-latest
    if: ${{ github.event.issue.pull_request }}
    permissions:
      pull-requests: write # required for adding reactions to command comments on PRs
      checks: read # required to check if all ci checks have passed
    outputs:
      continue: ${{ steps.command.outputs.continue }}
    steps:
      - name: Check command trigger
        id: command
        uses: github/command@v2
        with:
          command: "/regenerate-static-assets"
          permissions: "write,admin" # The allowed permission levels to invoke this command
          allow_forks: true
          allow_drafts: true
          skip_ci: true
          skip_completing: true

  regenerate-static-assets:
    runs-on: ubuntu-latest
    needs: check-command
    if: ${{ needs.check-command.outputs.continue == 'true' }}
    permissions:
      contents: write
    outputs:
      status: ${{ steps.commit.outputs.status }}
    steps:
      - name: Get PR branch
        id: pr
        uses: actions/github-script@v8
        with:
          script: |
            const pr = await github.rest.pulls.get({
              owner: context.repo.owner,
              repo: context.repo.repo,
              pull_number: context.issue.number
            });
            core.setOutput('ref', pr.data.head.ref);
            core.setOutput('repo', pr.data.head.repo.full_name);
      - name: Checkout PR branch
        uses: actions/checkout@v6
        with:
          repository: ${{ steps.pr.outputs.repo }}
          ref: ${{ steps.pr.outputs.ref }}
      - name: Regenerate static assets
        run: |
          make frontend-install-dependencies
          make frontend-build
      - name: Commit and push changes
        id: commit
        run: |
          echo "Checking for changes..."
          if git diff --quiet; then
            echo "No changes detected."
            echo "status=no_changes" >> $GITHUB_OUTPUT
            exit 0
          fi
          git config --global user.name "github-actions[bot]"
          git config --global user.email "github-actions[bot]@users.noreply.github.com"
          echo "Changes detected. Committing and pushing..."
          git add . 
          git commit -m "chore(ui): Regenerate static assets"
          git push origin ${{ steps.pr.outputs.ref }}
          echo "status=success" >> $GITHUB_OUTPUT

  create-response-comment:
    runs-on: ubuntu-latest
    needs: [check-command, regenerate-static-assets]
    if: ${{ !cancelled() && needs.check-command.outputs.continue == 'true' }}
    permissions:
      pull-requests: write
    steps:
      - name: Create response comment
        uses: actions/github-script@v8
        with:
          script: |
            const status = '${{ needs.regenerate-static-assets.outputs.status }}';
            let reaction = 'hooray';
            let message = '';
            var workflowUrl = `${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}`;
            if (status === 'no_changes') {
              message = `@${context.actor} No changes to commit ([ref](${workflowUrl})).`;
            } else {
              reaction = '-1';
              message = `@${context.actor} There was an issue regenerating static assets. Please check the [workflow run logs](${workflowUrl}) for more details.`;
            }
            if (message.length) {
              await github.rest.issues.createComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: context.issue.number,
                body: message
              });
            }
            await github.rest.reactions.createForIssueComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              comment_id: context.payload.comment.id,
              content: reaction
            });


================================================
FILE: .github/workflows/test-ui.yml
================================================
name: test-ui
on:
  pull_request:
    paths:
      - 'web/**'
  push:
    branches:
      - master
    paths:
      - 'web/**'
jobs:
  test-ui:
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
      - uses: actions/checkout@v5
      - run: make frontend-install-dependencies
      - run: make frontend-build

================================================
FILE: .github/workflows/test.yml
================================================
name: test
on:
  pull_request:
    paths-ignore:
      - '*.md'
      - '.examples/**'
  push:
    branches:
      - master
    paths-ignore:
      - '*.md'
      - '.github/**'
      - '.examples/**'
jobs:
  test:
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
      - uses: actions/setup-go@v6
        with:
          go-version: 1.25.5
      - uses: actions/checkout@v5
      - name: Build binary to make sure it works
        run: go build
      - name: Test
        # We're using "sudo" because one of the tests leverages ping, which requires super-user privileges.
        # As for the 'env "PATH=$PATH" "GOROOT=$GOROOT"', we need it to use the same "go" executable that
        # was configured by the "Set up Go" step (otherwise, it'd use sudo's "go" executable)
        run: sudo env "PATH=$PATH" "GOROOT=$GOROOT" go test ./... -race -coverprofile=coverage.txt -covermode=atomic
      - name: Codecov
        uses: codecov/codecov-action@v5.5.2
        with:
          files: ./coverage.txt
          token: ${{ secrets.CODECOV_TOKEN }}


================================================
FILE: .gitignore
================================================
# IDE
*.iml
.idea
.vscode

# OS
.DS_Store

# JS
node_modules

# Go
/vendor

# Misc
*.db
*.db-shm
*.db-wal
gatus
config/config.yml
config.yaml

================================================
FILE: Dockerfile
================================================
# Build the go application into a binary
FROM golang:alpine AS builder
RUN apk --update add ca-certificates
WORKDIR /app
COPY . ./
RUN go mod tidy -diff
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o gatus .

# Run Tests inside docker image if you don't have a configured go environment
#RUN apk update && apk add --virtual build-dependencies build-base gcc
#RUN go test ./... -mod vendor

# Run the binary on an empty container
FROM scratch
COPY --from=builder /app/gatus .
COPY --from=builder /app/config.yaml ./config/config.yaml
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
ENV GATUS_CONFIG_PATH=""
ENV GATUS_LOG_LEVEL="INFO"
ENV PORT="8080"
EXPOSE ${PORT}
ENTRYPOINT ["/gatus"]


================================================
FILE: LICENSE
================================================
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.



================================================
FILE: Makefile
================================================
BINARY=gatus

.PHONY: install
install:
	go build -v -o $(BINARY) .

.PHONY: run
run:
	ENVIRONMENT=dev GATUS_CONFIG_PATH=./config.yaml go run main.go

.PHONY: run-binary
run-binary:
	ENVIRONMENT=dev GATUS_CONFIG_PATH=./config.yaml ./$(BINARY)

.PHONY: clean
clean:
	rm $(BINARY)

.PHONY: test
test:
	go test ./... -cover


##########
# Docker #
##########

docker-build:
	docker build -t twinproduction/gatus:latest .

docker-run:
	docker run -p 8080:8080 --name gatus twinproduction/gatus:latest

docker-build-and-run: docker-build docker-run


#############
# Front end #
#############

frontend-install-dependencies:
	npm --prefix web/app install

frontend-build:
	npm --prefix web/app run build

frontend-run:
	npm --prefix web/app run serve


================================================
FILE: README.md
================================================
[![Gatus](.github/assets/logo-with-dark-text.png)](https://gatus.io)

![test](https://github.com/TwiN/gatus/actions/workflows/test.yml/badge.svg)
[![Go Report Card](https://goreportcard.com/badge/github.com/TwiN/gatus?)](https://goreportcard.com/report/github.com/TwiN/gatus)
[![codecov](https://codecov.io/gh/TwiN/gatus/branch/master/graph/badge.svg)](https://codecov.io/gh/TwiN/gatus)
[![Go version](https://img.shields.io/github/go-mod/go-version/TwiN/gatus.svg)](https://github.com/TwiN/gatus)
[![Docker pulls](https://img.shields.io/docker/pulls/twinproduction/gatus.svg)](https://cloud.docker.com/repository/docker/twinproduction/gatus)
[![Follow TwiN](https://img.shields.io/github/followers/TwiN?label=Follow&style=social)](https://github.com/TwiN)

Gatus is a developer-oriented health dashboard that gives you the ability to monitor your services using HTTP, ICMP, TCP, and even DNS
queries as well as evaluate the result of said queries by using a list of conditions on values like the status code,
the response time, the certificate expiration, the body and many others. The icing on top is that each of these health
checks can be paired with alerting via Slack, Teams, PagerDuty, Discord, Twilio and many more.

I personally deploy it in my Kubernetes cluster and let it monitor the status of my
core applications: https://status.twin.sh/

_Looking for a managed solution? Check out [Gatus.io](https://gatus.io)._

<details>
  <summary><b>Quick start</b></summary>

```console
docker run -p 8080:8080 --name gatus ghcr.io/twin/gatus:stable
```

You can also use Docker Hub if you prefer:
```console
docker run -p 8080:8080 --name gatus twinproduction/gatus:stable
```
For more details, see [Usage](#usage)
</details>

> ❤ Like this project? Please consider [sponsoring me](https://github.com/sponsors/TwiN).

![Gatus dashboard](.github/assets/dashboard-dark.jpg)

Have any feedback or questions? [Create a discussion](https://github.com/TwiN/gatus/discussions/new).


## Table of Contents
- [Table of Contents](#table-of-contents)
- [Why Gatus?](#why-gatus)
- [Features](#features)
- [Usage](#usage)
- [Configuration](#configuration)
  - [Endpoints](#endpoints)
  - [External Endpoints](#external-endpoints)
  - [Suites (ALPHA)](#suites-alpha)
  - [Conditions](#conditions)
    - [Placeholders](#placeholders)
    - [Functions](#functions)
  - [Web](#web)
  - [UI](#ui)
  - [Announcements](#announcements)
  - [Storage](#storage)
  - [Client configuration](#client-configuration)
  - [Tunneling](#tunneling)
  - [Alerting](#alerting)
    - [Configuring AWS SES alerts](#configuring-aws-ses-alerts)
    - [Configuring ClickUp alerts](#configuring-clickup-alerts)
    - [Configuring Datadog alerts](#configuring-datadog-alerts)
    - [Configuring Discord alerts](#configuring-discord-alerts)
    - [Configuring Email alerts](#configuring-email-alerts)
    - [Configuring Gitea alerts](#configuring-gitea-alerts)
    - [Configuring GitHub alerts](#configuring-github-alerts)
    - [Configuring GitLab alerts](#configuring-gitlab-alerts)
    - [Configuring Google Chat alerts](#configuring-google-chat-alerts)
    - [Configuring Gotify alerts](#configuring-gotify-alerts)
    - [Configuring HomeAssistant alerts](#configuring-homeassistant-alerts)
    - [Configuring IFTTT alerts](#configuring-ifttt-alerts)
    - [Configuring Ilert alerts](#configuring-ilert-alerts)
    - [Configuring Incident.io alerts](#configuring-incidentio-alerts)
    - [Configuring Line alerts](#configuring-line-alerts)
    - [Configuring Matrix alerts](#configuring-matrix-alerts)
    - [Configuring Mattermost alerts](#configuring-mattermost-alerts)
    - [Configuring Messagebird alerts](#configuring-messagebird-alerts)
    - [Configuring n8n alerts](#configuring-n8n-alerts)
    - [Configuring New Relic alerts](#configuring-new-relic-alerts)
    - [Configuring Ntfy alerts](#configuring-ntfy-alerts)
    - [Configuring Opsgenie alerts](#configuring-opsgenie-alerts)
    - [Configuring PagerDuty alerts](#configuring-pagerduty-alerts)
    - [Configuring Plivo alerts](#configuring-plivo-alerts)
    - [Configuring Pushover alerts](#configuring-pushover-alerts)
    - [Configuring Rocket.Chat alerts](#configuring-rocketchat-alerts)
    - [Configuring SendGrid alerts](#configuring-sendgrid-alerts)
    - [Configuring Signal alerts](#configuring-signal-alerts)
    - [Configuring SIGNL4 alerts](#configuring-signl4-alerts)
    - [Configuring Slack alerts](#configuring-slack-alerts)
    - [Configuring Splunk alerts](#configuring-splunk-alerts)
    - [Configuring Squadcast alerts](#configuring-squadcast-alerts)
    - [Configuring Teams alerts *(Deprecated)*](#configuring-teams-alerts-deprecated)
    - [Configuring Teams Workflow alerts](#configuring-teams-workflow-alerts)
    - [Configuring Telegram alerts](#configuring-telegram-alerts)
    - [Configuring Twilio alerts](#configuring-twilio-alerts)
    - [Configuring Vonage alerts](#configuring-vonage-alerts)
    - [Configuring Webex alerts](#configuring-webex-alerts)
    - [Configuring Zapier alerts](#configuring-zapier-alerts)
    - [Configuring Zulip alerts](#configuring-zulip-alerts)
    - [Configuring custom alerts](#configuring-custom-alerts)
    - [Setting a default alert](#setting-a-default-alert)
  - [Maintenance](#maintenance)
  - [Security](#security)
    - [Basic Authentication](#basic-authentication)
    - [OIDC](#oidc)
  - [TLS Encryption](#tls-encryption)
  - [Metrics](#metrics)
    - [Custom Labels](#custom-labels)
  - [Connectivity](#connectivity)
  - [Remote instances (EXPERIMENTAL)](#remote-instances-experimental)
- [Deployment](#deployment)
  - [Docker](#docker)
  - [Helm Chart](#helm-chart)
  - [Terraform](#terraform)
    - [Kubernetes](#kubernetes)
- [Running the tests](#running-the-tests)
- [Using in Production](#using-in-production)
- [FAQ](#faq)
  - [Sending a GraphQL request](#sending-a-graphql-request)
  - [Recommended interval](#recommended-interval)
  - [Default timeouts](#default-timeouts)
  - [Monitoring a TCP endpoint](#monitoring-a-tcp-endpoint)
  - [Monitoring a UDP endpoint](#monitoring-a-udp-endpoint)
  - [Monitoring a SCTP endpoint](#monitoring-a-sctp-endpoint)
  - [Monitoring a WebSocket endpoint](#monitoring-a-websocket-endpoint)
  - [Monitoring an endpoint using gRPC](#monitoring-an-endpoint-using-grpc)
  - [Monitoring an endpoint using ICMP](#monitoring-an-endpoint-using-icmp)
  - [Monitoring an endpoint using DNS queries](#monitoring-an-endpoint-using-dns-queries)
  - [Monitoring an endpoint using SSH](#monitoring-an-endpoint-using-ssh)
  - [Monitoring an endpoint using STARTTLS](#monitoring-an-endpoint-using-starttls)
  - [Monitoring an endpoint using TLS](#monitoring-an-endpoint-using-tls)
  - [Monitoring domain expiration](#monitoring-domain-expiration)
  - [Concurrency](#concurrency)
  - [Reloading configuration on the fly](#reloading-configuration-on-the-fly)
  - [Endpoint groups](#endpoint-groups)
  - [How do I sort by group by default?](#how-do-i-sort-by-group-by-default)
  - [Exposing Gatus on a custom path](#exposing-gatus-on-a-custom-path)
  - [Exposing Gatus on a custom port](#exposing-gatus-on-a-custom-port)
  - [Use environment variables in config files](#use-environment-variables-in-config-files)
  - [Configuring a startup delay](#configuring-a-startup-delay)
  - [Keeping your configuration small](#keeping-your-configuration-small)
  - [Proxy client configuration](#proxy-client-configuration)
  - [How to fix 431 Request Header Fields Too Large error](#how-to-fix-431-request-header-fields-too-large-error)
  - [Badges](#badges)
    - [Uptime](#uptime)
    - [Health](#health)
    - [Health (Shields.io)](#health-shieldsio)
    - [Response time](#response-time)
    - [Response time (chart)](#response-time-chart)
      - [How to change the color thresholds of the response time badge](#how-to-change-the-color-thresholds-of-the-response-time-badge)
  - [API](#api)
    - [Interacting with the API programmatically](#interacting-with-the-api-programmatically)
    - [Raw Data](#raw-data)
      - [Uptime](#uptime-1)
      - [Response Time](#response-time-1)
  - [Installing as binary](#installing-as-binary)
  - [High level design overview](#high-level-design-overview)


## Why Gatus?
Before getting into the specifics, I want to address the most common question:
> Why would I use Gatus when I can just use Prometheus’ Alertmanager, Cloudwatch or even Splunk?

Neither of these can tell you that there’s a problem if there are no clients actively calling the endpoint.
In other words, it's because monitoring metrics mostly rely on existing traffic, which effectively means that unless
your clients are already experiencing a problem, you won't be notified.

Gatus, on the other hand, allows you to configure health checks for each of your features, which in turn allows it to
monitor these features and potentially alert you before any clients are impacted.

A sign you may want to look into Gatus is by simply asking yourself whether you'd receive an alert if your load balancer
was to go down right now. Will any of your existing alerts be triggered? Your metrics won’t report an increase in errors
if no traffic makes it to your applications. This puts you in a situation where your clients are the ones
that will notify you about the degradation of your services rather than you reassuring them that you're working on
fixing the issue before they even know about it.


## Features
The main features of Gatus are:

- **Highly flexible health check conditions**: While checking the response status may be enough for some use cases, Gatus goes much further and allows you to add conditions on the response time, the response body and even the IP address.
- **Ability to use Gatus for user acceptance tests**: Thanks to the point above, you can leverage this application to create automated user acceptance tests.
- **Very easy to configure**: Not only is the configuration designed to be as readable as possible, it's also extremely easy to add a new service or a new endpoint to monitor.
- **Alerting**: While having a pretty visual dashboard is useful to keep track of the state of your application(s), you probably don't want to stare at it all day. Thus, notifications via Slack, Mattermost, Messagebird, PagerDuty, Twilio, Google chat and Teams are supported out of the box with the ability to configure a custom alerting provider for any needs you might have, whether it be a different provider or a custom application that manages automated rollbacks.
- **Metrics**
- **Low resource consumption**: As with most Go applications, the resource footprint that this application requires is negligibly small.
- **[Badges](#badges)**: ![Uptime 7d](https://status.twin.sh/api/v1/endpoints/core_blog-external/uptimes/7d/badge.svg) ![Response time 24h](https://status.twin.sh/api/v1/endpoints/core_blog-external/response-times/24h/badge.svg)
- **Dark mode**

![Gatus dashboard conditions](.github/assets/dashboard-conditions.jpg)


## Usage

```console
docker run -p 8080:8080 --name gatus ghcr.io/twin/gatus:stable
```

You can also use Docker Hub if you prefer:
```console
docker run -p 8080:8080 --name gatus twinproduction/gatus:stable
```
If you want to create your own configuration, see [Docker](#docker) for information on how to mount a configuration file.

Here's a simple example:
```yaml
endpoints:
  - name: website                 # Name of your endpoint, can be anything
    url: "https://twin.sh/health"
    interval: 5m                  # Duration to wait between every status check (default: 60s)
    conditions:
      - "[STATUS] == 200"         # Status must be 200
      - "[BODY].status == UP"     # The json path "$.status" must be equal to UP
      - "[RESPONSE_TIME] < 300"   # Response time must be under 300ms

  - name: make-sure-header-is-rendered
    url: "https://example.org/"
    interval: 60s
    conditions:
      - "[STATUS] == 200"                          # Status must be 200
      - "[BODY] == pat(*<h1>Example Domain</h1>*)" # Body must contain the specified header
```

This example would look similar to this:

![Simple example](.github/assets/example.jpg)

If you want to test it locally, see [Docker](#docker).

## Configuration
By default, the configuration file is expected to be at `config/config.yaml`.

You can specify a custom path by setting the `GATUS_CONFIG_PATH` environment variable.

If `GATUS_CONFIG_PATH` points to a directory, all `*.yaml` and `*.yml` files inside said directory and its
subdirectories are merged like so:
- All maps/objects are deep merged (i.e. you could define `alerting.slack` in one file and `alerting.pagerduty` in another file)
- All slices/arrays are appended (i.e. you can define `endpoints` in multiple files and each endpoint will be added to the final list of endpoints)
- Parameters with a primitive value (e.g. `metrics`, `alerting.slack.webhook-url`, etc.) may only be defined once to forcefully avoid any ambiguity
    - To clarify, this also means that you could not define `alerting.slack.webhook-url` in two files with different values. All files are merged into one before they are processed. This is by design.

> 💡 You can also use environment variables in the configuration file (e.g. `$DOMAIN`, `${DOMAIN}`)
>
> ⚠️ When your configuration parameter contains a `$` symbol, you have to escape `$` with `$$`.
>
> See [Use environment variables in config files](#use-environment-variables-in-config-files) or [examples/docker-compose-postgres-storage/config/config.yaml](.examples/docker-compose-postgres-storage/config/config.yaml) for examples.

If you want to test it locally, see [Docker](#docker).


## Configuration
| Parameter                    | Description                                                                                                                              | Default       |
|:-----------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------|:--------------|
| `metrics`                    | Whether to expose metrics at `/metrics`.                                                                                                 | `false`       |
| `storage`                    | [Storage configuration](#storage).                                                                                                       | `{}`          |
| `alerting`                   | [Alerting configuration](#alerting).                                                                                                     | `{}`          |
| `announcements`              | [Announcements configuration](#announcements).                                                                                           | `[]`          |
| `endpoints`                  | [Endpoints configuration](#endpoints).                                                                                                   | Required `[]` |
| `external-endpoints`         | [External Endpoints configuration](#external-endpoints).                                                                                 | `[]`          |
| `security`                   | [Security configuration](#security).                                                                                                     | `{}`          |
| `concurrency`                | Maximum number of endpoints/suites to monitor concurrently. Set to `0` for unlimited. See [Concurrency](#concurrency).                   | `3`           |
| `disable-monitoring-lock`    | Whether to [disable the monitoring lock](#disable-monitoring-lock). **Deprecated**: Use `concurrency: 0` instead.                        | `false`       |
| `skip-invalid-config-update` | Whether to ignore invalid configuration update. <br />See [Reloading configuration on the fly](#reloading-configuration-on-the-fly).     | `false`       |
| `web`                        | [Web configuration](#web).                                                                                                               | `{}`          |
| `ui`                         | [UI configuration](#ui).                                                                                                                 | `{}`          |
| `maintenance`                | [Maintenance configuration](#maintenance).                                                                                               | `{}`          |

If you want more verbose logging, you may set the `GATUS_LOG_LEVEL` environment variable to `DEBUG`.
Conversely, if you want less verbose logging, you can set the aforementioned environment variable to `WARN`, `ERROR` or `FATAL`.
The default value for `GATUS_LOG_LEVEL` is `INFO`.

### Endpoints
Endpoints are URLs, applications, or services that you want to monitor. Each endpoint has a list of conditions that are
evaluated on an interval that you define. If any condition fails, the endpoint is considered as unhealthy.
You can then configure alerts to be triggered when an endpoint is unhealthy once a certain threshold is reached.

| Parameter                                       | Description                                                                                                                                 | Default                    |
|:------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------|
| `endpoints`                                     | List of endpoints to monitor.                                                                                                               | Required `[]`              |
| `endpoints[].enabled`                           | Whether to monitor the endpoint.                                                                                                            | `true`                     |
| `endpoints[].name`                              | Name of the endpoint. Can be anything.                                                                                                      | Required `""`              |
| `endpoints[].group`                             | Group name. Used to group multiple endpoints together on the dashboard. <br />See [Endpoint groups](#endpoint-groups).                      | `""`                       |
| `endpoints[].url`                               | URL to send the request to.                                                                                                                 | Required `""`              |
| `endpoints[].method`                            | Request method.                                                                                                                             | `GET`                      |
| `endpoints[].conditions`                        | Conditions used to determine the health of the endpoint. <br />See [Conditions](#conditions).                                               | `[]`                       |
| `endpoints[].interval`                          | Duration to wait between every status check.                                                                                                | `60s`                      |
| `endpoints[].graphql`                           | Whether to wrap the body in a query param (`{"query":"$body"}`).                                                                            | `false`                    |
| `endpoints[].body`                              | Request body.                                                                                                                               | `""`                       |
| `endpoints[].headers`                           | Request headers.                                                                                                                            | `{}`                       |
| `endpoints[].dns`                               | Configuration for an endpoint of type DNS. <br />See [Monitoring an endpoint using DNS queries](#monitoring-an-endpoint-using-dns-queries). | `""`                       |
| `endpoints[].dns.query-type`                    | Query type (e.g. MX).                                                                                                                       | `""`                       |
| `endpoints[].dns.query-name`                    | Query name (e.g. example.com).                                                                                                              | `""`                       |
| `endpoints[].ssh`                               | Configuration for an endpoint of type SSH. <br />See [Monitoring an endpoint using SSH](#monitoring-an-endpoint-using-ssh).                 | `""`                       |
| `endpoints[].ssh.username`                      | SSH username (e.g. example).                                                                                                                | Required `""`              |
| `endpoints[].ssh.password`                      | SSH password (e.g. password).                                                                                                               | Required `""`              |
| `endpoints[].alerts`                            | List of all alerts for a given endpoint. <br />See [Alerting](#alerting).                                                                   | `[]`                       |
| `endpoints[].maintenance-windows`               | List of all maintenance windows for a given endpoint. <br />See [Maintenance](#maintenance).                                                | `[]`                       |
| `endpoints[].client`                            | [Client configuration](#client-configuration).                                                                                              | `{}`                       |
| `endpoints[].ui`                                | UI configuration at the endpoint level.                                                                                                     | `{}`                       |
| `endpoints[].ui.hide-conditions`                | Whether to hide conditions from the results. Note that this only hides conditions from results evaluated from the moment this was enabled.  | `false`                    |
| `endpoints[].ui.hide-hostname`                  | Whether to hide the hostname from the results.                                                                                              | `false`                    |
| `endpoints[].ui.hide-port`                      | Whether to hide the port from the results.                                                                                                  | `false`                    |
| `endpoints[].ui.hide-url`                       | Whether to hide the URL from the results. Useful if the URL contains a token.                                                               | `false`                    |
| `endpoints[].ui.hide-errors`                    | Whether to hide errors from the results.                                                                                                    | `false`                    |
| `endpoints[].ui.dont-resolve-failed-conditions` | Whether to resolve failed conditions for the UI.                                                                                            | `false`                    |
| `endpoints[].ui.resolve-successful-conditions`  | Whether to resolve successful conditions for the UI (helpful to expose body assertions even when checks pass).                              | `false`                    |
| `endpoints[].ui.badge.response-time`            | List of response time thresholds. Each time a threshold is reached, the badge has a different color.                                        | `[50, 200, 300, 500, 750]` |
| `endpoints[].extra-labels`                      | Extra labels to add to the metrics. Useful for grouping endpoints together.                                                                 | `{}`                       |
| `endpoints[].always-run`                        | (SUITES ONLY) Whether to execute this endpoint even if previous endpoints in the suite failed.                                              | `false`                    |
| `endpoints[].store`                             | (SUITES ONLY) Map of values to extract from the response and store in the suite context (stored even on failure).                           | `{}`                       |

You may use the following placeholders in the body (`endpoints[].body`):
- `[ENDPOINT_NAME]` (resolved from `endpoints[].name`)
- `[ENDPOINT_GROUP]` (resolved from `endpoints[].group`)
- `[ENDPOINT_URL]` (resolved from `endpoints[].url`)
- `[LOCAL_ADDRESS]` (resolves to the local IP and port like `192.0.2.1:25` or `[2001:db8::1]:80`)
- `[RANDOM_STRING_N]` (resolves to a random string of numbers and letters of length N (max: 8192))

### External Endpoints
Unlike regular endpoints, external endpoints are not monitored by Gatus, but they are instead pushed programmatically.
This allows you to monitor anything you want, even when what you want to check lives in an environment that would not normally be accessible by Gatus.

For instance:
- You can create your own agent that lives in a private network and pushes the status of your services to a publicly-exposed Gatus instance
- You can monitor services that are not supported by Gatus
- You can implement your own monitoring system while using Gatus as the dashboard

| Parameter                                 | Description                                                                                                                       | Default        |
|:------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------|:---------------|
| `external-endpoints`                      | List of endpoints to monitor.                                                                                                     | `[]`           |
| `external-endpoints[].enabled`            | Whether to monitor the endpoint.                                                                                                  | `true`         |
| `external-endpoints[].name`               | Name of the endpoint. Can be anything.                                                                                            | Required `""`  |
| `external-endpoints[].group`              | Group name. Used to group multiple endpoints together on the dashboard. <br />See [Endpoint groups](#endpoint-groups).            | `""`           |
| `external-endpoints[].token`              | Bearer token required to push status to.                                                                                          | Required `""`  |
| `external-endpoints[].alerts`             | List of all alerts for a given endpoint. <br />See [Alerting](#alerting).                                                         | `[]`           |
| `external-endpoints[].heartbeat`          | Heartbeat configuration for monitoring when the external endpoint stops sending updates.                                          | `{}`           |
| `external-endpoints[].heartbeat.interval` | Expected interval between updates. If no update is received within this interval, alerts will be triggered. Must be at least 10s. | `0` (disabled) |

Example:
```yaml
external-endpoints:
  - name: ext-ep-test
    group: core
    token: "potato"
    heartbeat:
      interval: 30m  # Automatically create a failure if no update is received within 30 minutes
    alerts:
      - type: discord
        description: "healthcheck failed"
        send-on-resolved: true
```

To push the status of an external endpoint, you can use [gatus-cli](https://github.com/TwiN/gatus-cli):
```
gatus-cli external-endpoint push --url https://status.example.org --key "core_ext-ep-test" --token "potato" --success
```

or send an HTTP request:
```
POST /api/v1/endpoints/{key}/external?success={success}&error={error}&duration={duration}
```
Where:
- `{key}` has the pattern `<GROUP_NAME>_<ENDPOINT_NAME>` in which both variables have ` `, `/`, `_`, `,`, `.`, `#`, `+` and `&` replaced by `-`.
  - Using the example configuration above, the key would be `core_ext-ep-test`.
- `{success}` is a boolean (`true` or `false`) value indicating whether the health check was successful or not.
- `{error}` (optional): a string describing the reason for a failed health check. If {success} is false, this should contain the error message; if the check is successful, this will be ignored.
- `{duration}` (optional): the time that the request took as a duration string (e.g. 10s).

You must also pass the token as a `Bearer` token in the `Authorization` header.


### Suites (ALPHA)
Suites are collections of endpoints that are executed sequentially with a shared context.
This allows you to create complex monitoring scenarios where the result from one endpoint can be used in subsequent endpoints, enabling workflow-style monitoring.

Here are a few cases in which suites could be useful:
- Testing multi-step authentication flows (login -> access protected resource -> logout)
- API workflows where you need to chain requests (create resource -> update -> verify -> delete)
- Monitoring business processes that span multiple services
- Validating data consistency across multiple endpoints

| Parameter                         | Description                                                                                         | Default       |
|:----------------------------------|:----------------------------------------------------------------------------------------------------|:--------------|
| `suites`                          | List of suites to monitor.                                                                          | `[]`          |
| `suites[].enabled`                | Whether to monitor the suite.                                                                       | `true`        |
| `suites[].name`                   | Name of the suite. Must be unique.                                                                  | Required `""` |
| `suites[].group`                  | Group name. Used to group multiple suites together on the dashboard.                                | `""`          |
| `suites[].interval`               | Duration to wait between suite executions.                                                          | `10m`         |
| `suites[].timeout`                | Maximum duration for the entire suite execution.                                                    | `5m`          |
| `suites[].context`                | Initial context values that can be referenced by endpoints.                                         | `{}`          |
| `suites[].endpoints`              | List of endpoints to execute sequentially.                                                          | Required `[]` |
| `suites[].endpoints[].store`      | Map of values to extract from the response and store in the suite context (stored even on failure). | `{}`          |
| `suites[].endpoints[].always-run` | Whether to execute this endpoint even if previous endpoints in the suite failed.                    | `false`       |

**Note**: Suite-level alerts are not supported yet. Configure alerts on individual endpoints within the suite instead.

#### Using Context in Endpoints
Once values are stored in the context, they can be referenced in subsequent endpoints:
- In the URL: `https://api.example.com/users/[CONTEXT].user_id`
- In headers: `Authorization: Bearer [CONTEXT].auth_token`
- In the body: `{"user_id": "[CONTEXT].user_id"}`
- In conditions: `[BODY].server_ip == [CONTEXT].server_ip`

Note that context/store keys are limited to A-Z, a-z, 0-9, underscores (`_`), and hyphens (`-`).

#### Example Suite Configuration
```yaml
suites:
  - name: item-crud-workflow
    group: api-tests
    interval: 5m
    context:
      price: "19.99"  # Initial static value in context
    endpoints:
      # Step 1: Create an item and store the item ID
      - name: create-item
        url: https://api.example.com/items
        method: POST
        body: '{"name": "Test Item", "price": "[CONTEXT].price"}'
        conditions:
          - "[STATUS] == 201"
          - "len([BODY].id) > 0"
          - "[BODY].price == [CONTEXT].price"
        store:
          itemId: "[BODY].id"
        alerts:
          - type: slack
            description: "Failed to create item"

      # Step 2: Update the item using the stored item ID
      - name: update-item
        url: https://api.example.com/items/[CONTEXT].itemId
        method: PUT
        body: '{"price": "24.99"}'
        conditions:
          - "[STATUS] == 200"
        alerts:
          - type: slack
            description: "Failed to update item"

      # Step 3: Fetch the item and validate the price
      - name: get-item
        url: https://api.example.com/items/[CONTEXT].itemId
        method: GET
        conditions:
          - "[STATUS] == 200"
          - "[BODY].price == 24.99"
        alerts:
          - type: slack
            description: "Item price did not update correctly"

      # Step 4: Delete the item (always-run: true to ensure cleanup even if step 2 or 3 fails)
      - name: delete-item
        url: https://api.example.com/items/[CONTEXT].itemId
        method: DELETE
        always-run: true
        conditions:
          - "[STATUS] == 204"
        alerts:
          - type: slack
            description: "Failed to delete item"
```

The suite will be considered successful only if all required endpoints pass their conditions.


### Conditions
Here are some examples of conditions you can use:

| Condition                        | Description                                         | Passing values             | Failing values   |
|:---------------------------------|:----------------------------------------------------|:---------------------------|------------------|
| `[STATUS] == 200`                | Status must be equal to 200                         | 200                        | 201, 404, ...    |
| `[STATUS] < 300`                 | Status must lower than 300                          | 200, 201, 299              | 301, 302, ...    |
| `[STATUS] <= 299`                | Status must be less than or equal to 299            | 200, 201, 299              | 301, 302, ...    |
| `[STATUS] > 400`                 | Status must be greater than 400                     | 401, 402, 403, 404         | 400, 200, ...    |
| `[STATUS] == any(200, 429)`      | Status must be either 200 or 429                    | 200, 429                   | 201, 400, ...    |
| `[CONNECTED] == true`            | Connection to host must've been successful          | true                       | false            |
| `[RESPONSE_TIME] < 500`          | Response time must be below 500ms                   | 100ms, 200ms, 300ms        | 500ms, 501ms     |
| `[IP] == 127.0.0.1`              | Target IP must be 127.0.0.1                         | 127.0.0.1                  | 0.0.0.0          |
| `[BODY] == 1`                    | The body must be equal to 1                         | 1                          | `{}`, `2`, ...   |
| `[BODY].user.name == john`       | JSONPath value of `$.user.name` is equal to `john`  | `{"user":{"name":"john"}}` |                  |
| `[BODY].data[0].id == 1`         | JSONPath value of `$.data[0].id` is equal to 1      | `{"data":[{"id":1}]}`      |                  |
| `[BODY].age == [BODY].id`        | JSONPath value of `$.age` is equal JSONPath `$.id`  | `{"age":1,"id":1}`         |                  |
| `len([BODY].data) < 5`           | Array at JSONPath `$.data` has less than 5 elements | `{"data":[{"id":1}]}`      |                  |
| `len([BODY].name) == 8`          | String at JSONPath `$.name` has a length of 8       | `{"name":"john.doe"}`      | `{"name":"bob"}` |
| `has([BODY].errors) == false`    | JSONPath `$.errors` does not exist                  | `{"name":"john.doe"}`      | `{"errors":[]}`  |
| `has([BODY].users) == true`      | JSONPath `$.users` exists                           | `{"users":[]}`             | `{}`             |
| `[BODY].name == pat(john*)`      | String at JSONPath `$.name` matches pattern `john*` | `{"name":"john.doe"}`      | `{"name":"bob"}` |
| `[BODY].id == any(1, 2)`         | Value at JSONPath `$.id` is equal to `1` or `2`     | 1, 2                       | 3, 4, 5          |
| `[CERTIFICATE_EXPIRATION] > 48h` | Certificate expiration is more than 48h away        | 49h, 50h, 123h             | 1h, 24h, ...     |
| `[DOMAIN_EXPIRATION] > 720h`     | The domain must expire in more than 720h            | 4000h                      | 1h, 24h, ...     |


#### Placeholders
| Placeholder                | Description                                                                               | Example of resolved value                    |
|:---------------------------|:------------------------------------------------------------------------------------------|:---------------------------------------------|
| `[STATUS]`                 | Resolves into the HTTP status of the request                                              | `404`                                        |
| `[RESPONSE_TIME]`          | Resolves into the response time the request took, in ms                                   | `10`                                         |
| `[IP]`                     | Resolves into the IP of the target host                                                   | `192.168.0.232`                              |
| `[BODY]`                   | Resolves into the response body. Supports JSONPath.                                       | `{"name":"john.doe"}`                        |
| `[CONNECTED]`              | Resolves into whether a connection could be established                                   | `true`                                       |
| `[CERTIFICATE_EXPIRATION]` | Resolves into the duration before certificate expiration (valid units are "s", "m", "h".) | `24h`, `48h`, 0 (if not protocol with certs) |
| `[DOMAIN_EXPIRATION]`      | Resolves into the duration before the domain expires (valid units are "s", "m", "h".)     | `24h`, `48h`, `1234h56m78s`                  |
| `[DNS_RCODE]`              | Resolves into the DNS status of the response                                              | `NOERROR`                                    |


#### Functions
| Function | Description                                                                                                                                                                                                                         | Example                            |
|:---------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------|
| `len`    | If the given path leads to an array, returns its length. Otherwise, the JSON at the given path is minified and converted to a string, and the resulting number of characters is returned. Works only with the `[BODY]` placeholder. | `len([BODY].username) > 8`         |
| `has`    | Returns `true` or `false` based on whether a given path is valid. Works only with the `[BODY]` placeholder.                                                                                                                         | `has([BODY].errors) == false`      |
| `pat`    | Specifies that the string passed as parameter should be evaluated as a pattern. Works only with `==` and `!=`.                                                                                                                      | `[IP] == pat(192.168.*)`           |
| `any`    | Specifies that any one of the values passed as parameters is a valid value. Works only with `==` and `!=`.                                                                                                                          | `[BODY].ip == any(127.0.0.1, ::1)` |

> 💡 Use `pat` only when you need to. `[STATUS] == pat(2*)` is a lot more expensive than `[STATUS] < 300`.

### Web
Allows you to configure how and where the dashboard is being served.

| Parameter                  | Description                                                                                 | Default   |
|:---------------------------|:--------------------------------------------------------------------------------------------|:----------|
| `web`                      | Web configuration                                                                           | `{}`      |
| `web.address`              | Address to listen on.                                                                       | `0.0.0.0` |
| `web.port`                 | Port to listen on.                                                                          | `8080`    |
| `web.read-buffer-size`     | Buffer size for reading requests from a connection. Also limit for the maximum header size. | `8192`    |
| `web.tls.certificate-file` | Optional public certificate file for TLS in PEM format.                                     | `""`      |
| `web.tls.private-key-file` | Optional private key file for TLS in PEM format.                                            | `""`      |

### UI
Allows you to configure the application wide defaults for the dashboard's UI. Some of these parameters can be overridden locally by users using the local storage of their browser.

| Parameter                 | Description                                                                                                                              | Default                                             |
|:--------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------|
| `ui`                      | UI configuration                                                                                                                         | `{}`                                                |
| `ui.title`                | [Title of the document](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title).                                                | `Health Dashboard ǀ Gatus`                          |
| `ui.description`          | Meta description for the page.                                                                                                           | `Gatus is an advanced...`.                          |
| `ui.dashboard-heading`    | Dashboard title between header and endpoints                                                                                             | `Health Dashboard`                                  |
| `ui.dashboard-subheading` | Dashboard description between header and endpoints                                                                                       | `Monitor the health of your endpoints in real-time` |
| `ui.header`               | Header at the top of the dashboard.                                                                                                      | `Gatus`                                             |
| `ui.logo`                 | URL to the logo to display.                                                                                                              | `""`                                                |
| `ui.link`                 | Link to open when the logo is clicked.                                                                                                   | `""`                                                |
| `ui.favicon.default`      | Favourite default icon to display in web browser tab or address bar.                                                                     | `/favicon.ico`                                      |
| `ui.favicon.size16x16`    | Favourite icon to display in web browser for 16x16 size.                                                                                 | `/favicon-16x16.png`                                |
| `ui.favicon.size32x32`    | Favourite icon to display in web browser for 32x32 size.                                                                                 | `/favicon-32x32.png`                                |
| `ui.buttons`              | List of buttons to display below the header.                                                                                             | `[]`                                                |
| `ui.buttons[].name`       | Text to display on the button.                                                                                                           | Required `""`                                       |
| `ui.buttons[].link`       | Link to open when the button is clicked.                                                                                                 | Required `""`                                       |
| `ui.custom-css`           | Custom CSS                                                                                                                               | `""`                                                |
| `ui.dark-mode`            | Whether to enable dark mode by default. Note that this is superseded by the user's operating system theme preferences.                   | `true`                                              |
| `ui.default-sort-by`      | Default sorting option for endpoints in the dashboard. Can be `name`, `group`, or `health`. Note that user preferences override this.    | `name`                                              |
| `ui.default-filter-by`    | Default filter option for endpoints in the dashboard. Can be `none`, `failing`, or `unstable`. Note that user preferences override this. | `none`                                              |

### Announcements
System-wide announcements allow you to display important messages at the top of the status page. These can be used to inform users about planned maintenance, ongoing issues, or general information. You can use markdown to format your announcements.

This is essentially what some status page calls "incident communications".

| Parameter                   | Description                                                                                                              | Default  |
|:----------------------------|:-------------------------------------------------------------------------------------------------------------------------|:---------|
| `announcements`             | List of announcements to display                                                                                         | `[]`     |
| `announcements[].timestamp` | UTC timestamp when the announcement was made (RFC3339 format)                                                            | Required |
| `announcements[].type`      | Type of announcement. Valid values: `outage`, `warning`, `information`, `operational`, `none`                            | `"none"` |
| `announcements[].message`   | The message to display to users                                                                                          | Required |
| `announcements[].archived`  | Whether to archive the announcement. Archived announcements show at the bottom of the status page instead of at the top. | `false`  |

Types:
- **outage**: Indicates service disruptions or critical issues (red theme)
- **warning**: Indicates potential issues or important notices (yellow theme)
- **information**: General information or updates (blue theme)
- **operational**: Indicates resolved issues or normal operations (green theme)
- **none**: Neutral announcements with no specific severity (gray theme, default if none are specified)

Example Configuration:
```yaml
announcements:
  - timestamp: 2025-11-07T14:00:00Z
    type: outage
    message: "Scheduled maintenance on database servers from 14:00 to 16:00 UTC"
  - timestamp: 2025-11-07T16:15:00Z
    type: operational
    message: "Database maintenance completed successfully. All systems operational."
  - timestamp: 2025-11-07T12:00:00Z
    type: information
    message: "New monitoring dashboard features will be deployed next week"
  - timestamp: 2025-11-06T09:00:00Z
    type: warning
    message: "Elevated API response times observed for US customers"
    archived: true
```

If at least one announcement is archived, a **Past Announcements** section will be rendered at the bottom of the status page:
![Gatus past announcements section](.github/assets/past-announcements.jpg)


### Storage
| Parameter                           | Description                                                                                                                                        | Default    |
|:------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------|:-----------|
| `storage`                           | Storage configuration                                                                                                                              | `{}`       |
| `storage.path`                      | Path to persist the data in. Only supported for types `sqlite` and `postgres`.                                                                     | `""`       |
| `storage.type`                      | Type of storage. Valid types: `memory`, `sqlite`, `postgres`.                                                                                      | `"memory"` |
| `storage.caching`                   | Whether to use write-through caching. Improves loading time for large dashboards. <br />Only supported if `storage.type` is `sqlite` or `postgres` | `false`    |
| `storage.maximum-number-of-results` | The maximum number of results that an endpoint can have                                                                                            | `100`      |
| `storage.maximum-number-of-events`  | The maximum number of events that an endpoint can have                                                                                             | `50`       |

The results for each endpoint health check as well as the data for uptime and the past events must be persisted
so that they can be displayed on the dashboard. These parameters allow you to configure the storage in question.

- If `storage.type` is `memory` (default):
```yaml
# Note that this is the default value, and you can omit the storage configuration altogether to achieve the same result.
# Because the data is stored in memory, the data will not survive a restart.
storage:
  type: memory
  maximum-number-of-results: 200
  maximum-number-of-events: 5
```
- If `storage.type` is `sqlite`, `storage.path` must not be blank:
```yaml
storage:
  type: sqlite
  path: data.db
```
See [examples/docker-compose-sqlite-storage](.examples/docker-compose-sqlite-storage) for an example.

- If `storage.type` is `postgres`, `storage.path` must be the connection URL:
```yaml
storage:
  type: postgres
  path: "postgres://user:password@127.0.0.1:5432/gatus?sslmode=disable"
```
See [examples/docker-compose-postgres-storage](.examples/docker-compose-postgres-storage) for an example.


### Client configuration
In order to support a wide range of environments, each monitored endpoint has a unique configuration for
the client used to send the request.

| Parameter                              | Description                                                                   | Default         |
|:---------------------------------------|:------------------------------------------------------------------------------|:----------------|
| `client.insecure`                      | Whether to skip verifying the server's certificate chain and host name.       | `false`         |
| `client.ignore-redirect`               | Whether to ignore redirects (true) or follow them (false, default).           | `false`         |
| `client.timeout`                       | Duration before timing out.                                                   | `10s`           |
| `client.dns-resolver`                  | Override the DNS resolver using the format `{proto}://{host}:{port}`.         | `""`            |
| `client.oauth2`                        | OAuth2 client configuration.                                                  | `{}`            |
| `client.oauth2.token-url`              | The token endpoint URL                                                        | required `""`   |
| `client.oauth2.client-id`              | The client id which should be used for the `Client credentials flow`          | required `""`   |
| `client.oauth2.client-secret`          | The client secret which should be used for the `Client credentials flow`      | required `""`   |
| `client.oauth2.scopes[]`               | A list of `scopes` which should be used for the `Client credentials flow`.    | required `[""]` |
| `client.proxy-url`                     | The URL of the proxy to use for the client                                    | `""`            |
| `client.identity-aware-proxy`          | Google Identity-Aware-Proxy client configuration.                             | `{}`            |
| `client.identity-aware-proxy.audience` | The Identity-Aware-Proxy audience. (client-id of the IAP oauth2 credential)   | required `""`   |
| `client.tls.certificate-file`          | Path to a client certificate (in PEM format) for mTLS configurations.         | `""`            |
| `client.tls.private-key-file`          | Path to a client private key (in PEM format) for mTLS configurations.         | `""`            |
| `client.tls.renegotiation`             | Type of renegotiation support to provide. (`never`, `freely`, `once`).        | `"never"`       |
| `client.network`                       | The network to use for ICMP endpoint client (`ip`, `ip4` or `ip6`).           | `"ip"`          |
| `client.tunnel`                        | Name of the SSH tunnel to use for this endpoint. See [Tunneling](#tunneling). | `""`            |


> 📝 Some of these parameters are ignored based on the type of endpoint. For instance, there's no certificate involved
> in ICMP requests (ping), therefore, setting `client.insecure` to `true` for an endpoint of that type will not do anything.

This default configuration is as follows:

```yaml
client:
  insecure: false
  ignore-redirect: false
  timeout: 10s
```

Note that this configuration is only available under `endpoints[]`, `alerting.mattermost` and `alerting.custom`.

Here's an example with the client configuration under `endpoints[]`:

```yaml
endpoints:
  - name: website
    url: "https://twin.sh/health"
    client:
      insecure: false
      ignore-redirect: false
      timeout: 10s
    conditions:
      - "[STATUS] == 200"
```

This example shows how you can specify a custom DNS resolver:

```yaml
endpoints:
  - name: with-custom-dns-resolver
    url: "https://your.health.api/health"
    client:
      dns-resolver: "tcp://8.8.8.8:53"
    conditions:
      - "[STATUS] == 200"
```

This example shows how you can use the `client.oauth2` configuration to query a backend API with `Bearer token`:

```yaml
endpoints:
  - name: with-custom-oauth2
    url: "https://your.health.api/health"
    client:
      oauth2:
        token-url: https://your-token-server/token
        client-id: 00000000-0000-0000-0000-000000000000
        client-secret: your-client-secret
        scopes: ['https://your.health.api/.default']
    conditions:
      - "[STATUS] == 200"
```

This example shows how you can use the `client.identity-aware-proxy` configuration to query a backend API with `Bearer token` using Google Identity-Aware-Proxy:

```yaml
endpoints:
  - name: with-custom-iap
    url: "https://my.iap.protected.app/health"
    client:
      identity-aware-proxy:
        audience: "XXXXXXXX-XXXXXXXXXXXX.apps.googleusercontent.com"
    conditions:
      - "[STATUS] == 200"
```

> 📝 Note that Gatus will use the [gcloud default credentials](https://cloud.google.com/docs/authentication/application-default-credentials) within its environment to generate the token.

This example shows you how you can use the `client.tls` configuration to perform an mTLS query to a backend API:

```yaml
endpoints:
  - name: website
    url: "https://your.mtls.protected.app/health"
    client:
      tls:
        certificate-file: /path/to/user_cert.pem
        private-key-file: /path/to/user_key.pem
        renegotiation: once
    conditions:
      - "[STATUS] == 200"
```

> 📝 Note that if running in a container, you must volume mount the certificate and key into the container.

### Tunneling
Gatus supports SSH tunneling to monitor internal services through jump hosts or bastion servers.
This is particularly useful for monitoring services that are not directly accessible from where Gatus is deployed.

SSH tunnels are defined globally in the `tunneling` section and then referenced by name in endpoint client configurations.

| Parameter                             | Description                                                 | Default       |
|:--------------------------------------|:------------------------------------------------------------|:--------------|
| `tunneling`                           | SSH tunnel configurations                                   | `{}`          |
| `tunneling.<tunnel-name>`             | Configuration for a named SSH tunnel                        | `{}`          |
| `tunneling.<tunnel-name>.type`        | Type of tunnel (currently only `SSH` is supported)          | Required `""` |
| `tunneling.<tunnel-name>.host`        | SSH server hostname or IP address                           | Required `""` |
| `tunneling.<tunnel-name>.port`        | SSH server port                                             | `22`          |
| `tunneling.<tunnel-name>.username`    | SSH username                                                | Required `""` |
| `tunneling.<tunnel-name>.password`    | SSH password (use either this or private-key)               | `""`          |
| `tunneling.<tunnel-name>.private-key` | SSH private key in PEM format (use either this or password) | `""`          |
| `client.tunnel`                       | Name of the tunnel to use for this endpoint                 | `""`          |

```yaml
tunneling:
  production:
    type: SSH
    host: "jumphost.example.com"
    username: "monitoring"
    private-key: |
      -----BEGIN RSA PRIVATE KEY-----
      MIIEpAIBAAKCAQEA...
      -----END RSA PRIVATE KEY-----

endpoints:
  - name: "internal-api"
    url: "http://internal-api.example.com:8080/health"
    client:
      tunnel: "production"
    conditions:
      - "[STATUS] == 200"
```

> ⚠️ **WARNING**:: Tunneling may introduce additional latency, especially if the connection to the tunnel is retried frequently.
> This may lead to inaccurate response time measurements.


### Alerting
Gatus supports multiple alerting providers, such as Slack and PagerDuty, and supports different alerts for each
individual endpoints with configurable descriptions and thresholds.

Alerts are configured at the endpoint level like so:

| Parameter                            | Description                                                                                                                                               | Default       |
|:-------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------|
| `alerts`                             | List of all alerts for a given endpoint.                                                                                                                  | `[]`          |
| `alerts[].type`                      | Type of alert. <br />See table below for all valid types.                                                                                                 | Required `""` |
| `alerts[].enabled`                   | Whether to enable the alert.                                                                                                                              | `true`        |
| `alerts[].failure-threshold`         | Number of failures in a row needed before triggering the alert.                                                                                           | `3`           |
| `alerts[].success-threshold`         | Number of successes in a row before an ongoing incident is marked as resolved.                                                                            | `2`           |
| `alerts[].minimum-reminder-interval` | Minimum time interval between alert reminders. E.g. `"30m"`, `"1h45m30s"` or `"24h"`. If empty or `0`, reminders are disabled. Cannot be lower than `5m`. | `0`           |
| `alerts[].send-on-resolved`          | Whether to send a notification once a triggered alert is marked as resolved.                                                                              | `false`       |
| `alerts[].description`               | Description of the alert. Will be included in the alert sent.                                                                                             | `""`          |
| `alerts[].provider-override`         | Alerting provider configuration override for the given alert type                                                                                         | `{}`          |

Here's an example of what an alert configuration might look like at the endpoint level:
```yaml
endpoints:
  - name: example
    url: "https://example.org"
    conditions:
      - "[STATUS] == 200"
    alerts:
      - type: slack
        description: "healthcheck failed"
        send-on-resolved: true
```

You can also override global provider configuration by using `alerts[].provider-override`, like so:
```yaml
endpoints:
  - name: example
    url: "https://example.org"
    conditions:
      - "[STATUS] == 200"
    alerts:
      - type: slack
        provider-override:
          webhook-url: "https://hooks.slack.com/services/**********/**********/**********"
```

> 📝 If an alerting provider is not properly configured, all alerts configured with the provider's type will be
> ignored.

| Parameter                  | Description                                                                                                                             | Default |
|:---------------------------|:----------------------------------------------------------------------------------------------------------------------------------------|:--------|
| `alerting.awsses`          | Configuration for alerts of type `awsses`. <br />See [Configuring AWS SES alerts](#configuring-aws-ses-alerts).                         | `{}`    |
| `alerting.clickup`         | Configuration for alerts of type `clickup`. <br />See [Configuring ClickUp alerts](#configuring-clickup-alerts).                        | `{}`    |
| `alerting.custom`          | Configuration for custom actions on failure or alerts. <br />See [Configuring Custom alerts](#configuring-custom-alerts).               | `{}`    |
| `alerting.datadog`         | Configuration for alerts of type `datadog`. <br />See [Configuring Datadog alerts](#configuring-datadog-alerts).                        | `{}`    |
| `alerting.discord`         | Configuration for alerts of type `discord`. <br />See [Configuring Discord alerts](#configuring-discord-alerts).                        | `{}`    |
| `alerting.email`           | Configuration for alerts of type `email`. <br />See [Configuring Email alerts](#configuring-email-alerts).                              | `{}`    |
| `alerting.gitea`           | Configuration for alerts of type `gitea`. <br />See [Configuring Gitea alerts](#configuring-gitea-alerts).                              | `{}`    |
| `alerting.github`          | Configuration for alerts of type `github`. <br />See [Configuring GitHub alerts](#configuring-github-alerts).                           | `{}`    |
| `alerting.gitlab`          | Configuration for alerts of type `gitlab`. <br />See [Configuring GitLab alerts](#configuring-gitlab-alerts).                           | `{}`    |
| `alerting.googlechat`      | Configuration for alerts of type `googlechat`. <br />See [Configuring Google Chat alerts](#configuring-google-chat-alerts).             | `{}`    |
| `alerting.gotify`          | Configuration for alerts of type `gotify`. <br />See [Configuring Gotify alerts](#configuring-gotify-alerts).                           | `{}`    |
| `alerting.homeassistant`   | Configuration for alerts of type `homeassistant`. <br />See [Configuring HomeAssistant alerts](#configuring-homeassistant-alerts).      | `{}`    |
| `alerting.ifttt`           | Configuration for alerts of type `ifttt`. <br />See [Configuring IFTTT alerts](#configuring-ifttt-alerts).                              | `{}`    |
| `alerting.ilert`           | Configuration for alerts of type `ilert`. <br />See [Configuring ilert alerts](#configuring-ilert-alerts).                              | `{}`    |
| `alerting.incident-io`     | Configuration for alerts of type `incident-io`. <br />See [Configuring Incident.io alerts](#configuring-incidentio-alerts).             | `{}`    |
| `alerting.line`            | Configuration for alerts of type `line`. <br />See [Configuring Line alerts](#configuring-line-alerts).                                 | `{}`    |
| `alerting.matrix`          | Configuration for alerts of type `matrix`. <br />See [Configuring Matrix alerts](#configuring-matrix-alerts).                           | `{}`    |
| `alerting.mattermost`      | Configuration for alerts of type `mattermost`. <br />See [Configuring Mattermost alerts](#configuring-mattermost-alerts).               | `{}`    |
| `alerting.messagebird`     | Configuration for alerts of type `messagebird`. <br />See [Configuring Messagebird alerts](#configuring-messagebird-alerts).            | `{}`    |
| `alerting.n8n`             | Configuration for alerts of type `n8n`. <br />See [Configuring n8n alerts](#configuring-n8n-alerts).                                    | `{}`    |
| `alerting.newrelic`        | Configuration for alerts of type `newrelic`. <br />See [Configuring New Relic alerts](#configuring-new-relic-alerts).                   | `{}`    |
| `alerting.ntfy`            | Configuration for alerts of type `ntfy`. <br />See [Configuring Ntfy alerts](#configuring-ntfy-alerts).                                 | `{}`    |
| `alerting.opsgenie`        | Configuration for alerts of type `opsgenie`. <br />See [Configuring Opsgenie alerts](#configuring-opsgenie-alerts).                     | `{}`    |
| `alerting.pagerduty`       | Configuration for alerts of type `pagerduty`. <br />See [Configuring PagerDuty alerts](#configuring-pagerduty-alerts).                  | `{}`    |
| `alerting.plivo`           | Configuration for alerts of type `plivo`. <br />See [Configuring Plivo alerts](#configuring-plivo-alerts).                              | `{}`    |
| `alerting.pushover`        | Configuration for alerts of type `pushover`. <br />See [Configuring Pushover alerts](#configuring-pushover-alerts).                     | `{}`    |
| `alerting.rocketchat`      | Configuration for alerts of type `rocketchat`. <br />See [Configuring Rocket.Chat alerts](#configuring-rocketchat-alerts).              | `{}`    |
| `alerting.sendgrid`        | Configuration for alerts of type `sendgrid`. <br />See [Configuring SendGrid alerts](#configuring-sendgrid-alerts).                     | `{}`    |
| `alerting.signal`          | Configuration for alerts of type `signal`. <br />See [Configuring Signal alerts](#configuring-signal-alerts).                           | `{}`    |
| `alerting.signl4`          | Configuration for alerts of type `signl4`. <br />See [Configuring SIGNL4 alerts](#configuring-signl4-alerts).                           | `{}`    |
| `alerting.slack`           | Configuration for alerts of type `slack`. <br />See [Configuring Slack alerts](#configuring-slack-alerts).                              | `{}`    |
| `alerting.splunk`          | Configuration for alerts of type `splunk`. <br />See [Configuring Splunk alerts](#configuring-splunk-alerts).                           | `{}`    |
| `alerting.squadcast`       | Configuration for alerts of type `squadcast`. <br />See [Configuring Squadcast alerts](#configuring-squadcast-alerts).                  | `{}`    |
| `alerting.teams`           | Configuration for alerts of type `teams`. *(Deprecated)* <br />See [Configuring Teams alerts](#configuring-teams-alerts-deprecated).    | `{}`    |
| `alerting.teams-workflows` | Configuration for alerts of type `teams-workflows`. <br />See [Configuring Teams Workflow alerts](#configuring-teams-workflow-alerts).  | `{}`    |
| `alerting.telegram`        | Configuration for alerts of type `telegram`. <br />See [Configuring Telegram alerts](#configuring-telegram-alerts).                     | `{}`    |
| `alerting.twilio`          | Settings for alerts of type `twilio`. <br />See [Configuring Twilio alerts](#configuring-twilio-alerts).                                | `{}`    |
| `alerting.vonage`          | Configuration for alerts of type `vonage`. <br />See [Configuring Vonage alerts](#configuring-vonage-alerts).                           | `{}`    |
| `alerting.webex`           | Configuration for alerts of type `webex`. <br />See [Configuring Webex alerts](#configuring-webex-alerts).                              | `{}`    |
| `alerting.zapier`          | Configuration for alerts of type `zapier`. <br />See [Configuring Zapier alerts](#configuring-zapier-alerts).                           | `{}`    |
| `alerting.zulip`           | Configuration for alerts of type `zulip`. <br />See [Configuring Zulip alerts](#configuring-zulip-alerts).                              | `{}`    |


#### Configuring AWS SES alerts
| Parameter                            | Description                                                                                | Default       |
|:-------------------------------------|:-------------------------------------------------------------------------------------------|:--------------|
| `alerting.aws-ses`                   | Settings for alerts of type `aws-ses`                                                      | `{}`          |
| `alerting.aws-ses.access-key-id`     | AWS Access Key ID                                                                          | Optional `""` |
| `alerting.aws-ses.secret-access-key` | AWS Secret Access Key                                                                      | Optional `""` |
| `alerting.aws-ses.region`            | AWS Region                                                                                 | Required `""` |
| `alerting.aws-ses.from`              | The Email address to send the emails from (should be registered in SES)                    | Required `""` |
| `alerting.aws-ses.to`                | Comma separated list of email address to notify                                            | Required `""` |
| `alerting.aws-ses.default-alert`     | Default alert configuration. <br />See [Setting a default alert](#setting-a-default-alert) | N/A           |
| `alerting.aws-ses.overrides`         | List of overrides that may be prioritized over the default configuration                   | `[]`          |
| `alerting.aws-ses.overrides[].group` | Endpoint group for which the configuration will be overridden by this configuration        | `""`          |
| `alerting.aws-ses.overrides[].*`     | See `alerting.aws-ses.*` parameters                                                        | `{}`          |

```yaml
alerting:
  aws-ses:
    access-key-id: "..."
    secret-access-key: "..."
    region: "us-east-1"
    from: "status@example.com"
    to: "user@example.com"

endpoints:
  - name: website
    interval: 30s
    url: "https://twin.sh/health"
    conditions:
      - "[STATUS] == 200"
      - "[BODY].status == UP"
      - "[RESPONSE_TIME] < 300"
    alerts:
      - type: aws-ses
        failure-threshold: 5
        send-on-resolved: true
        description: "healthcheck failed"
```

If the `access-key-id` and `secret-access-key` are not defined Gatus will fall back to IAM authentication.

Make sure you have the ability to use `ses:SendEmail`.


#### Configuring ClickUp alerts

| Parameter                          | Description                                                                                | Default       |
| :--------------------------------- | :----------------------------------------------------------------------------------------- | :------------ |
| `alerting.clickup`                 | Configuration for alerts of type `clickup`                                                 | `{}`          |
| `alerting.clickup.list-id`         | ClickUp List ID where tasks will be created                                                | Required `""` |
| `alerting.clickup.token`           | ClickUp API token                                                                          | Required `""` |
| `alerting.clickup.api-url`         | Custom API URL                   | `https://api.clickup.com/api/v2`          |
| `alerting.clickup.assignees`       | List of user IDs to assign tasks to                                                        | `[]`          |
| `alerting.clickup.status`          | Initial status for created tasks                                                           | `""`          |
| `alerting.clickup.priority`        | Priority level: `urgent`, `high`, `normal`, `low`, or `none`                               | `normal`      |
| `alerting.clickup.notify-all`      | Whether to notify all assignees when task is created                                       | `true`        |
| `alerting.clickup.name`            | Custom task name template (supports placeholders)                                          | `Health Check: [ENDPOINT_GROUP]:[ENDPOINT_NAME]`          |
| `alerting.clickup.content`         | Custom task content template (supports placeholders)                                       | `Triggered: [ENDPOINT_GROUP] - [ENDPOINT_NAME] - [ALERT_DESCRIPTION] - [RESULT_ERRORS]`          |
| `alerting.clickup.default-alert`   | Default alert configuration. <br />See [Setting a default alert](#setting-a-default-alert) | N/A           |
| `alerting.clickup.overrides`       | List of overrides that may be prioritized over the default configuration                   | `[]`          |
| `alerting.clickup.overrides[].group` | Endpoint group for which the configuration will be overridden by this configuration      | `""`          |
| `alerting.clickup.overrides[].*`   | See `alerting.clickup.*` parameters                                                        | `{}`          |

The ClickUp alerting provider creates tasks in a ClickUp list when alerts are triggered. If `send-on-resolved` is set to `true` on the endpoint alert, the task will be automatically closed when the alert is resolved.

The following placeholders are supported in `name` and `content`:

-   `[ENDPOINT_GROUP]` - Resolved from `endpoints[].group`
-   `[ENDPOINT_NAME]` - Resolved from `endpoints[].name`
-   `[ALERT_DESCRIPTION]` - Resolved from `endpoints[].alerts[].description`
-   `[RESULT_ERRORS]` - Resolved from the health evaluation errors

```yaml
alerting:
  clickup:
    list-id: "123456789"
    token: "pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    assignees:
      - "12345"
      - "67890"
    status: "in progress"
    priority: high
    name: "Health Check Alert: [ENDPOINT_GROUP] - [ENDPOINT_NAME]"
    content: "Alert triggered for [ENDPOINT_GROUP] - [ENDPOINT_NAME] - [ALERT_DESCRIPTION] - [RESULT_ERRORS]"

endpoints:
  - name: website
    url: "https://twin.sh/health"
    interval: 5m
    conditions:
      - "[STATUS] == 200"
    alerts:
      - type: clickup
        send-on-resolved: true
```

To get your ClickUp API token follow: [Generate or regenerate a Personal API Token](https://developer.clickup.com/docs/authentication#:~:text=the%20API%20docs.-,Generate%20or%20regenerate%20a%20Personal%20API%20Token,-Log%20in%20to)

To find your List ID:

1. Open the ClickUp list where you want tasks to be created
2. The List ID is in the URL: `https://app.clickup.com/{workspace_id}/v/l/li/{list_id}`

To find Assignee IDs:

1. Go to `https://app.clickup.com/{workspace_id}/teams-pulse/teams/people`
2. Hover over a team member
3. Click the 3 dots (overflow menu)
3. Click `Copy member ID`

#### Configuring Datadog alerts

> ⚠️ **WARNING**: This alerting provider has not been tested yet. If you've tested it and confirmed that it works, please remove this warning and create a pull request, or comment on [#1223](https://github.com/TwiN/gatus/discussions/1223) with whether the provider works as intended. Thank you for your cooperation.

| Parameter                            | Description                                                                                | Default           |
|:-------------------------------------|:-------------------------------------------------------------------------------------------|:------------------|
| `alerting.datadog`                   | Configuration for alerts of type `datadog`                                                 | `{}`              |
| `alerting.datadog.api-key`           | Datadog API key                                                                            | Required `""`     |
| `alerting.datadog.site`              | Datadog site (e.g., datadoghq.com, datadoghq.eu)                                           | `"datadoghq.com"` |
| `alerting.datadog.tags`              | Additional tags to include                                                                 | `[]`              |
| `alerting.datadog.default-alert`     | Default alert configuration. <br />See [Setting a default alert](#setting-a-default-alert) | N/A               |
| `alerting.datadog.overrides`         | List of overrides that may be prioritized over the default configuration                   | `[]`              |
| `alerting.datadog.overrides[].group` | Endpoint group for which the configuration will be overridden by this configuration        | `""`              |
| `alerting.datadog.overrides[].*`     | See `alerting.datadog.*` parameters                                                        | `{}`              |

```yaml
alerting:
  datadog:
    api-key: "YOUR_API_KEY"
    site: "datadoghq.com"  # or datadoghq.eu for EU region
    tags:
      - "environment:production"
      - "team:platform"

endpoints:
  - name: website
    url: "https://twin.sh/health"
    interval: 5m
    conditions:
      - "[STATUS] == 200"
    alerts:
      - type: datadog
        send-on-resolved: true
```


#### Configuring Discord alerts
| Parameter                            | Description                                                                                | Default                             |
|:-------------------------------------|:-------------------------------------------------------------------------------------------|:------------------------------------|
| `alerting.discord`                   | Configuration for alerts of type `discord`                                                 | `{}`                                |
| `alerting.discord.webhook-url`       | Discord Webhook URL                                                                        | Required `""`                       |
| `alerting.discord.title`             | Title of the notification                                                                  | `":helmet_with_white_cross: Gatus"` |
| `alerting.discord.message-content`   | Message content to send before the embed (useful for pinging users/roles, e.g. `<@123>`)   | `""`                                |
| `alerting.discord.default-alert`     | Default alert configuration. <br />See [Setting a default alert](#setting-a-default-alert) | N/A                                 |
| `alerting.discord.overrides`         | List of overrides that may be prioritized over the default configuration                   | `[]`                                |
| `alerting.discord.overrides[].group` | Endpoint group for which the configuration will be overridden by this configuration        | `""`                                |
| `alerting.discord.overrides[].*`     | See `alerting.discord.*` parameters                                                        | `{}`                                |

```yaml
alerting:
  discord:
    webhook-url: "https://discord.com/api/webhooks/**********/**********"

endpoints:
  - name: website
    url: "https://twin.sh/health"
    interval: 5m
    conditions:
      - "[STATUS] == 200"
      - "[BODY].status == UP"
      - "[RESPONSE_TIME] < 300"
    alerts:
      - type: discord
        description: "healthcheck failed"
        send-on-resolved: true
```


#### Configuring Email alerts
| Parameter                          | Description                                                                                   | Default       |
|:-----------------------------------|:----------------------------------------------------------------------------------------------|:--------------|
| `alerting.email`                   | Configuration for alerts of type `email`                                                      | `{}`          |
| `alerting.email.from`              | Email used to send the alert                                                                  | Required `""` |
| `alerting.email.username`          | Username of the SMTP server used to send the alert. If empty, uses `alerting.email.from`.     | `""`          |
| `alerting.email.password`          | Password of the SMTP server used to send the alert. If empty, no authentication is performed. | `""`          |
| `alerting.email.host`              | Host of the mail server (e.g. `smtp.gmail.com`)                                               | Required `""` |
| `alerting.email.port`              | Port the mail server is listening to (e.g. `587`)                                             | Required `0`  |
| `alerting.email.to`                | Email(s) to send the alerts to                                                                | Required `""` |
| `alerting.email.default-alert`     | Default alert configuration. <br />See [Setting a default alert](#setting-a-default-alert)    | N/A           |
| `alerting.email.client.insecure`   | Whether to skip TLS verification                                                              | `false`       |
| `alerting.email.overrides`         | List of overrides that may be prioritized over the default configuration                      | `[]`          |
| `alerting.email.overrides[].group` | Endpoint group for which the configuration will be overridden by this configuration           | `""`          |
| `alerting.email.overrides[].*`     | See `alerting.email.*` parameters                                                             | `{}`          |

```yaml
alerting:
  email:
    from: "from@example.com"
    username: "from@example.com"
    password: "hunter2"
    host: "mail.example.com"
    port: 587
    to: "recipient1@example.com,recipient2@example.com"
    client:
      insecure: false
    # You can also add group-specific to keys, which will
    # override the to key above for the specified groups
    overrides:
      - group: "core"
        to: "recipient3@example.com,recipient4@example.com"

endpoints:
  - name: website
    url: "https://twin.sh/health"
    interval: 5m
    conditions:
      - "[STATUS] == 200"
      - "[BODY].status == UP"
      - "[RESPONSE_TIME] < 300"
    alerts:
      - type: email
        description: "healthcheck failed"
        send-on-resolved: true

  - name: back-end
    group: core
    url: "https://example.org/"
    interval: 5m
    conditions:
      - "[STATUS] == 200"
      - "[CERTIFICATE_EXPIRATION] > 48h"
    alerts:
      - type: email
        description: "healthcheck failed"
        send-on-resolved: true
```

> ⚠ Some mail servers are painfully slow.


#### Configuring Gitea alerts

| Parameter                       | Description                                                                                                | Default       |
|:--------------------------------|:-----------------------------------------------------------------------------------------------------------|:--------------|
| `alerting.gitea`                | Configuration for alerts of type `gitea`                                                                   | `{}`          |
| `alerting.gitea.repository-url` | Gitea repository URL (e.g. `https://gitea.com/TwiN/example`)                                               | Required `""` |
| `alerting.gitea.token`          | Personal access token to use for authentication. <br />Must have at least RW on issues and RO on metadata. | Required `""` |
| `alerting.gitea.default-alert`  | Default alert configuration. <br />See [Setting a default alert](#setting-a-default-alert).                | N/A           |

The Gitea alerting provider creates an issue prefixed with `alert(gatus):` and suffixed with the endpoint's display
name for each alert. If `send-on-resolved` is set to `true` on the endpoint alert, the issue will be automatically
closed when the alert is resolved.

```yaml
alerting:
  gitea:
    repository-url: "https://gitea.com/TwiN/test"
    token: "349d63f16......"

endpoints:
  - name: example
    url: "https://twin.sh/health"
    interval: 5m
    conditions:
      - "[STATUS] == 200"
      - "[BODY].status == UP"
      - "[RESPONSE_TIME] < 75"
    alerts:
      - type: gitea
        failure-threshold: 2
        success-threshold: 3
        send-on-resolved: true
        description: "Everything's burning AAAAAHHHHHHHHHHHHHHH"
```

![Gitea alert](.github/assets/gitea-alerts.png)


#### Configuring GitHub alerts

| Parameter                        | Description                                                                                                | Default       |
|:---------------------------------|:-----------------------------------------------------------------------------------------------------------|:--------------|
| `alerting.github`                | Configuration for alerts of type `github`                                                                  | `{}`          |
| `alerting.github.repository-url` | GitHub repository URL (e.g. `https://github.com/TwiN/example`)                                             | Required `""` |
| `alerting.github.token`          | Personal access token to use for authentication. <br />Must have at least RW on issues and RO on metadata. | Required `""` |
| `alerting.github.default-alert`  | Default alert configuration. <br />See [Setting a default alert](#setting-a-default-alert).                | N/A           |

The GitHub alerting provider creates an issue prefixed with `alert(gatus):` and suffixed with the endpoint's display
name for each alert. If `send-on-resolved` is set to `true` on the endpoint alert, the issue will be automatically
closed when the alert is resolved.

```yaml
alerting:
  github:
    repository-url: "https://github.com/TwiN/test"
    token: "github_pat_12345..."

endpoints:
  - name: example
    url: "https://twin.sh/health"
    interval: 5m
    conditions:
      - "[STATUS] == 200"
      - "[BODY].status == UP"
      - "[RESPONSE_TIME] < 75"
    alerts:
      - type: github
        failure-threshold: 2
        success-threshold: 3
        send-on-resolved: true
        description: "Everything's burning AAAAAHHHHHHHHHHHHHHH"
```

![GitHub alert](.github/assets/github-alerts.png)


#### Configuring GitLab alerts
| Parameter                           | Description                                                                                                         | Default       |
|:------------------------------------|:--------------------------------------------------------------------------------------------------------------------|:--------------|
| `alerting.gitlab`                   | Configuration for alerts of type `gitlab`                                                                           | `{}`          |
| `alerting.gitlab.webhook-url`       | GitLab alert webhook URL (e.g. `https://gitlab.com/yourusername/example/alerts/notify/gatus/xxxxxxxxxxxxxxxx.json`) | Required `""` |
| `alerting.gitlab.authorization-key` | GitLab alert authorization key.                                                                                     | Required `""` |
| `alerting.gitlab.severity`          | Override default severity (critical), can be one of `critical, high, medium, low, info, unknown`                    | `""`          |
| `alerting.gitlab.monitoring-tool`   | Override the monitoring tool name (gatus)                                                                           | `"gatus"`     |
| `alerting.gitlab.environment-name`  | Set gitlab environment's name. Required to display alerts on a dashboard.                                           | `""`          |
| `alerting.gitlab.service`           | Override endpoint display name                                                                                      | `""`          |
| `alerting.gitlab.default-alert`     | Default alert configuration. <br />See [Setting a default alert](#setting-a-default-alert).                         | N/A           |

The GitLab alerting provider creates an alert prefixed with `alert(gatus):` and suffixed with the endpoint's display
name for each alert. If `send-on-resolved` is set to `true` on the endpoint alert, the alert will be automatically
closed when the alert is resolved. See
https://docs.gitlab.com/ee/operations/incident_management/integrations.html#configuration to configure the endpoint.

```yaml
alerting:
  gitlab:
    webhook-url: "https://gitlab.com/hlidotbe/example/alerts/notify/gatus/xxxxxxxxxxxxxxxx.json"
    authorization-key: "12345"

endpoints:
  - name: example
    url: "https://twin.sh/health"
    interval: 5m
    conditions:
      - "[STATUS] == 200"
      - "[BODY].status == UP"
      - "[RESPONSE_TIME] < 75"
    alerts:
      - type: gitlab
        failure-threshold: 2
        success-threshold: 3
        send-on-resolved: true
        description: "Everything's burning AAAAAHHHHHHHHHHHHHHH"
```

![GitLab alert](.github/assets/gitlab-alerts.png)


#### Configuring Google Chat alerts
| Parameter                               | Description                                                                                 | Default       |
|:----------------------------------------|:--------------------------------------------------------------------------------------------|:--------------|
| `alerting.googlechat`                   | Configuration for alerts of type `googlechat`                                               | `{}`          |
| `alerting.googlechat.webhook-url`       | Google Chat Webhook URL                                                                     | Required `""` |
| `alerting.googlechat.client`            | Client configuration. <br />See [Client configuration](#client-configuration).              | `{}`          |
| `alerting.googlechat.default-alert`     | Default alert configuration. <br />See [Setting a default alert](#setting-a-default-alert). | N/A           |
| `alerting.googlechat.overrides`         | List of overrides that may be prioritized over the default configuration                    | `[]`          |
| `alerting.googlechat.overrides[].group` | Endpoint group for which the configuration will be overridden by this configuration         | `""`          |
| `alerting.googlechat.overrides[].*`     | See `alerting.googlechat.*` parameters                                                      | `{}`          |

```yaml
alerting:
  googlechat:
    webhook-url: "https://chat.googleapis.com/v1/spaces/*******/messages?key=**********&token=********"

endpoints:
  - name: website
    url: "https://twin.sh/health"
    interval: 5m
    conditions:
      - "[STATUS] == 200"
      - "[BODY].status == UP"
      - "[RESPONSE_TIME] < 300"
    alerts:
      - type: googlechat
        description: "healthcheck failed"
        send-on-resolved: true
```


#### Configuring Gotify alerts
| Parameter                                     | Description                                                                                 | Default               |
|:----------------------------------------------|:--------------------------------------------------------------------------------------------|:----------------------|
| `alerting.gotify`                             | Configuration for alerts of type `gotify`                                                   | `{}`                  |
| `alerting.gotify.server-url`                  | Gotify server URL                                                                           | Required `""`         |
| `alerting.gotify.token`                       | Token that is used for authentication.                                                      | Required `""`         |
| `alerting.gotify.priority`                    | Priority of the alert according to Gotify standards.                                        | `5`                   |
| `alerting.gotify.title`                       | Title of the notification                                                                   | `"Gatus: <endpoint>"` |
| `alerting.gotify.default-alert`               | Default alert configuration. <br />See [Setting a default alert](#setting-a-default-alert). | N/A                   |

```yaml
alerting:
  gotify:
    server-url: "https://gotify.example"
    token: "**************"

endpoints:
  - name: website
    url: "https://twin.sh/health"
    interval: 5m
    conditions:
      - "[STATUS] == 200"
      - "[BODY].status == UP"
      - "[RESPONSE_TIME] < 300"
    alerts:
      - type: gotify
        description: "healthcheck failed"
        send-on-resolved: true
```

Here's an example of what the notifications look like:

![Gotify notifications](.github/assets/gotify-alerts.png)


#### Configuring HomeAssistant alerts
| Parameter                                  | Description                                                                            | Default Value |
|:-------------------------------------------|:---------------------------------------------------------------------------------------|:--------------|
| `alerting.homeassistant.url`               | HomeAssistant instance URL                                                             | Required `""` |
| `alerting.homeassistant.token`             | Long-lived access token from HomeAssistant                                             | Required `""` |
| `alerting.homeassistant.default-alert`     | Default alert configuration to use for endpoints with an alert of the appropriate type | `{}`          |
| `alerting.homeassistant.overrides`         | List of overrides that may be prioritized over the default configuration               | `[]`          |
| `alerting.homeassistant.overrides[].group` | Endpoint group for which the configuration will be overridden by this configuration    | `""`          |
| `alerting.homeassistant.overrides[].*`     | See `alerting.homeassistant.*` parameters                                              | `{}`          |

```yaml
alerting:
  homeassistant:
    url: "http://homeassistant:8123"  # URL of your HomeAssistant instance
    token: "YOUR_LONG_LIVED_ACCESS_TOKEN"  # Long-lived access token from HomeAssistant

endpoints:
  - name: my-service
    url: "https://my-service.com"
    interval: 5m
    conditions:
      - "[STATUS] == 200"
    alerts:
      - type: homeassistant
        enabled: true
        send-on-resolved: true
        description: "My service health check"
        failure-threshold: 3
        success-threshold: 2
```

The alerts will be sent as events to HomeAssistant with the event type `gatus_alert`. The event data includes:
- `status`: "triggered" or "resolved"
- `endpoint`: The name of the monitored endpoint
- `description`: The alert description if provided
- `conditions`: List of conditions and their results
- `failure_count`: Number of consecutive failures (when triggered)
- `success_count`: Number of consecutive successes (when resolved)

You can use these events in HomeAssistant automations to:
- Send notifications
- Control devices
- Trigger scenes
- Log to history
- And more

Example HomeAssistant automation:
```yaml
automation:
  - alias: "Gatus Alert Handler"
    trigger:
      platform: event
      event_type: gatus_alert
    action:
      - service: notify.notify
        data_template:
          title: "Gatus Alert: {{ trigger.event.data.event_data.endpoint }}"
          message: >
            Status: {{ trigger.event.data.event_data.status }}
            {% if trigger.event.data.event_data.description %}
            Description: {{ trigger.event.data.event_data.description }}
            {% endif %}
            {% for condition in trigger.event.data.event_data.conditions %}
            {{ '✅' if condition.success else '❌' }} {{ condition.condition }}
            {% endfor %}
```

To get your HomeAssistant long-lived access token:
1. Open HomeAssistant
2. Click on your profile name (bottom left)
3. Scroll down to "Long-Lived Access Tokens"
4. Click "Create Token"
5. Give it a name (e.g., "Gatus")
6. Copy the token - you'll only see it once!


#### Configuring IFTTT alerts

> ⚠️ **WARNING**: This alerting provider has not been tested yet. If you've tested it and confirmed that it works, please remove this warning and create a pull request, or comment on [#1223](https://github.com/TwiN/gatus/discussions/1223) with whether the provider works as intended. Thank you for your cooperation.

| Parameter                          | Description                                                                                | Default       |
|:-----------------------------------|:-------------------------------------------------------------------------------------------|:--------------|
| `alerting.ifttt`                   | Configuration for alerts of type `ifttt`                                                   | `{}`          |
| `alerting.ifttt.webhook-key`       | IFTTT Webhook key                                                                          | Required `""` |
| `alerting.ifttt.event-name`        | IFTTT event name                                                                           | Required `""` |
| `alerting.ifttt.default-alert`     | Default alert configuration. <br />See [Setting a default alert](#setting-a-default-alert) | N/A           |
| `alerting.ifttt.overrides`         | List of overrides that may be prioritized over the default configuration                   | `[]`          |
| `alerting.ifttt.overrides[].group` | Endpoint group for which the configuration will be overridden by this configuration        | `""`          |
| `alerting.ifttt.overrides[].*`     | See `alerting.ifttt.*` parameters                                                          | `{}`          |

```yaml
alerting:
  ifttt:
    webhook-key: "YOUR_WEBHOOK_KEY"
    event-name: "gatus_alert"

endpoints:
  - name: website
    url: "https://twin.sh/health"
    interval: 5m
    conditions:
      - "[STATUS] == 200"
    alerts:
      - type: ifttt
        send-on-resolved: true
```


#### Configuring Ilert alerts
| Parameter                          | Description                                                                                | Default |
|:-----------------------------------|:-------------------------------------------------------------------------------------------|:--------|
| `alerting.ilert`                   | Configuration for alerts of type `ilert`                                                   | `{}`    |
| `alerting.ilert.integration-key`   | ilert Alert Source integration key                                                         | `""`    |
| `alerting.ilert.default-alert`     | Default alert configuration. <br />See [Setting a default alert](#setting-a-default-alert) | N/A     |
| `alerting.ilert.overrides`         | List of overrides that may be prioritized over the default configuration                   | `[]`    |
| `alerting.ilert.overrides[].group` | Endpoint group for which the configuration will be overridden by this configuration        | `""`    |
| `alerting.ilert.overrides[].*`     | See `alerting.ilert.*` parameters                                                          | `{}`    |

It is highly recommended to set `endpoints[].alerts[].send-on-resolved` to `true` for alerts
of type `ilert`, because unlike other alerts, the operation resulting from setting said
parameter to `true` will not create another alert but mark the alert as resolved on
ilert instead.

Behavior:
- By default, `alerting.ilert.integration-key` is used as the integration key
- If the endpoint being evaluated belongs to a group (`endpoints[].group`) matching the value of `alerting.ilert.overrides[].group`, the provider will use that override's integration key instead of `alerting.ilert.integration-key`'s

```yaml
alerting:
  ilert:
    integration-key: "********************************"
    # You can also add group-specific integration keys, which will
    # override the integration key above for the specified groups
    overrides:
      - group: "core"
        integration-key: "********************************"

endpoints:
  - name: website
    url: "https://twin.sh/health"
    interval: 30s
    conditions:
      - "[STATUS] == 200"
      - "[BODY].status == UP"
      - "[RESPONSE_TIME] < 300"
    alerts:
      - type: ilert
        failure-threshold: 3
        success-threshold: 5
        send-on-resolved: true
        description: "healthcheck failed"
```


#### Configuring Incident.io alerts
| Parameter                                | Description                                                                                | Default       |
|:-----------------------------------------|:-------------------------------------------------------------------------------------------|:--------------|
| `alerting.incident-io`                   | Configuration for alerts of type `incident-io`                                             | `{}`          |
| `alerting.incident-io.url`               | url to trigger an alert event.                                                             | Required `""` |
| `alerting.incident-io.auth-token`        | Token that is used for authentication.                                                     | Required `""` |
| `alerting.incident-io.source-url`        | Source URL                                                                                 | `""`          |
| `alerting.incident-io.default-alert`     | Default alert configuration. <br />See [Setting a default alert](#setting-a-default-alert) | N/A           |
| `alerting.incident-io.overrides`         | List of overrides that may be prioritized over the default configuration                   | `[]`          |
| `alerting.incident-io.overrides[].group` | Endpoint group for which the configuration will be overridden by this configuration        | `""`          |
| `alerting.incident-io.overrides[].*`     | See `alerting.incident-io.*` parameters                                                    | `{}`          |

```yaml
alerting:
  incident-io:
    url: "*****************"
    auth-token: "********************************************"

endpoints:
  - name: website
    url: "https://twin.sh/health"
    interval: 30s
    conditions:
      - "[STATUS] == 200"
      - "[BODY].status == UP"
      - "[RESPONSE_TIME] < 300"
    alerts:
      - type: incident-io
        description: "healthcheck failed"
        send-on-resolved: true
```
In order to get the required alert source config id and authentication token, you must configure an HTTP alert source.

> **_NOTE:_**  the source config id is of the form `https://api.incident.io/v2/alert_events/http/$ID` and the token is expected to be passed as a bearer token like so: `Authorization: Bearer $TOKEN`


#### Configuring Line alerts

| Parameter                            | Description                                                                                | Default       |
|:-------------------------------------|:-------------------------------------------------------------------------------------------|:--------------|
| `alerting.line`                      | Configuration for alerts of type `line`                                                    | `{}`          |
| `alerting.line.channel-access-token` | Line Messaging API channel access token                                                    | Required `""` |
| `alerting.line.user-ids`             | List of Line user IDs to send messages to (this can be user ids, room ids or group ids)    | Required `[]` |
| `alerting.line.default-alert`        | Default alert configuration. <br />See [Setting a default alert](#setting-a-default-alert) | N/A           |
| `alerting.line.overrides`            | List of overrides that may be prioritized over the default configuration                   | `[]`          |
| `alerting.line.overrides[].group`    | Endpoint group for which the configuration will be overridden by this configuration        | `""`          |
| `alerting.line.overrides[].*`        | See `alerting.line.*` parameters                                                           | `{}`          |

```yaml
alerting:
  line:
    channel-access-token: "YOUR_CHANNEL_ACCESS_TOKEN"
    user-ids:
      - "U1234567890abcdef" # This can be a group id, room id or user id
      - "U2345678901bcdefg"

endpoints:
  - name: website
    url: "https://twin.sh/health"
    interval: 5m
    conditions:
      - "[STATUS] == 200"
    alerts:
      - type: line
        send-on-resolved: true
```


#### Configuring Matrix alerts
| Parameter                                | Description                                                                                | Default                            |
|:-----------------------------------------|:-------------------------------------------------------------------------------------------|:-----------------------------------|
| `alerting.matrix`                        | Configuration for alerts of type `matrix`                                                  | `{}`                               |
| `alerting.matrix.server-url`             | Homeserver URL                                                                             | `https://matrix-client.matrix.org` |
| `alerting.matrix.access-token`           | Bot user access token (see https://webapps.stackexchange.com/q/131056)                     | Required `""`                      |
| `alerting.matrix.internal-room-id`       | Internal room ID of room to send alerts to (can be found in Room Settings > Advanced)      | Required `""`                      |
| `alerting.matrix.default-alert`          | Default alert configuration. <br />See [Setting a default alert](#setting-a-default-alert) | N/A                                |
| `alerting.matrix.overrides`              | List of overrides that may be prioritized over the default configuration                   | `[]`                               |
| `alerting.matrix.overrides[].group`      | Endpoint group for which the configuration will be overridden by this configuration        | `""`                               |
| `alerting.matrix.overrides[].*`          | See `alerting.matrix.*` parameters                                                         | `{}`                               |

```yaml
alerting:
  matrix:
    server-url: "https://matrix-client.matrix.org"
    access-token: "123456"
    internal-room-id: "!example:matrix.org"

endpoints:
  - name: website
    interval: 5m
    url: "https://twin.sh/health"
    conditions:
      - "[STATUS] == 200"
      - "[BODY].status == UP"
      - "[RESPONSE_TIME] < 300"
    alerts:
      - type: matrix
        send-on-resolved: true
        description: "healthcheck failed"
```


#### Configuring Mattermost alerts
| Parameter                                     | Description                                                                                 | Default       |
|:----------------------------------------------|:--------------------------------------------------------------------------------------------|:--------------|
| `alerting.mattermost`                         | Configuration for alerts of type `mattermost`                                               | `{}`          |
| `alerting.mattermost.webhook-url`             | Mattermost Webhook URL                                                                      | Required `""` |
| `alerting.mattermost.channel`                 | Mattermost channel name override (optional)                                                 | `""`          |
| `alerting.mattermost.client`                  | Client configuration. <br />See [Client configuration](#client-configuration).              | `{}`          |
| `alerting.mattermost.default-alert`           | Default alert configuration. <br />See [Setting a default alert](#setting-a-default-alert). | N/A           |
| `alerting.mattermost.overrides`               | List of overrides that may be prioritized over the default configuration                    | `[]`          |
| `alerting.mattermost.overrides[].group`       | Endpoint group for which the configuration will be overridden by this configuration         | `""`          |
| `alerting.mattermost.overrides[].*`           | See `alerting.mattermost.*` parameters                                                      | `{}`          |

```yaml
alerting:
  mattermost:
    webhook-url: "http://**********/hooks/**********"
    client:
      insecure: true

endpoints:
  - name: website
    url: "https://twin.sh/health"
    interval: 5m
    conditions:
      - "[STATUS] == 200"
      - "[BODY].status == UP"
      - "[RESPONSE_TIME] < 300"
    alerts:
      - type: mattermost
        description: "healthcheck failed"
        send-on-resolved: true
```

Here's an example of what the notifications look like:

![Mattermost notifications](.github/assets/mattermost-alerts.png)


#### Configuring Messagebird alerts
| Parameter                            | Description                                                                                | Default       |
|:-------------------------------------|:-------------------------------------------------------------------------------------------|:--------------|
| `alerting.messagebird`               | Configuration for alerts of type `messagebird`                                             | `{}`          |
| `alerting.messagebird.access-key`    | Messagebird access key                                                                     | Required `""` |
| `alerting.messagebird.originator`    | The sender of the message                                                                  | Required `""` |
| `alerting.messagebird.recipients`    | The recipients of the message                                                              | Required `""` |
| `alerting.messagebird.default-alert` | Default alert configuration. <br />See [Setting a default alert](#setting-a-default-alert) | N/A           |

Example of sending **SMS** text message alert using Messagebird:
```yaml
alerting:
  messagebird:
    access-key: "..."
    originator: "31619191918"
    recipients: "31619191919,31619191920"

endpoints:
  - name: website
    interval: 5m
    url: "https://twin.sh/health"
    conditions:
      - "[STATUS] == 200"
      - "[BODY].status == UP"
      - "[RESPONSE_TIME] < 300"
    alerts:
      - type: messagebird
        failure-threshold: 3
        send-on-resolved: true
        description: "healthcheck failed"
```


#### Configuring New Relic alerts

> ⚠️ **WARNING**: This alerting provider has not been tested yet. If you've tested it and confirmed that it works, please remove this warning and create a pull request, or comment on [#1223](https://github.com/TwiN/gatus/discussions/1223) with whether the provider works as intended. Thank you for your cooperation.

| Parameter                             | Description                                                                                | Default       |
|:--------------------------------------|:-------------------------------------------------------------------------------------------|:--------------|
| `alerting.newrelic`                   | Configuration for alerts of type `newrelic`                                                | `{}`          |
| `alerting.newrelic.api-key`           | New Relic API key                                                                          | Required `""` |
| `alerting.newrelic.account-id`        | New Relic account ID                                                                       | Required `""` |
| `alerting.newrelic.region`            | Region (US or EU)                                                                          | `"US"`        |
| `alerting.newrelic.default-alert`     | Default alert configuration. <br />See [Setting a default alert](#setting-a-default-alert) | N/A           |
| `alerting.newrelic.overrides`         | List of overrides that may be prioritized over the default configuration                   | `[]`          |
| `alerting.newrelic.overrides[].group` | Endpoint group for which the configuration will be overridden by this configuration        | `""`          |
| `alerting.newrelic.overrides[].*`     | See `alerting.newrelic.*` parameters                                                       | `{}`          |

```yaml
alerting:
  newrelic:
    api-key: "YOUR_API_KEY"
    account-id: "1234567"
    region: "US"  # or "EU" for European region

endpoints:
  - name: example
    url: "https://example.org"
    interval: 5m
    conditions:
      - "[STATUS] == 200"
    alerts:
      - type: newrelic
        send-on-resolved: true
```


#### Configuring n8n alerts
| Parameter                        | Description                                                                                | Default       |
|:---------------------------------|:-------------------------------------------------------------------------------------------|:--------------|
| `alerting.n8n`                   | Configuration for alerts of type `n8n`                                                     | `{}`          |
| `alerting.n8n.webhook-url`       | n8n webhook URL                                                                            | Required `""` |
| `alerting.n8n.title`             | Title of the alert sent to n8n                                                             | `""`          |
| `alerting.n8n.default-alert`     | Default alert configuration. <br />See [Setting a default alert](#setting-a-default-alert) | N/A           |
| `alerting.n8n.overrides`        
Download .txt
gitextract_9_t34r5f/

├── .dockerignore
├── .examples/
│   ├── docker-compose/
│   │   └── compose.yaml
│   ├── docker-compose-grafana-prometheus/
│   │   ├── README.md
│   │   ├── compose.yaml
│   │   ├── grafana/
│   │   │   ├── grafana.ini
│   │   │   └── provisioning/
│   │   │       ├── dashboards/
│   │   │       │   ├── dashboard.yml
│   │   │       │   └── gatus.json
│   │   │       └── datasources/
│   │   │           └── prometheus.yml
│   │   └── prometheus/
│   │       └── prometheus.yml
│   ├── docker-compose-mattermost/
│   │   └── compose.yaml
│   ├── docker-compose-mtls/
│   │   ├── certs/
│   │   │   ├── client/
│   │   │   │   ├── client.crt
│   │   │   │   └── client.key
│   │   │   └── server/
│   │   │       ├── ca.crt
│   │   │       ├── server.crt
│   │   │       └── server.key
│   │   ├── compose.yaml
│   │   └── nginx/
│   │       └── default.conf
│   ├── docker-compose-multiple-config-files/
│   │   ├── compose.yaml
│   │   └── config/
│   │       ├── backend.yaml
│   │       ├── frontend.yaml
│   │       └── global.yaml
│   ├── docker-compose-postgres-storage/
│   │   └── compose.yaml
│   ├── docker-compose-sqlite-storage/
│   │   ├── compose.yaml
│   │   └── data/
│   │       └── .gitkeep
│   ├── docker-minimal/
│   │   └── Dockerfile
│   ├── kubernetes/
│   │   └── gatus.yaml
│   └── nixos/
│       ├── README.md
│       └── gatus.nix
├── .gitattributes
├── .github/
│   ├── FUNDING.yml
│   ├── assets/
│   │   └── gatus-diagram.drawio
│   ├── codecov.yml
│   ├── dependabot.yml
│   └── workflows/
│       ├── benchmark.yml
│       ├── labeler.yml
│       ├── publish-custom.yml
│       ├── publish-experimental.yml
│       ├── publish-latest.yml
│       ├── publish-release.yml
│       ├── regenerate-static-assets.yml
│       ├── test-ui.yml
│       └── test.yml
├── .gitignore
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── alerting/
│   ├── alert/
│   │   ├── alert.go
│   │   ├── alert_test.go
│   │   └── type.go
│   ├── config.go
│   └── provider/
│       ├── awsses/
│       │   ├── awsses.go
│       │   └── awsses_test.go
│       ├── clickup/
│       │   ├── clickup.go
│       │   └── clickup_test.go
│       ├── custom/
│       │   ├── custom.go
│       │   └── custom_test.go
│       ├── datadog/
│       │   ├── datadog.go
│       │   └── datadog_test.go
│       ├── discord/
│       │   ├── discord.go
│       │   └── discord_test.go
│       ├── email/
│       │   ├── email.go
│       │   └── email_test.go
│       ├── gitea/
│       │   ├── gitea.go
│       │   └── gitea_test.go
│       ├── github/
│       │   ├── github.go
│       │   └── github_test.go
│       ├── gitlab/
│       │   ├── gitlab.go
│       │   └── gitlab_test.go
│       ├── googlechat/
│       │   ├── googlechat.go
│       │   └── googlechat_test.go
│       ├── gotify/
│       │   ├── gotify.go
│       │   └── gotify_test.go
│       ├── homeassistant/
│       │   ├── homeassistant.go
│       │   └── homeassistant_test.go
│       ├── ifttt/
│       │   ├── ifttt.go
│       │   └── ifttt_test.go
│       ├── ilert/
│       │   ├── ilert.go
│       │   └── ilert_test.go
│       ├── incidentio/
│       │   ├── dedup.go
│       │   ├── incidentio.go
│       │   └── incidentio_test.go
│       ├── line/
│       │   ├── line.go
│       │   └── line_test.go
│       ├── matrix/
│       │   ├── matrix.go
│       │   └── matrix_test.go
│       ├── mattermost/
│       │   ├── mattermost.go
│       │   └── mattermost_test.go
│       ├── messagebird/
│       │   ├── messagebird.go
│       │   └── messagebird_test.go
│       ├── n8n/
│       │   ├── n8n.go
│       │   └── n8n_test.go
│       ├── newrelic/
│       │   ├── newrelic.go
│       │   └── newrelic_test.go
│       ├── ntfy/
│       │   ├── ntfy.go
│       │   └── ntfy_test.go
│       ├── opsgenie/
│       │   ├── opsgenie.go
│       │   └── opsgenie_test.go
│       ├── pagerduty/
│       │   ├── pagerduty.go
│       │   └── pagerduty_test.go
│       ├── plivo/
│       │   ├── plivo.go
│       │   └── plivo_test.go
│       ├── provider.go
│       ├── provider_test.go
│       ├── pushover/
│       │   ├── pushover.go
│       │   └── pushover_test.go
│       ├── rocketchat/
│       │   ├── rocketchat.go
│       │   └── rocketchat_test.go
│       ├── sendgrid/
│       │   ├── sendgrid.go
│       │   └── sendgrid_test.go
│       ├── signal/
│       │   ├── signal.go
│       │   └── signal_test.go
│       ├── signl4/
│       │   ├── signl4.go
│       │   └── signl4_test.go
│       ├── slack/
│       │   ├── slack.go
│       │   └── slack_test.go
│       ├── splunk/
│       │   ├── splunk.go
│       │   └── splunk_test.go
│       ├── squadcast/
│       │   ├── squadcast.go
│       │   └── squadcast_test.go
│       ├── teams/
│       │   ├── teams.go
│       │   └── teams_test.go
│       ├── teamsworkflows/
│       │   ├── teamsworkflows.go
│       │   └── teamsworkflows_test.go
│       ├── telegram/
│       │   ├── telegram.go
│       │   └── telegram_test.go
│       ├── twilio/
│       │   ├── twilio.go
│       │   └── twilio_test.go
│       ├── vonage/
│       │   ├── vonage.go
│       │   └── vonage_test.go
│       ├── webex/
│       │   ├── webex.go
│       │   └── webex_test.go
│       ├── zapier/
│       │   ├── zapier.go
│       │   └── zapier_test.go
│       └── zulip/
│           ├── zulip.go
│           └── zulip_test.go
├── api/
│   ├── api.go
│   ├── api_test.go
│   ├── badge.go
│   ├── badge_test.go
│   ├── cache.go
│   ├── chart.go
│   ├── chart_test.go
│   ├── config.go
│   ├── config_test.go
│   ├── custom_css.go
│   ├── endpoint_status.go
│   ├── endpoint_status_test.go
│   ├── external_endpoint.go
│   ├── external_endpoint_test.go
│   ├── raw.go
│   ├── raw_test.go
│   ├── spa.go
│   ├── spa_test.go
│   ├── suite_status.go
│   ├── suite_status_test.go
│   ├── util.go
│   └── util_test.go
├── client/
│   ├── client.go
│   ├── client_test.go
│   ├── config.go
│   ├── config_test.go
│   └── grpc.go
├── config/
│   ├── announcement/
│   │   ├── announcement.go
│   │   └── announcement_test.go
│   ├── config.go
│   ├── config_test.go
│   ├── connectivity/
│   │   ├── connectivity.go
│   │   └── connectivity_test.go
│   ├── endpoint/
│   │   ├── common.go
│   │   ├── common_test.go
│   │   ├── condition.go
│   │   ├── condition_bench_test.go
│   │   ├── condition_result.go
│   │   ├── condition_test.go
│   │   ├── dns/
│   │   │   ├── dns.go
│   │   │   └── dns_test.go
│   │   ├── endpoint.go
│   │   ├── endpoint_test.go
│   │   ├── event.go
│   │   ├── event_test.go
│   │   ├── external_endpoint.go
│   │   ├── external_endpoint_test.go
│   │   ├── heartbeat/
│   │   │   └── heartbeat.go
│   │   ├── placeholder.go
│   │   ├── placeholder_test.go
│   │   ├── result.go
│   │   ├── result_test.go
│   │   ├── ssh/
│   │   │   ├── ssh.go
│   │   │   └── ssh_test.go
│   │   ├── status.go
│   │   ├── status_test.go
│   │   ├── ui/
│   │   │   ├── ui.go
│   │   │   └── ui_test.go
│   │   └── uptime.go
│   ├── gontext/
│   │   ├── gontext.go
│   │   └── gontext_test.go
│   ├── key/
│   │   ├── key.go
│   │   ├── key_bench_test.go
│   │   └── key_test.go
│   ├── maintenance/
│   │   ├── maintenance.go
│   │   └── maintenance_test.go
│   ├── remote/
│   │   └── remote.go
│   ├── suite/
│   │   ├── result.go
│   │   ├── suite.go
│   │   ├── suite_status.go
│   │   └── suite_test.go
│   ├── tunneling/
│   │   ├── sshtunnel/
│   │   │   ├── sshtunnel.go
│   │   │   └── sshtunnel_test.go
│   │   ├── tunneling.go
│   │   └── tunneling_test.go
│   ├── ui/
│   │   ├── ui.go
│   │   └── ui_test.go
│   ├── util.go
│   └── web/
│       ├── web.go
│       └── web_test.go
├── controller/
│   ├── controller.go
│   └── controller_test.go
├── docs/
│   └── pagerduty-integration-guide.md
├── go.mod
├── go.sum
├── jsonpath/
│   ├── jsonpath.go
│   ├── jsonpath_bench_test.go
│   └── jsonpath_test.go
├── main.go
├── metrics/
│   ├── metrics.go
│   └── metrics_test.go
├── pattern/
│   ├── pattern.go
│   ├── pattern_bench_test.go
│   └── pattern_test.go
├── security/
│   ├── basic.go
│   ├── basic_test.go
│   ├── config.go
│   ├── config_test.go
│   ├── oidc.go
│   ├── oidc_test.go
│   └── sessions.go
├── storage/
│   ├── config.go
│   ├── store/
│   │   ├── common/
│   │   │   ├── errors.go
│   │   │   └── paging/
│   │   │       ├── endpoint_status_params.go
│   │   │       ├── endpoint_status_params_test.go
│   │   │       ├── suite_status_params.go
│   │   │       └── suite_status_params_test.go
│   │   ├── memory/
│   │   │   ├── memory.go
│   │   │   ├── memory_test.go
│   │   │   ├── uptime.go
│   │   │   ├── uptime_bench_test.go
│   │   │   ├── uptime_test.go
│   │   │   ├── util.go
│   │   │   ├── util_bench_test.go
│   │   │   └── util_test.go
│   │   ├── sql/
│   │   │   ├── specific_postgres.go
│   │   │   ├── specific_sqlite.go
│   │   │   ├── sql.go
│   │   │   └── sql_test.go
│   │   ├── store.go
│   │   ├── store_bench_test.go
│   │   └── store_test.go
│   └── type.go
├── test/
│   └── mock.go
├── testdata/
│   ├── badcert.key
│   ├── badcert.pem
│   ├── cert.key
│   └── cert.pem
├── watchdog/
│   ├── alerting.go
│   ├── alerting_test.go
│   ├── endpoint.go
│   ├── external_endpoint.go
│   ├── suite.go
│   └── watchdog.go
└── web/
    ├── app/
    │   ├── .gitignore
    │   ├── README.md
    │   ├── babel.config.js
    │   ├── package.json
    │   ├── postcss.config.js
    │   ├── public/
    │   │   ├── index.html
    │   │   └── manifest.json
    │   ├── src/
    │   │   ├── App.vue
    │   │   ├── components/
    │   │   │   ├── AnnouncementBanner.vue
    │   │   │   ├── EndpointCard.vue
    │   │   │   ├── FlowStep.vue
    │   │   │   ├── Loading.vue
    │   │   │   ├── Pagination.vue
    │   │   │   ├── PastAnnouncements.vue
    │   │   │   ├── ResponseTimeChart.vue
    │   │   │   ├── SearchBar.vue
    │   │   │   ├── SequentialFlowDiagram.vue
    │   │   │   ├── Settings.vue
    │   │   │   ├── Social.vue
    │   │   │   ├── StatusBadge.vue
    │   │   │   ├── StepDetailsModal.vue
    │   │   │   ├── SuiteCard.vue
    │   │   │   ├── Tooltip.vue
    │   │   │   └── ui/
    │   │   │       ├── badge/
    │   │   │       │   ├── Badge.vue
    │   │   │       │   └── index.js
    │   │   │       ├── button/
    │   │   │       │   ├── Button.vue
    │   │   │       │   └── index.js
    │   │   │       ├── card/
    │   │   │       │   ├── Card.vue
    │   │   │       │   ├── CardContent.vue
    │   │   │       │   ├── CardHeader.vue
    │   │   │       │   ├── CardTitle.vue
    │   │   │       │   └── index.js
    │   │   │       ├── input/
    │   │   │       │   ├── Input.vue
    │   │   │       │   └── index.js
    │   │   │       └── select/
    │   │   │           ├── Select.vue
    │   │   │           └── index.js
    │   │   ├── index.css
    │   │   ├── main.js
    │   │   ├── router/
    │   │   │   └── index.js
    │   │   ├── utils/
    │   │   │   ├── format.js
    │   │   │   ├── markdown.js
    │   │   │   ├── misc.js
    │   │   │   └── time.js
    │   │   └── views/
    │   │       ├── EndpointDetails.vue
    │   │       ├── Home.vue
    │   │       └── SuiteDetails.vue
    │   ├── tailwind.config.js
    │   └── vue.config.js
    ├── static/
    │   ├── css/
    │   │   └── app.css
    │   ├── index.html
    │   ├── js/
    │   │   ├── app.js
    │   │   └── chunk-vendors.js
    │   └── manifest.json
    ├── static.go
    └── static_test.go
Download .txt
Showing preview only (509K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (3373 symbols across 219 files)

FILE: alerting/alert/alert.go
  type Alert (line 23) | type Alert struct
    method ValidateAndSetDefaults (line 76) | func (alert *Alert) ValidateAndSetDefaults() error {
    method GetDescription (line 93) | func (alert *Alert) GetDescription() string {
    method IsEnabled (line 102) | func (alert *Alert) IsEnabled() bool {
    method IsSendingOnResolved (line 110) | func (alert *Alert) IsSendingOnResolved() bool {
    method Checksum (line 119) | func (alert *Alert) Checksum() string {
    method ProviderOverrideAsBytes (line 131) | func (alert *Alert) ProviderOverrideAsBytes() []byte {

FILE: alerting/alert/alert_test.go
  function TestAlert_ValidateAndSetDefaults (line 9) | func TestAlert_ValidateAndSetDefaults(t *testing.T) {
  function TestAlert_IsEnabled (line 111) | func TestAlert_IsEnabled(t *testing.T) {
  function TestAlert_GetDescription (line 123) | func TestAlert_GetDescription(t *testing.T) {
  function TestAlert_IsSendingOnResolved (line 132) | func TestAlert_IsSendingOnResolved(t *testing.T) {
  function TestAlert_Checksum (line 144) | func TestAlert_Checksum(t *testing.T) {

FILE: alerting/alert/type.go
  type Type (line 5) | type Type
  constant TypeAWSSES (line 9) | TypeAWSSES Type = "aws-ses"
  constant TypeClickUp (line 12) | TypeClickUp Type = "clickup"
  constant TypeCustom (line 15) | TypeCustom Type = "custom"
  constant TypeDatadog (line 18) | TypeDatadog Type = "datadog"
  constant TypeDiscord (line 21) | TypeDiscord Type = "discord"
  constant TypeEmail (line 24) | TypeEmail Type = "email"
  constant TypeGitHub (line 27) | TypeGitHub Type = "github"
  constant TypeGitLab (line 30) | TypeGitLab Type = "gitlab"
  constant TypeGitea (line 33) | TypeGitea Type = "gitea"
  constant TypeGoogleChat (line 36) | TypeGoogleChat Type = "googlechat"
  constant TypeGotify (line 39) | TypeGotify Type = "gotify"
  constant TypeHomeAssistant (line 42) | TypeHomeAssistant Type = "homeassistant"
  constant TypeIFTTT (line 45) | TypeIFTTT Type = "ifttt"
  constant TypeIlert (line 48) | TypeIlert Type = "ilert"
  constant TypeIncidentIO (line 51) | TypeIncidentIO Type = "incident-io"
  constant TypeLine (line 54) | TypeLine Type = "line"
  constant TypeMatrix (line 57) | TypeMatrix Type = "matrix"
  constant TypeMattermost (line 60) | TypeMattermost Type = "mattermost"
  constant TypeMessagebird (line 63) | TypeMessagebird Type = "messagebird"
  constant TypeNewRelic (line 66) | TypeNewRelic Type = "newrelic"
  constant TypeN8N (line 69) | TypeN8N Type = "n8n"
  constant TypeNtfy (line 72) | TypeNtfy Type = "ntfy"
  constant TypeOpsgenie (line 75) | TypeOpsgenie Type = "opsgenie"
  constant TypePagerDuty (line 78) | TypePagerDuty Type = "pagerduty"
  constant TypePlivo (line 81) | TypePlivo Type = "plivo"
  constant TypePushover (line 84) | TypePushover Type = "pushover"
  constant TypeRocketChat (line 87) | TypeRocketChat Type = "rocketchat"
  constant TypeSendGrid (line 90) | TypeSendGrid Type = "sendgrid"
  constant TypeSignal (line 93) | TypeSignal Type = "signal"
  constant TypeSIGNL4 (line 96) | TypeSIGNL4 Type = "signl4"
  constant TypeSlack (line 99) | TypeSlack Type = "slack"
  constant TypeSplunk (line 102) | TypeSplunk Type = "splunk"
  constant TypeSquadcast (line 105) | TypeSquadcast Type = "squadcast"
  constant TypeTeams (line 108) | TypeTeams Type = "teams"
  constant TypeTeamsWorkflows (line 111) | TypeTeamsWorkflows Type = "teams-workflows"
  constant TypeTelegram (line 114) | TypeTelegram Type = "telegram"
  constant TypeTwilio (line 117) | TypeTwilio Type = "twilio"
  constant TypeVonage (line 120) | TypeVonage Type = "vonage"
  constant TypeWebex (line 123) | TypeWebex Type = "webex"
  constant TypeZapier (line 126) | TypeZapier Type = "zapier"
  constant TypeZulip (line 129) | TypeZulip Type = "zulip"

FILE: alerting/config.go
  type Config (line 54) | type Config struct
    method GetAlertingProviderByAlertType (line 180) | func (config *Config) GetAlertingProviderByAlertType(alertType alert.T...
    method SetAlertingProviderToNil (line 199) | func (config *Config) SetAlertingProviderToNil(p provider.AlertProvide...

FILE: alerting/provider/awsses/awsses.go
  constant CharSet (line 20) | CharSet = "UTF-8"
  type Config (line 29) | type Config struct
    method Validate (line 38) | func (cfg *Config) Validate() error {
    method Merge (line 50) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 69) | type AlertProvider struct
    method Validate (line 86) | func (provider *AlertProvider) Validate() error {
    method Send (line 100) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method createClient (line 136) | func (provider *AlertProvider) createClient(ctx context.Context, cfg *...
    method buildMessageSubjectAndBody (line 152) | func (provider *AlertProvider) buildMessageSubjectAndBody(ep *endpoint...
    method GetDefaultAlert (line 182) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 187) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 212) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 80) | type Override struct

FILE: alerting/provider/awsses/awsses_test.go
  function TestAlertProvider_Validate (line 10) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_ValidateWithOverride (line 29) | func TestAlertProvider_ValidateWithOverride(t *testing.T) {
  function TestAlertProvider_buildRequestBody (line 69) | func TestAlertProvider_buildRequestBody(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 120) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {
  function TestAlertProvider_getConfigWithOverrides (line 129) | func TestAlertProvider_getConfigWithOverrides(t *testing.T) {

FILE: alerting/provider/clickup/clickup.go
  type Config (line 32) | type Config struct
    method Validate (line 44) | func (cfg *Config) Validate() error {
    method Merge (line 73) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 104) | type AlertProvider struct
    method Validate (line 121) | func (provider *AlertProvider) Validate() error {
    method Send (line 134) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method CreateTask (line 162) | func (provider *AlertProvider) CreateTask(cfg *Config, body map[string...
    method CloseTask (line 186) | func (provider *AlertProvider) CloseTask(cfg *Config, ep *endpoint.End...
    method UpdateTaskStatus (line 228) | func (provider *AlertProvider) UpdateTaskStatus(cfg *Config, taskID, s...
    method GetDefaultAlert (line 253) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 258) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 282) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 115) | type Override struct

FILE: alerting/provider/clickup/clickup_test.go
  function TestAlertProvider_Validate (line 16) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_ValidateSetsDefaultAPIURL (line 50) | func TestAlertProvider_ValidateSetsDefaultAPIURL(t *testing.T) {
  function TestAlertProvider_Send (line 60) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 188) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {
  function TestAlertProvider_GetConfig (line 197) | func TestAlertProvider_GetConfig(t *testing.T) {

FILE: alerting/provider/custom/custom.go
  type Config (line 21) | type Config struct
    method Validate (line 32) | func (cfg *Config) Validate() error {
    method Merge (line 39) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 62) | type AlertProvider struct
    method Validate (line 79) | func (provider *AlertProvider) Validate() error {
    method Send (line 83) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildHTTPRequest (line 101) | func (provider *AlertProvider) buildHTTPRequest(cfg *Config, ep *endpo...
    method GetAlertStatePlaceholderValue (line 152) | func (provider *AlertProvider) GetAlertStatePlaceholderValue(cfg *Conf...
    method GetDefaultAlert (line 166) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 171) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 196) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 73) | type Override struct

FILE: alerting/provider/custom/custom_test.go
  function TestAlertProvider_Validate (line 15) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_Send (line 30) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_buildHTTPRequest (line 107) | func TestAlertProvider_buildHTTPRequest(t *testing.T) {
  function TestAlertProviderWithResultErrors_buildHTTPRequest (line 154) | func TestAlertProviderWithResultErrors_buildHTTPRequest(t *testing.T) {
  function TestAlertProvider_buildHTTPRequestWithCustomPlaceholder (line 210) | func TestAlertProvider_buildHTTPRequestWithCustomPlaceholder(t *testing....
  function TestAlertProvider_buildHTTPRequestWithCustomPlaceholderAndResultConditions (line 264) | func TestAlertProvider_buildHTTPRequestWithCustomPlaceholderAndResultCon...
  function TestAlertProvider_GetAlertStatePlaceholderValueDefaults (line 327) | func TestAlertProvider_GetAlertStatePlaceholderValueDefaults(t *testing....
  function TestAlertProvider_GetDefaultAlert (line 342) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {
  function TestAlertProvider_GetConfig (line 351) | func TestAlertProvider_GetConfig(t *testing.T) {

FILE: alerting/provider/datadog/datadog.go
  type Config (line 23) | type Config struct
    method Validate (line 29) | func (cfg *Config) Validate() error {
    method Merge (line 36) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 49) | type AlertProvider struct
    method Validate (line 66) | func (provider *AlertProvider) Validate() error {
    method Send (line 80) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 124) | func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpo...
    method GetDefaultAlert (line 181) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 186) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 211) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 60) | type Override struct
  type Body (line 113) | type Body struct

FILE: alerting/provider/datadog/datadog_test.go
  function TestAlertProvider_Validate (line 15) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_Send (line 52) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 176) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {

FILE: alerting/provider/discord/discord.go
  type Config (line 22) | type Config struct
    method Validate (line 28) | func (cfg *Config) Validate() error {
    method Merge (line 35) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 48) | type AlertProvider struct
    method Validate (line 64) | func (provider *AlertProvider) Validate() error {
    method Send (line 78) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 120) | func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpo...
    method GetDefaultAlert (line 170) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 175) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 200) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 58) | type Override struct
  type Body (line 101) | type Body struct
  type Embed (line 106) | type Embed struct
  type Field (line 113) | type Field struct

FILE: alerting/provider/discord/discord_test.go
  function TestAlertProvider_Validate (line 14) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_ValidateWithOverride (line 25) | func TestAlertProvider_ValidateWithOverride(t *testing.T) {
  function TestAlertProvider_Send (line 64) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_buildRequestBody (line 172) | func TestAlertProvider_buildRequestBody(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 265) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {
  function TestAlertProvider_GetConfig (line 274) | func TestAlertProvider_GetConfig(t *testing.T) {

FILE: alerting/provider/email/email.go
  type Config (line 24) | type Config struct
    method Validate (line 36) | func (cfg *Config) Validate() error {
    method Merge (line 49) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 74) | type AlertProvider struct
    method Validate (line 91) | func (provider *AlertProvider) Validate() error {
    method Send (line 105) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildMessageSubjectAndBody (line 143) | func (provider *AlertProvider) buildMessageSubjectAndBody(ep *endpoint...
    method GetDefaultAlert (line 180) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 185) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 210) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 85) | type Override struct

FILE: alerting/provider/email/email_test.go
  function TestAlertProvider_Validate (line 10) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_ValidateWithNoCredentials (line 21) | func TestAlertProvider_ValidateWithNoCredentials(t *testing.T) {
  function TestAlertProvider_ValidateWithOverride (line 28) | func TestAlertProvider_ValidateWithOverride(t *testing.T) {
  function TestAlertProvider_buildRequestBody (line 71) | func TestAlertProvider_buildRequestBody(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 152) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {
  function TestAlertProvider_GetConfig (line 161) | func TestAlertProvider_GetConfig(t *testing.T) {

FILE: alerting/provider/gitea/gitea.go
  type Config (line 24) | type Config struct
    method Validate (line 38) | func (cfg *Config) Validate() error {
    method Merge (line 85) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 101) | type AlertProvider struct
    method Validate (line 109) | func (provider *AlertProvider) Validate() error {
    method Send (line 115) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildIssueBody (line 170) | func (provider *AlertProvider) buildIssueBody(ep *endpoint.Endpoint, a...
    method GetDefaultAlert (line 193) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 198) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 214) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...

FILE: alerting/provider/gitea/gitea_test.go
  function isIgnorableTestError (line 16) | func isIgnorableTestError(err error) bool {
  function TestAlertProvider_Validate (line 27) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_Send (line 72) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_buildRequestBody (line 128) | func TestAlertProvider_buildRequestBody(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 182) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {
  function TestAlertProvider_GetConfig (line 191) | func TestAlertProvider_GetConfig(t *testing.T) {

FILE: alerting/provider/github/github.go
  type Config (line 24) | type Config struct
    method Validate (line 34) | func (cfg *Config) Validate() error {
    method Merge (line 81) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 91) | type AlertProvider struct
    method Validate (line 99) | func (provider *AlertProvider) Validate() error {
    method Send (line 105) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildIssueBody (line 143) | func (provider *AlertProvider) buildIssueBody(ep *endpoint.Endpoint, a...
    method GetDefaultAlert (line 166) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 171) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 187) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...

FILE: alerting/provider/github/github_test.go
  function TestAlertProvider_Validate (line 15) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_Send (line 60) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_buildRequestBody (line 116) | func TestAlertProvider_buildRequestBody(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 170) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {
  function TestAlertProvider_GetConfig (line 179) | func TestAlertProvider_GetConfig(t *testing.T) {

FILE: alerting/provider/gitlab/gitlab.go
  constant DefaultSeverity (line 20) | DefaultSeverity       = "critical"
  constant DefaultMonitoringTool (line 21) | DefaultMonitoringTool = "gatus"
  type Config (line 29) | type Config struct
    method Validate (line 38) | func (cfg *Config) Validate() error {
    method Merge (line 56) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 78) | type AlertProvider struct
    method Validate (line 86) | func (provider *AlertProvider) Validate() error {
    method Send (line 92) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildAlertBody (line 133) | func (provider *AlertProvider) buildAlertBody(cfg *Config, ep *endpoin...
    method GetDefaultAlert (line 180) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 185) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 201) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type AlertBody (line 119) | type AlertBody struct

FILE: alerting/provider/gitlab/gitlab_test.go
  function TestAlertProvider_Validate (line 14) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_Send (line 54) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_buildAlertBody (line 111) | func TestAlertProvider_buildAlertBody(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 160) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {
  function TestAlertProvider_GetConfig (line 169) | func TestAlertProvider_GetConfig(t *testing.T) {

FILE: alerting/provider/googlechat/googlechat.go
  type Config (line 22) | type Config struct
    method Validate (line 27) | func (cfg *Config) Validate() error {
    method Merge (line 34) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 44) | type AlertProvider struct
    method Validate (line 61) | func (provider *AlertProvider) Validate() error {
    method Send (line 75) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 141) | func (provider *AlertProvider) buildRequestBody(ep *endpoint.Endpoint,...
    method GetDefaultAlert (line 215) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 220) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 245) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 55) | type Override struct
  type Body (line 98) | type Body struct
  type Cards (line 102) | type Cards struct
  type Sections (line 106) | type Sections struct
  type Widgets (line 110) | type Widgets struct
  type KeyValue (line 115) | type KeyValue struct
  type Buttons (line 123) | type Buttons struct
  type TextButton (line 127) | type TextButton struct
  type OnClick (line 132) | type OnClick struct
  type OpenLink (line 136) | type OpenLink struct

FILE: alerting/provider/googlechat/googlechat_test.go
  function TestAlertProvider_Validate (line 14) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_ValidateWithOverride (line 25) | func TestAlertProvider_ValidateWithOverride(t *testing.T) {
  function TestAlertProvider_Send (line 62) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_buildRequestBody (line 139) | func TestAlertProvider_buildRequestBody(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 207) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {
  function TestAlertProvider_GetConfig (line 216) | func TestAlertProvider_GetConfig(t *testing.T) {

FILE: alerting/provider/gotify/gotify.go
  constant DefaultPriority (line 17) | DefaultPriority = 5
  type Config (line 24) | type Config struct
    method Validate (line 31) | func (cfg *Config) Validate() error {
    method Merge (line 44) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 60) | type AlertProvider struct
    method Validate (line 68) | func (provider *AlertProvider) Validate() error {
    method Send (line 73) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 103) | func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpo...
    method GetDefaultAlert (line 137) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 142) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 158) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Body (line 96) | type Body struct

FILE: alerting/provider/gotify/gotify_test.go
  function TestAlertProvider_Validate (line 12) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_buildRequestBody (line 48) | func TestAlertProvider_buildRequestBody(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 108) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {
  function TestAlertProvider_GetConfig (line 115) | func TestAlertProvider_GetConfig(t *testing.T) {

FILE: alerting/provider/homeassistant/homeassistant.go
  type Config (line 23) | type Config struct
    method Validate (line 28) | func (cfg *Config) Validate() error {
    method Merge (line 38) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 48) | type AlertProvider struct
    method Validate (line 65) | func (provider *AlertProvider) Validate() error {
    method Send (line 79) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 119) | func (provider *AlertProvider) buildRequestBody(ep *endpoint.Endpoint,...
    method GetDefaultAlert (line 166) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 171) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 193) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 59) | type Override struct
  type Body (line 103) | type Body struct

FILE: alerting/provider/homeassistant/homeassistant_test.go
  function TestAlertProvider_Validate (line 14) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_ValidateWithOverride (line 29) | func TestAlertProvider_ValidateWithOverride(t *testing.T) {
  function TestAlertProvider_Send (line 55) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_buildRequestBody (line 122) | func TestAlertProvider_buildRequestBody(t *testing.T) {

FILE: alerting/provider/ifttt/ifttt.go
  type Config (line 23) | type Config struct
    method Validate (line 28) | func (cfg *Config) Validate() error {
    method Merge (line 38) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 48) | type AlertProvider struct
    method Validate (line 65) | func (provider *AlertProvider) Validate() error {
    method Send (line 79) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 114) | func (provider *AlertProvider) buildRequestBody(ep *endpoint.Endpoint,...
    method GetDefaultAlert (line 154) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 159) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 184) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 59) | type Override struct
  type Body (line 107) | type Body struct

FILE: alerting/provider/ifttt/ifttt_test.go
  function TestAlertProvider_Validate (line 15) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_Send (line 47) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 147) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {

FILE: alerting/provider/ilert/ilert.go
  constant restAPIUrl (line 18) | restAPIUrl = "https://api.ilert.com/api/v1/events/gatus/"
  type Config (line 26) | type Config struct
    method Validate (line 30) | func (cfg *Config) Validate() error {
    method Merge (line 37) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 44) | type AlertProvider struct
    method Validate (line 59) | func (provider *AlertProvider) Validate() error {
    method Send (line 72) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 108) | func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpo...
    method GetDefaultAlert (line 136) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 140) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 165) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 54) | type Override struct
  type Body (line 97) | type Body struct

FILE: alerting/provider/ilert/ilert_test.go
  function TestAlertProvider_Validate (line 16) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_ValidateWithOverride (line 54) | func TestAlertProvider_ValidateWithOverride(t *testing.T) {
  function TestAlertProvider_Send (line 81) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_BuildRequestBody (line 160) | func TestAlertProvider_BuildRequestBody(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 224) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {
  function TestAlertProvider_GetConfig (line 233) | func TestAlertProvider_GetConfig(t *testing.T) {

FILE: alerting/provider/incidentio/dedup.go
  function generateDeduplicationKey (line 14) | func generateDeduplicationKey(ep *endpoint.Endpoint, alert *alert.Alert)...

FILE: alerting/provider/incidentio/incidentio.go
  constant restAPIUrl (line 22) | restAPIUrl = "https://api.incident.io/v2/alert_events/http/"
  type Config (line 32) | type Config struct
    method Validate (line 39) | func (cfg *Config) Validate() error {
    method Merge (line 52) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 68) | type AlertProvider struct
    method Validate (line 83) | func (provider *AlertProvider) Validate() error {
    method Send (line 96) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 141) | func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpo...
    method GetConfig (line 194) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method GetDefaultAlert (line 219) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method ValidateOverrides (line 223) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 78) | type Override struct
  type Body (line 127) | type Body struct
  type Response (line 137) | type Response struct

FILE: alerting/provider/incidentio/incidentio_test.go
  function TestAlertProvider_Validate (line 16) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_Send (line 85) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_BuildRequestBody (line 181) | func TestAlertProvider_BuildRequestBody(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 304) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {
  function TestAlertProvider_GetConfig (line 313) | func TestAlertProvider_GetConfig(t *testing.T) {
  function TestAlertProvider_ValidateWithOverride (line 408) | func TestAlertProvider_ValidateWithOverride(t *testing.T) {

FILE: alerting/provider/line/line.go
  type Config (line 23) | type Config struct
    method Validate (line 28) | func (cfg *Config) Validate() error {
    method Merge (line 38) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 48) | type AlertProvider struct
    method Validate (line 65) | func (provider *AlertProvider) Validate() error {
    method Send (line 79) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 121) | func (provider *AlertProvider) buildRequestBody(ep *endpoint.Endpoint,...
    method GetDefaultAlert (line 160) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 165) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 190) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 59) | type Override struct
  type Body (line 110) | type Body struct
  type Message (line 115) | type Message struct

FILE: alerting/provider/line/line_test.go
  function TestAlertProvider_Validate (line 14) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_Send (line 46) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 136) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {
  function contains (line 145) | func contains(s, substr string) bool {

FILE: alerting/provider/matrix/matrix.go
  constant defaultServerURL (line 19) | defaultServerURL = "https://matrix-client.matrix.org"
  type Config (line 27) | type Config struct
    method Validate (line 38) | func (cfg *Config) Validate() error {
    method Merge (line 51) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 64) | type AlertProvider struct
    method Validate (line 81) | func (provider *AlertProvider) Validate() error {
    method Send (line 95) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 137) | func (provider *AlertProvider) buildRequestBody(ep *endpoint.Endpoint,...
    method GetDefaultAlert (line 212) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 217) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 242) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 75) | type Override struct
  type Body (line 129) | type Body struct
  function buildPlaintextMessageBody (line 148) | func buildPlaintextMessageBody(ep *endpoint.Endpoint, alert *alert.Alert...
  function buildHTMLMessageBody (line 173) | func buildHTMLMessageBody(ep *endpoint.Endpoint, alert *alert.Alert, res...
  function randStringBytes (line 201) | func randStringBytes(n int) string {

FILE: alerting/provider/matrix/matrix_test.go
  function TestAlertProvider_Validate (line 14) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_ValidateWithOverride (line 45) | func TestAlertProvider_ValidateWithOverride(t *testing.T) {
  function TestAlertProvider_Send (line 95) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_buildRequestBody (line 182) | func TestAlertProvider_buildRequestBody(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 231) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {
  function TestAlertProvider_GetConfig (line 240) | func TestAlertProvider_GetConfig(t *testing.T) {

FILE: alerting/provider/mattermost/mattermost.go
  type Config (line 22) | type Config struct
    method Validate (line 28) | func (cfg *Config) Validate() error {
    method Merge (line 35) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 48) | type AlertProvider struct
    method Validate (line 65) | func (provider *AlertProvider) Validate() error {
    method Send (line 79) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 126) | func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpo...
    method GetDefaultAlert (line 178) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 183) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 208) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 59) | type Override struct
  type Body (line 102) | type Body struct
  type Attachment (line 110) | type Attachment struct
  type Field (line 119) | type Field struct

FILE: alerting/provider/mattermost/mattermost_test.go
  function TestAlertProvider_Validate (line 14) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_ValidateWithOverride (line 25) | func TestAlertProvider_ValidateWithOverride(t *testing.T) {
  function TestAlertProvider_Send (line 62) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_buildRequestBody (line 139) | func TestAlertProvider_buildRequestBody(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 189) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {
  function TestAlertProvider_GetConfig (line 198) | func TestAlertProvider_GetConfig(t *testing.T) {

FILE: alerting/provider/messagebird/messagebird.go
  constant restAPIURL (line 17) | restAPIURL = "https://rest.messagebird.com/messages"
  type Config (line 25) | type Config struct
    method Validate (line 31) | func (cfg *Config) Validate() error {
    method Merge (line 44) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 57) | type AlertProvider struct
    method Validate (line 65) | func (provider *AlertProvider) Validate() error {
    method Send (line 71) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 102) | func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpo...
    method GetDefaultAlert (line 118) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 123) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 139) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Body (line 95) | type Body struct

FILE: alerting/provider/messagebird/messagebird_test.go
  function TestMessagebirdAlertProvider_IsValid (line 14) | func TestMessagebirdAlertProvider_IsValid(t *testing.T) {
  function TestAlertProvider_Send (line 31) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_buildRequestBody (line 108) | func TestAlertProvider_buildRequestBody(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 158) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {
  function TestAlertProvider_GetConfig (line 167) | func TestAlertProvider_GetConfig(t *testing.T) {

FILE: alerting/provider/n8n/n8n.go
  type Config (line 22) | type Config struct
    method Validate (line 27) | func (cfg *Config) Validate() error {
    method Merge (line 34) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 44) | type AlertProvider struct
    method Validate (line 59) | func (provider *AlertProvider) Validate() error {
    method Send (line 73) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 113) | func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpo...
    method GetDefaultAlert (line 146) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 151) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 176) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 53) | type Override struct
  type Body (line 96) | type Body struct
  type ConditionResult (line 107) | type ConditionResult struct

FILE: alerting/provider/n8n/n8n_test.go
  function TestAlertProvider_Validate (line 14) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_ValidateWithOverride (line 25) | func TestAlertProvider_ValidateWithOverride(t *testing.T) {
  function TestAlertProvider_Send (line 62) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_buildRequestBody (line 139) | func TestAlertProvider_buildRequestBody(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 266) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {
  function TestAlertProvider_GetConfig (line 275) | func TestAlertProvider_GetConfig(t *testing.T) {

FILE: alerting/provider/newrelic/newrelic.go
  type Config (line 24) | type Config struct
    method Validate (line 30) | func (cfg *Config) Validate() error {
    method Merge (line 40) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 53) | type AlertProvider struct
    method Validate (line 70) | func (provider *AlertProvider) Validate() error {
    method Send (line 84) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 135) | func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpo...
    method GetDefaultAlert (line 182) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 187) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 212) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 64) | type Override struct
  type Event (line 120) | type Event struct

FILE: alerting/provider/newrelic/newrelic_test.go
  function TestAlertProvider_Validate (line 15) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_Send (line 52) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 182) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {

FILE: alerting/provider/ntfy/ntfy.go
  constant DefaultURL (line 20) | DefaultURL      = "https://ntfy.sh"
  constant DefaultPriority (line 21) | DefaultPriority = 3
  constant TokenPrefix (line 22) | TokenPrefix     = "tk_"
  type Config (line 32) | type Config struct
    method Validate (line 43) | func (cfg *Config) Validate() error {
    method Merge (line 62) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 90) | type AlertProvider struct
    method Validate (line 107) | func (provider *AlertProvider) Validate() error {
    method Send (line 131) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 175) | func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpo...
    method GetDefaultAlert (line 210) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 215) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 240) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 101) | type Override struct
  type Body (line 164) | type Body struct

FILE: alerting/provider/ntfy/ntfy_test.go
  function TestAlertProvider_Validate (line 14) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_buildRequestBody (line 99) | func TestAlertProvider_buildRequestBody(t *testing.T) {
  function TestAlertProvider_Send (line 181) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_GetConfig (line 304) | func TestAlertProvider_GetConfig(t *testing.T) {

FILE: alerting/provider/opsgenie/opsgenie.go
  constant restAPI (line 20) | restAPI = "https://api.opsgenie.com/v2/alerts"
  type Config (line 27) | type Config struct
    method Validate (line 57) | func (cfg *Config) Validate() error {
    method Merge (line 76) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 97) | type AlertProvider struct
    method Validate (line 105) | func (provider *AlertProvider) Validate() error {
    method Send (line 112) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method sendAlertRequest (line 138) | func (provider *AlertProvider) sendAlertRequest(cfg *Config, ep *endpo...
    method closeAlert (line 143) | func (provider *AlertProvider) closeAlert(cfg *Config, ep *endpoint.En...
    method sendRequest (line 149) | func (provider *AlertProvider) sendRequest(cfg *Config, url, method st...
    method buildCreateRequestBody (line 172) | func (provider *AlertProvider) buildCreateRequestBody(cfg *Config, ep ...
    method buildCloseRequestBody (line 224) | func (provider *AlertProvider) buildCloseRequestBody(ep *endpoint.Endp...
    method GetDefaultAlert (line 232) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 237) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 253) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  function buildKey (line 258) | func buildKey(ep *endpoint.Endpoint) string {
  function toKebabCase (line 266) | func toKebabCase(val string) string {
  type alertCreateRequest (line 270) | type alertCreateRequest struct
  type alertCloseRequest (line 281) | type alertCloseRequest struct

FILE: alerting/provider/opsgenie/opsgenie_test.go
  function TestAlertProvider_Validate (line 14) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_Send (line 25) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_buildCreateRequestBody (line 101) | func TestAlertProvider_buildCreateRequestBody(t *testing.T) {
  function TestAlertProvider_buildCloseRequestBody (line 277) | func TestAlertProvider_buildCloseRequestBody(t *testing.T) {
  function TestAlertProvider_GetConfig (line 322) | func TestAlertProvider_GetConfig(t *testing.T) {

FILE: alerting/provider/pagerduty/pagerduty.go
  constant restAPIURL (line 19) | restAPIURL = "https://events.pagerduty.com/v2/enqueue"
  type Config (line 27) | type Config struct
    method Validate (line 31) | func (cfg *Config) Validate() error {
    method Merge (line 38) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 45) | type AlertProvider struct
    method Validate (line 62) | func (provider *AlertProvider) Validate() error {
    method Send (line 79) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 131) | func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpo...
    method GetDefaultAlert (line 156) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 161) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 186) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 56) | type Override struct
  type Body (line 117) | type Body struct
  type Payload (line 124) | type Payload struct
  type pagerDutyResponsePayload (line 191) | type pagerDutyResponsePayload struct

FILE: alerting/provider/pagerduty/pagerduty_test.go
  function TestAlertProvider_Validate (line 14) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_ValidateWithOverride (line 25) | func TestAlertProvider_ValidateWithOverride(t *testing.T) {
  function TestAlertProvider_Send (line 52) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_buildRequestBody (line 129) | func TestAlertProvider_buildRequestBody(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 167) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {
  function TestAlertProvider_GetConfig (line 176) | func TestAlertProvider_GetConfig(t *testing.T) {

FILE: alerting/provider/plivo/plivo.go
  type Config (line 26) | type Config struct
    method Validate (line 33) | func (cfg *Config) Validate() error {
    method Merge (line 49) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 65) | type AlertProvider struct
    method Validate (line 82) | func (provider *AlertProvider) Validate() error {
    method Send (line 96) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method sendSMS (line 112) | func (provider *AlertProvider) sendSMS(cfg *Config, to, message string...
    method buildMessage (line 141) | func (provider *AlertProvider) buildMessage(cfg *Config, ep *endpoint....
    method GetDefaultAlert (line 150) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 155) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 180) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 76) | type Override struct

FILE: alerting/provider/plivo/plivo_test.go
  function TestPlivoAlertProvider_IsValid (line 15) | func TestPlivoAlertProvider_IsValid(t *testing.T) {
  function TestAlertProvider_Send (line 113) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_buildMessage (line 200) | func TestAlertProvider_buildMessage(t *testing.T) {
  function TestAlertProvider_sendSMS (line 246) | func TestAlertProvider_sendSMS(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 307) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {
  function TestAlertProvider_GetConfig (line 316) | func TestAlertProvider_GetConfig(t *testing.T) {
  function TestConfig_Validate (line 419) | func TestConfig_Validate(t *testing.T) {
  function TestConfig_Merge (line 488) | func TestConfig_Merge(t *testing.T) {

FILE: alerting/provider/provider.go
  type AlertProvider (line 49) | type AlertProvider interface
  type Config (line 63) | type Config interface
  function MergeProviderDefaultAlertIntoEndpointAlert (line 69) | func MergeProviderDefaultAlertIntoEndpointAlert(providerDefaultAlert, en...

FILE: alerting/provider/provider_test.go
  function TestParseWithDefaultAlert (line 10) | func TestParseWithDefaultAlert(t *testing.T) {

FILE: alerting/provider/pushover/pushover.go
  constant ApiURL (line 18) | ApiURL          = "https://api.pushover.net/1/messages.json"
  constant defaultPriority (line 19) | defaultPriority = 0
  type Config (line 29) | type Config struct
    method Validate (line 63) | func (cfg *Config) Validate() error {
    method Merge (line 85) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 113) | type AlertProvider struct
    method Validate (line 121) | func (provider *AlertProvider) Validate() error {
    method Send (line 127) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 163) | func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpo...
    method GetDefaultAlert (line 204) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 209) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 225) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Body (line 150) | type Body struct

FILE: alerting/provider/pushover/pushover_test.go
  function TestPushoverAlertProvider_IsValid (line 14) | func TestPushoverAlertProvider_IsValid(t *testing.T) {
  function TestAlertProvider_Send (line 49) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_buildRequestBody (line 126) | func TestAlertProvider_buildRequestBody(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 212) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {
  function TestAlertProvider_GetConfig (line 221) | func TestAlertProvider_GetConfig(t *testing.T) {

FILE: alerting/provider/rocketchat/rocketchat.go
  type Config (line 22) | type Config struct
    method Validate (line 27) | func (cfg *Config) Validate() error {
    method Merge (line 34) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 44) | type AlertProvider struct
    method Validate (line 61) | func (provider *AlertProvider) Validate() error {
    method Send (line 75) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 125) | func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpo...
    method GetDefaultAlert (line 179) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 184) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 209) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 55) | type Override struct
  type Body (line 102) | type Body struct
  type Attachment (line 109) | type Attachment struct
  type Field (line 118) | type Field struct

FILE: alerting/provider/rocketchat/rocketchat_test.go
  function TestAlertProvider_Validate (line 15) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_Send (line 47) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 157) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {

FILE: alerting/provider/sendgrid/sendgrid.go
  constant ApiURL (line 19) | ApiURL = "https://api.sendgrid.com/v3/mail/send"
  type Config (line 29) | type Config struct
    method Validate (line 38) | func (cfg *Config) Validate() error {
    method Merge (line 51) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 67) | type AlertProvider struct
    method Validate (line 84) | func (provider *AlertProvider) Validate() error {
    method Send (line 98) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildSendGridPayload (line 148) | func (provider *AlertProvider) buildSendGridPayload(cfg *Config, subje...
    method buildMessageSubjectAndBody (line 178) | func (provider *AlertProvider) buildMessageSubjectAndBody(ep *endpoint...
    method GetDefaultAlert (line 215) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 220) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 245) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 78) | type Override struct
  type SendGridPayload (line 127) | type SendGridPayload struct
  type Personalization (line 134) | type Personalization struct
  type Email (line 138) | type Email struct
  type Content (line 142) | type Content struct

FILE: alerting/provider/sendgrid/sendgrid_test.go
  function TestAlertProvider_Validate (line 15) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_ValidateWithOverride (line 26) | func TestAlertProvider_ValidateWithOverride(t *testing.T) {
  function TestAlertProvider_Send (line 107) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_buildSendGridPayload (line 174) | func TestAlertProvider_buildSendGridPayload(t *testing.T) {
  function TestAlertProvider_buildMessageSubjectAndBody (line 219) | func TestAlertProvider_buildMessageSubjectAndBody(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 300) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {
  function TestAlertProvider_GetConfig (line 309) | func TestAlertProvider_GetConfig(t *testing.T) {
  function TestConfig_Validate (line 440) | func TestConfig_Validate(t *testing.T) {
  function TestConfig_Merge (line 483) | func TestConfig_Merge(t *testing.T) {
  function TestConfig_MergeWithClientConfig (line 498) | func TestConfig_MergeWithClientConfig(t *testing.T) {

FILE: alerting/provider/signal/signal.go
  type Config (line 25) | type Config struct
    method Validate (line 31) | func (cfg *Config) Validate() error {
    method Merge (line 47) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 60) | type AlertProvider struct
    method Validate (line 77) | func (provider *AlertProvider) Validate() error {
    method Send (line 91) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 128) | func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpo...
    method GetDefaultAlert (line 163) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 168) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 193) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 71) | type Override struct
  type Body (line 121) | type Body struct

FILE: alerting/provider/signal/signal_test.go
  function TestAlertProvider_Validate (line 15) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_Send (line 52) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 144) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {

FILE: alerting/provider/signl4/signl4.go
  type Config (line 22) | type Config struct
    method Validate (line 26) | func (cfg *Config) Validate() error {
    method Merge (line 33) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 40) | type AlertProvider struct
    method Validate (line 57) | func (provider *AlertProvider) Validate() error {
    method Send (line 71) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 108) | func (provider *AlertProvider) buildRequestBody(ep *endpoint.Endpoint,...
    method GetDefaultAlert (line 151) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 156) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 181) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 51) | type Override struct
  type Body (line 99) | type Body struct

FILE: alerting/provider/signl4/signl4_test.go
  function TestAlertProvider_Validate (line 14) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_ValidateWithOverride (line 25) | func TestAlertProvider_ValidateWithOverride(t *testing.T) {
  function TestAlertProvider_Send (line 62) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_buildRequestBody (line 139) | func TestAlertProvider_buildRequestBody(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 224) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {
  function TestAlertProvider_GetConfig (line 233) | func TestAlertProvider_GetConfig(t *testing.T) {
  function TestAlertProvider_GetConfigWithInvalidAlertOverride (line 324) | func TestAlertProvider_GetConfigWithInvalidAlertOverride(t *testing.T) {
  function TestAlertProvider_ValidateWithDuplicateGroupOverride (line 356) | func TestAlertProvider_ValidateWithDuplicateGroupOverride(t *testing.T) {
  function TestAlertProvider_ValidateOverridesWithInvalidAlert (line 378) | func TestAlertProvider_ValidateOverridesWithInvalidAlert(t *testing.T) {

FILE: alerting/provider/slack/slack.go
  type Config (line 22) | type Config struct
    method Validate (line 27) | func (cfg *Config) Validate() error {
    method Merge (line 34) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 44) | type AlertProvider struct
    method Validate (line 61) | func (provider *AlertProvider) Validate() error {
    method Send (line 75) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 118) | func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpo...
    method GetDefaultAlert (line 167) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 172) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 197) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 55) | type Override struct
  type Body (line 98) | type Body struct
  type Attachment (line 103) | type Attachment struct
  type Field (line 111) | type Field struct

FILE: alerting/provider/slack/slack_test.go
  function TestAlertProvider_Validate (line 14) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_ValidateWithOverride (line 25) | func TestAlertProvider_ValidateWithOverride(t *testing.T) {
  function TestAlertProvider_Send (line 62) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_buildRequestBody (line 139) | func TestAlertProvider_buildRequestBody(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 234) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {
  function TestAlertProvider_GetConfig (line 243) | func TestAlertProvider_GetConfig(t *testing.T) {

FILE: alerting/provider/splunk/splunk.go
  type Config (line 24) | type Config struct
    method Validate (line 32) | func (cfg *Config) Validate() error {
    method Merge (line 42) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 61) | type AlertProvider struct
    method Validate (line 78) | func (provider *AlertProvider) Validate() error {
    method Send (line 92) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 139) | func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpo...
    method GetDefaultAlert (line 187) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 192) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 217) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 72) | type Override struct
  type Body (line 120) | type Body struct
  type Event (line 128) | type Event struct

FILE: alerting/provider/splunk/splunk_test.go
  function TestAlertProvider_Validate (line 14) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_Send (line 51) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 148) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {

FILE: alerting/provider/squadcast/squadcast.go
  type Config (line 22) | type Config struct
    method Validate (line 26) | func (cfg *Config) Validate() error {
    method Merge (line 33) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 40) | type AlertProvider struct
    method Validate (line 57) | func (provider *AlertProvider) Validate() error {
    method Send (line 71) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 107) | func (provider *AlertProvider) buildRequestBody(ep *endpoint.Endpoint,...
    method GetDefaultAlert (line 157) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 162) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 187) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 51) | type Override struct
  type Body (line 98) | type Body struct

FILE: alerting/provider/squadcast/squadcast_test.go
  function TestAlertProvider_Validate (line 15) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_Send (line 42) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 134) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {

FILE: alerting/provider/teams/teams.go
  type Config (line 22) | type Config struct
    method Validate (line 30) | func (cfg *Config) Validate() error {
    method Merge (line 37) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 50) | type AlertProvider struct
    method Validate (line 67) | func (provider *AlertProvider) Validate() error {
    method Send (line 81) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 119) | func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpo...
    method GetDefaultAlert (line 163) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 168) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 193) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 61) | type Override struct
  type Body (line 104) | type Body struct
  type Section (line 113) | type Section struct

FILE: alerting/provider/teams/teams_test.go
  function TestAlertProvider_Validate (line 14) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_ValidateWithOverride (line 25) | func TestAlertProvider_ValidateWithOverride(t *testing.T) {
  function TestAlertProvider_Send (line 62) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_buildRequestBody (line 139) | func TestAlertProvider_buildRequestBody(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 200) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {
  function TestAlertProvider_GetConfig (line 209) | func TestAlertProvider_GetConfig(t *testing.T) {

FILE: alerting/provider/teamsworkflows/teamsworkflows.go
  type Config (line 22) | type Config struct
    method Validate (line 27) | func (cfg *Config) Validate() error {
    method Merge (line 34) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 44) | type AlertProvider struct
    method Validate (line 61) | func (provider *AlertProvider) Validate() error {
    method Send (line 75) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 138) | func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpo...
    method GetDefaultAlert (line 230) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 235) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 260) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 55) | type Override struct
  type AdaptiveCardBody (line 99) | type AdaptiveCardBody struct
  type CardBody (line 107) | type CardBody struct
  type MSTeamsBody (line 121) | type MSTeamsBody struct
  type FactSetBody (line 126) | type FactSetBody struct
  type Fact (line 132) | type Fact struct

FILE: alerting/provider/teamsworkflows/teamsworkflows_test.go
  function TestAlertProvider_Validate (line 14) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_ValidateWithOverride (line 25) | func TestAlertProvider_ValidateWithOverride(t *testing.T) {
  function TestAlertProvider_Send (line 62) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_buildRequestBody (line 139) | func TestAlertProvider_buildRequestBody(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 200) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {
  function TestAlertProvider_GetConfig (line 209) | func TestAlertProvider_GetConfig(t *testing.T) {

FILE: alerting/provider/telegram/telegram.go
  constant ApiURL (line 17) | ApiURL = "https://api.telegram.org"
  type Config (line 25) | type Config struct
    method Validate (line 34) | func (cfg *Config) Validate() error {
    method Merge (line 47) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 66) | type AlertProvider struct
    method Validate (line 83) | func (provider *AlertProvider) Validate() error {
    method Send (line 97) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 128) | func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpo...
    method GetDefaultAlert (line 164) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 169) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 194) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 77) | type Override struct
  type Body (line 120) | type Body struct

FILE: alerting/provider/telegram/telegram_test.go
  function TestAlertProvider_Validate (line 14) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_Send (line 47) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_buildRequestBody (line 124) | func TestAlertProvider_buildRequestBody(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 200) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {
  function TestAlertProvider_GetConfig (line 209) | func TestAlertProvider_GetConfig(t *testing.T) {

FILE: alerting/provider/twilio/twilio.go
  type Config (line 26) | type Config struct
    method Validate (line 38) | func (cfg *Config) Validate() error {
    method Merge (line 54) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 76) | type AlertProvider struct
    method Validate (line 84) | func (provider *AlertProvider) Validate() error {
    method Send (line 89) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 114) | func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpo...
    method GetDefaultAlert (line 147) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 152) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 168) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...

FILE: alerting/provider/twilio/twilio_test.go
  function TestTwilioAlertProvider_IsValid (line 13) | func TestTwilioAlertProvider_IsValid(t *testing.T) {
  function TestAlertProvider_Send (line 31) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_buildRequestBody (line 108) | func TestAlertProvider_buildRequestBody(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 175) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {
  function TestAlertProvider_GetConfig (line 184) | func TestAlertProvider_GetConfig(t *testing.T) {

FILE: alerting/provider/vonage/vonage.go
  constant ApiURL (line 18) | ApiURL = "https://rest.nexmo.com/sms/json"
  type Config (line 28) | type Config struct
    method Validate (line 35) | func (cfg *Config) Validate() error {
    method Merge (line 51) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 67) | type AlertProvider struct
    method Validate (line 84) | func (provider *AlertProvider) Validate() error {
    method Send (line 98) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method sendSMS (line 115) | func (provider *AlertProvider) sendSMS(cfg *Config, to, message string...
    method buildMessage (line 170) | func (provider *AlertProvider) buildMessage(cfg *Config, ep *endpoint....
    method GetDefaultAlert (line 179) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 184) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 209) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 78) | type Override struct
  type Response (line 154) | type Response struct
  type Message (line 159) | type Message struct

FILE: alerting/provider/vonage/vonage_test.go
  function TestVonageAlertProvider_IsValid (line 15) | func TestVonageAlertProvider_IsValid(t *testing.T) {
  function TestVonageAlertProvider_IsValidWithOverride (line 33) | func TestVonageAlertProvider_IsValidWithOverride(t *testing.T) {
  function TestVonageAlertProvider_IsNotValidWithInvalidOverrideGroup (line 58) | func TestVonageAlertProvider_IsNotValidWithInvalidOverrideGroup(t *testi...
  function TestVonageAlertProvider_IsNotValidWithDuplicateOverrideGroup (line 83) | func TestVonageAlertProvider_IsNotValidWithDuplicateOverrideGroup(t *tes...
  function TestVonageAlertProvider_IsValidWithInvalidFrom (line 117) | func TestVonageAlertProvider_IsValidWithInvalidFrom(t *testing.T) {
  function TestVonageAlertProvider_IsValidWithInvalidTo (line 131) | func TestVonageAlertProvider_IsValidWithInvalidTo(t *testing.T) {
  function TestAlertProvider_Send (line 145) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_buildMessage (line 279) | func TestAlertProvider_buildMessage(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 339) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {
  function TestAlertProvider_GetConfig (line 348) | func TestAlertProvider_GetConfig(t *testing.T) {

FILE: alerting/provider/webex/webex.go
  type Config (line 22) | type Config struct
    method Validate (line 26) | func (cfg *Config) Validate() error {
    method Merge (line 33) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 40) | type AlertProvider struct
    method Validate (line 57) | func (provider *AlertProvider) Validate() error {
    method Send (line 71) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 105) | func (provider *AlertProvider) buildRequestBody(ep *endpoint.Endpoint,...
    method GetDefaultAlert (line 138) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 143) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 168) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 51) | type Override struct
  type Body (line 98) | type Body struct

FILE: alerting/provider/webex/webex_test.go
  function TestAlertProvider_Validate (line 15) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_Send (line 42) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 127) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {

FILE: alerting/provider/zapier/zapier.go
  type Config (line 23) | type Config struct
    method Validate (line 27) | func (cfg *Config) Validate() error {
    method Merge (line 34) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 41) | type AlertProvider struct
    method Validate (line 58) | func (provider *AlertProvider) Validate() error {
    method Send (line 72) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 117) | func (provider *AlertProvider) buildRequestBody(ep *endpoint.Endpoint,...
    method GetDefaultAlert (line 164) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 169) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 194) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 52) | type Override struct
  type Body (line 99) | type Body struct

FILE: alerting/provider/zapier/zapier_test.go
  function TestAlertProvider_Validate (line 15) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_Send (line 42) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 155) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {

FILE: alerting/provider/zulip/zulip.go
  type Config (line 25) | type Config struct
    method Validate (line 32) | func (cfg *Config) Validate() error {
    method Merge (line 48) | func (cfg *Config) Merge(override *Config) {
  type AlertProvider (line 64) | type AlertProvider struct
    method Validate (line 81) | func (provider *AlertProvider) Validate() error {
    method Send (line 95) | func (provider *AlertProvider) Send(ep *endpoint.Endpoint, alert *aler...
    method buildRequestBody (line 122) | func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpo...
    method GetDefaultAlert (line 150) | func (provider *AlertProvider) GetDefaultAlert() *alert.Alert {
    method GetConfig (line 155) | func (provider *AlertProvider) GetConfig(group string, alert *alert.Al...
    method ValidateOverrides (line 180) | func (provider *AlertProvider) ValidateOverrides(group string, alert *...
  type Override (line 75) | type Override struct

FILE: alerting/provider/zulip/zulip_test.go
  function TestAlertProvider_Validate (line 16) | func TestAlertProvider_Validate(t *testing.T) {
  function TestAlertProvider_buildRequestBody (line 93) | func TestAlertProvider_buildRequestBody(t *testing.T) {
  function TestAlertProvider_GetDefaultAlert (line 219) | func TestAlertProvider_GetDefaultAlert(t *testing.T) {
  function TestAlertProvider_Send (line 228) | func TestAlertProvider_Send(t *testing.T) {
  function TestAlertProvider_GetConfig (line 339) | func TestAlertProvider_GetConfig(t *testing.T) {

FILE: api/api.go
  type API (line 25) | type API struct
    method Router (line 43) | func (a *API) Router() *fiber.App {
    method createRouter (line 47) | func (a *API) createRouter(cfg *config.Config) *fiber.App {
  function New (line 29) | func New(cfg *config.Config) *API {

FILE: api/api_test.go
  function TestNew (line 14) | func TestNew(t *testing.T) {

FILE: api/badge.go
  constant badgeColorHexAwesome (line 21) | badgeColorHexAwesome  = "#40cc11"
  constant badgeColorHexGreat (line 22) | badgeColorHexGreat    = "#94cc11"
  constant badgeColorHexGood (line 23) | badgeColorHexGood     = "#ccd311"
  constant badgeColorHexPassable (line 24) | badgeColorHexPassable = "#ccb311"
  constant badgeColorHexBad (line 25) | badgeColorHexBad      = "#cc8111"
  constant badgeColorHexVeryBad (line 26) | badgeColorHexVeryBad  = "#c7130a"
  constant HealthStatusUp (line 30) | HealthStatusUp      = "up"
  constant HealthStatusDown (line 31) | HealthStatusDown    = "down"
  constant HealthStatusUnknown (line 32) | HealthStatusUnknown = "?"
  function UptimeBadge (line 42) | func UptimeBadge(c *fiber.Ctx) error {
  function ResponseTimeBadge (line 79) | func ResponseTimeBadge(cfg *config.Config) fiber.Handler {
  function HealthBadge (line 116) | func HealthBadge(c *fiber.Ctx) error {
  function HealthBadgeShields (line 145) | func HealthBadgeShields(c *fiber.Ctx) error {
  function generateUptimeBadgeSVG (line 178) | func generateUptimeBadgeSVG(duration string, uptime float64) []byte {
  function getBadgeColorFromUptime (line 231) | func getBadgeColorFromUptime(uptime float64) string {
  function generateResponseTimeBadgeSVG (line 246) | func generateResponseTimeBadgeSVG(duration string, averageResponseTime i...
  function getBadgeColorFromResponseTime (line 296) | func getBadgeColorFromResponseTime(responseTime int, key string, cfg *co...
  function generateHealthBadgeSVG (line 310) | func generateHealthBadgeSVG(healthStatus string) []byte {
  function generateHealthBadgeShields (line 359) | func generateHealthBadgeShields(healthStatus string) ([]byte, error) {
  function getBadgeColorFromHealth (line 370) | func getBadgeColorFromHealth(healthStatus string) string {
  function getBadgeShieldsColorFromHealth (line 379) | func getBadgeShieldsColorFromHealth(healthStatus string) string {

FILE: api/badge_test.go
  function TestBadge (line 17) | func TestBadge(t *testing.T) {
  function TestGetBadgeColorFromUptime (line 161) | func TestGetBadgeColorFromUptime(t *testing.T) {
  function TestGetBadgeColorFromResponseTime (line 216) | func TestGetBadgeColorFromResponseTime(t *testing.T) {
  function TestGetBadgeColorFromHealth (line 370) | func TestGetBadgeColorFromHealth(t *testing.T) {

FILE: api/cache.go
  constant cacheTTL (line 10) | cacheTTL = 10 * time.Second

FILE: api/chart.go
  constant timeFormat (line 19) | timeFormat = "3:04PM"
  function ResponseTimeChart (line 34) | func ResponseTimeChart(c *fiber.Ctx) error {
  function ResponseTimeHistory (line 130) | func ResponseTimeHistory(c *fiber.Ctx) error {

FILE: api/chart_test.go
  function TestResponseTimeChart (line 15) | func TestResponseTimeChart(t *testing.T) {
  function TestResponseTimeHistory (line 85) | func TestResponseTimeHistory(t *testing.T) {

FILE: api/config.go
  type ConfigHandler (line 12) | type ConfigHandler struct
    method GetConfig (line 17) | func (handler ConfigHandler) GetConfig(c *fiber.Ctx) error {

FILE: api/config_test.go
  function TestConfigHandler_ServeHTTP (line 12) | func TestConfigHandler_ServeHTTP(t *testing.T) {

FILE: api/custom_css.go
  type CustomCSSHandler (line 7) | type CustomCSSHandler struct
    method GetCustomCSS (line 11) | func (handler CustomCSSHandler) GetCustomCSS(c *fiber.Ctx) error {

FILE: api/endpoint_status.go
  function EndpointStatuses (line 22) | func EndpointStatuses(cfg *config.Config) fiber.Handler {
  function getEndpointStatusesFromRemoteInstances (line 54) | func getEndpointStatusesFromRemoteInstances(remoteConfig *remote.Config)...
  function EndpointStatus (line 87) | func EndpointStatus(cfg *config.Config) fiber.Handler {

FILE: api/endpoint_status_test.go
  function TestEndpointStatus (line 84) | func TestEndpointStatus(t *testing.T) {
  function TestEndpointStatuses (line 154) | func TestEndpointStatuses(t *testing.T) {

FILE: api/external_endpoint.go
  function CreateExternalEndpointResult (line 18) | func CreateExternalEndpointResult(cfg *config.Config) fiber.Handler {

FILE: api/external_endpoint_test.go
  function TestCreateExternalEndpointResult (line 18) | func TestCreateExternalEndpointResult(t *testing.T) {

FILE: api/raw.go
  function UptimeRaw (line 14) | func UptimeRaw(c *fiber.Ctx) error {
  function ResponseTimeRaw (line 49) | func ResponseTimeRaw(c *fiber.Ctx) error {

FILE: api/raw_test.go
  function TestRawDataEndpoint (line 16) | func TestRawDataEndpoint(t *testing.T) {

FILE: api/spa.go
  function SinglePageApplication (line 13) | func SinglePageApplication(uiConfig *ui.Config) fiber.Handler {

FILE: api/spa_test.go
  function TestSinglePageApplication (line 18) | func TestSinglePageApplication(t *testing.T) {

FILE: api/suite_status.go
  function SuiteStatuses (line 14) | func SuiteStatuses(cfg *config.Config) fiber.Handler {
  function SuiteStatus (line 37) | func SuiteStatus(cfg *config.Config) fiber.Handler {

FILE: api/suite_status_test.go
  function TestSuiteStatus (line 144) | func TestSuiteStatus(t *testing.T) {
  function TestSuiteStatus_SuiteNotInStoreButInConfig (line 214) | func TestSuiteStatus_SuiteNotInStoreButInConfig(t *testing.T) {
  function TestSuiteStatuses (line 366) | func TestSuiteStatuses(t *testing.T) {
  function TestSuiteStatuses_NoSuitesInStoreButExistInConfig (line 450) | func TestSuiteStatuses_NoSuitesInStoreButExistInConfig(t *testing.T) {
  function boolPtr (line 504) | func boolPtr(b bool) *bool {
  function contains (line 508) | func contains(s, substr string) bool {

FILE: api/util.go
  constant DefaultPage (line 11) | DefaultPage = 1
  constant DefaultPageSize (line 14) | DefaultPageSize = 50
  function extractPageAndPageSizeFromRequest (line 17) | func extractPageAndPageSizeFromRequest(c *fiber.Ctx, maximumNumberOfResu...

FILE: api/util_test.go
  function TestExtractPageAndPageSizeFromRequest (line 12) | func TestExtractPageAndPageSizeFromRequest(t *testing.T) {

FILE: client/client.go
  constant dnsPort (line 34) | dnsPort = 53
  function GetHTTPClient (line 47) | func GetHTTPClient(config *Config) *http.Client {
  function GetDomainExpiration (line 58) | func GetDomainExpiration(hostname string) (domainExpiration time.Duratio...
  function parseLocalAddressPlaceholder (line 92) | func parseLocalAddressPlaceholder(item string, localAddr net.Addr) string {
  function CanCreateNetworkConnection (line 98) | func CanCreateNetworkConnection(netType string, address string, body str...
  function CanCreateSCTPConnection (line 125) | func CanCreateSCTPConnection(address string, config *Config) bool {
  function CanPerformStartTLS (line 151) | func CanPerformStartTLS(address string, config *Config) (connected bool,...
  function CanPerformTLS (line 209) | func CanPerformTLS(address string, body string, config *Config) (connect...
  function CanCreateSSHConnection (line 251) | func CanCreateSSHConnection(address, username, password, privateKey stri...
  function CheckSSHBanner (line 289) | func CheckSSHBanner(address string, cfg *Config) (bool, int, error) {
  function ExecuteSSHCommand (line 318) | func ExecuteSSHCommand(sshClient *ssh.Client, body string, config *Confi...
  function Ping (line 355) | func Ping(address string, config *Config) (bool, time.Duration) {
  function ShouldRunPingerAsPrivileged (line 377) | func ShouldRunPingerAsPrivileged() bool {
  function QueryWebSocket (line 395) | func QueryWebSocket(address, body string, headers map[string]string, con...
  function QueryDNS (line 446) | func QueryDNS(queryType, queryName, url string) (connected bool, dnsRcod...
  function InjectHTTPClient (line 510) | func InjectHTTPClient(httpClient *http.Client) {
  function rdapQuery (line 515) | func rdapQuery(hostname string) (*whois.Response, error) {
  function reverseNameForIP (line 535) | func reverseNameForIP(ipStr string) (string, error) {

FILE: client/client_test.go
  function TestGetHTTPClient (line 19) | func TestGetHTTPClient(t *testing.T) {
  function TestRdapQuery (line 45) | func TestRdapQuery(t *testing.T) {
  function TestGetDomainExpiration (line 60) | func TestGetDomainExpiration(t *testing.T) {
  function TestPing (line 88) | func TestPing(t *testing.T) {
  function TestShouldRunPingerAsPrivileged (line 136) | func TestShouldRunPingerAsPrivileged(t *testing.T) {
  function TestCanPerformStartTLS (line 162) | func TestCanPerformStartTLS(t *testing.T) {
  function TestCanPerformTLS (line 223) | func TestCanPerformTLS(t *testing.T) {
  function TestCanCreateConnection (line 292) | func TestCanCreateConnection(t *testing.T) {
  function TestHttpClientProvidesOAuth2BearerToken (line 307) | func TestHttpClientProvidesOAuth2BearerToken(t *testing.T) {
  function TestQueryWebSocket (line 363) | func TestQueryWebSocket(t *testing.T) {
  function TestTlsRenegotiation (line 375) | func TestTlsRenegotiation(t *testing.T) {
  function TestQueryDNS (line 419) | func TestQueryDNS(t *testing.T) {
  function TestCheckSSHBanner (line 549) | func TestCheckSSHBanner(t *testing.T) {

FILE: client/config.go
  constant defaultTimeout (line 22) | defaultTimeout = 10 * time.Second
  function GetDefaultConfig (line 41) | func GetDefaultConfig() *Config {
  type Config (line 47) | type Config struct
    method ValidateAndSetDefaults (line 120) | func (c *Config) ValidateAndSetDefaults() error {
    method HasCustomDNSResolver (line 145) | func (c *Config) HasCustomDNSResolver() bool {
    method parseDNSResolver (line 150) | func (c *Config) parseDNSResolver() (*DNSResolverConfig, error) {
    method HasOAuth2Config (line 177) | func (c *Config) HasOAuth2Config() bool {
    method HasIAPConfig (line 182) | func (c *Config) HasIAPConfig() bool {
    method HasTLSConfig (line 187) | func (c *Config) HasTLSConfig() bool {
    method getHTTPClient (line 214) | func (c *Config) getHTTPClient() *http.Client {
  type DNSResolverConfig (line 89) | type DNSResolverConfig struct
  type OAuth2Config (line 96) | type OAuth2Config struct
    method isValid (line 197) | func (c *OAuth2Config) isValid() bool {
  type IAPConfig (line 104) | type IAPConfig struct
    method isValid (line 192) | func (c *IAPConfig) isValid() bool {
  type TLSConfig (line 109) | type TLSConfig struct
    method isValid (line 202) | func (t *TLSConfig) isValid() error {
  function validateIAPToken (line 289) | func validateIAPToken(ctx context.Context, c IAPConfig) bool {
  function configureIAP (line 310) | func configureIAP(httpClient *http.Client, c IAPConfig) *http.Client {
  function configureOAuth2 (line 327) | func configureOAuth2(httpClient *http.Client, c OAuth2Config) *http.Clie...
  function configureTLS (line 341) | func configureTLS(tlsConfig *tls.Config, c TLSConfig) *tls.Config {

FILE: client/config_test.go
  function TestConfig_getHTTPClient (line 10) | func TestConfig_getHTTPClient(t *testing.T) {
  function TestConfig_ValidateAndSetDefaults_withCustomDNSResolver (line 40) | func TestConfig_ValidateAndSetDefaults_withCustomDNSResolver(t *testing....
  function TestConfig_getHTTPClient_withCustomProxyURL (line 84) | func TestConfig_getHTTPClient_withCustomProxyURL(t *testing.T) {
  function TestConfig_TlsIsValid (line 110) | func TestConfig_TlsIsValid(t *testing.T) {

FILE: client/grpc.go
  function PerformGRPCHealthCheck (line 18) | func PerformGRPCHealthCheck(address string, useTLS bool, cfg *Config) (b...

FILE: config/announcement/announcement.go
  constant TypeOutage (line 11) | TypeOutage = "outage"
  constant TypeWarning (line 14) | TypeWarning = "warning"
  constant TypeInformation (line 17) | TypeInformation = "information"
  constant TypeOperational (line 20) | TypeOperational = "operational"
  constant TypeNone (line 23) | TypeNone = "none"
  type Announcement (line 47) | type Announcement struct
    method ValidateAndSetDefaults (line 63) | func (a *Announcement) ValidateAndSetDefaults() error {
  function SortByTimestamp (line 84) | func SortByTimestamp(announcements []*Announcement) {
  function ValidateAndSetDefaults (line 91) | func ValidateAndSetDefaults(announcements []*Announcement) error {

FILE: config/announcement/announcement_test.go
  function TestAnnouncement_ValidateAndSetDefaults (line 9) | func TestAnnouncement_ValidateAndSetDefaults(t *testing.T) {
  function TestAnnouncement_ValidateAndSetDefaults_AllTypes (line 89) | func TestAnnouncement_ValidateAndSetDefaults_AllTypes(t *testing.T) {
  function TestSortByTimestamp (line 109) | func TestSortByTimestamp(t *testing.T) {
  function TestSortByTimestamp_WithArchivedField (line 130) | func TestSortByTimestamp_WithArchivedField(t *testing.T) {
  function TestValidateAndSetDefaults_Slice (line 155) | func TestValidateAndSetDefaults_Slice(t *testing.T) {
  function TestAnnouncement_ArchivedFieldDefaults (line 219) | func TestAnnouncement_ArchivedFieldDefaults(t *testing.T) {
  function TestValidateAndSetDefaults_EmptySlice (line 236) | func TestValidateAndSetDefaults_EmptySlice(t *testing.T) {

FILE: config/config.go
  constant DefaultConfigurationFilePath (line 38) | DefaultConfigurationFilePath = "config/config.yaml"
  constant DefaultFallbackConfigurationFilePath (line 42) | DefaultFallbackConfigurationFilePath = "config/config.yml"
  constant DefaultConcurrency (line 45) | DefaultConcurrency = 3
  type Config (line 63) | type Config struct
    method GetUniqueExtraMetricLabels (line 133) | func (config *Config) GetUniqueExtraMetricLabels() []string {
    method GetEndpointByKey (line 152) | func (config *Config) GetEndpointByKey(key string) *endpoint.Endpoint {
    method GetExternalEndpointByKey (line 162) | func (config *Config) GetExternalEndpointByKey(key string) *endpoint.E...
    method HasLoadedConfigurationBeenModified (line 174) | func (config *Config) HasLoadedConfigurationBeenModified() bool {
    method UpdateLastFileModTime (line 193) | func (config *Config) UpdateLastFileModTime() {
  function LoadConfiguration (line 199) | func LoadConfiguration(configPath string) (*Config, error) {
  function walkConfigDir (line 261) | func walkConfigDir(path string, fn fs.WalkDirFunc) error {
  function parseAndValidateConfigBytes (line 282) | func parseAndValidateConfigBytes(yamlBytes []byte) (config *Config, err ...
  function ValidateConnectivityConfig (line 348) | func ValidateConnectivityConfig(config *Config) error {
  function ValidateTunnelingConfig (line 358) | func ValidateTunnelingConfig(config *Config) error {
  function resolveTunnelForClientConfig (line 383) | func resolveTunnelForClientConfig(config *Config, clientConfig *client.C...
  function ValidateAnnouncementsConfig (line 408) | func ValidateAnnouncementsConfig(config *Config) error {
  function ValidateRemoteConfig (line 419) | func ValidateRemoteConfig(config *Config) error {
  function ValidateStorageConfig (line 428) | func ValidateStorageConfig(config *Config) error {
  function ValidateMaintenanceConfig (line 443) | func ValidateMaintenanceConfig(config *Config) error {
  function ValidateUIConfig (line 454) | func ValidateUIConfig(config *Config) error {
  function ValidateWebConfig (line 465) | func ValidateWebConfig(config *Config) error {
  function ValidateEndpointsConfig (line 474) | func ValidateEndpointsConfig(config *Config) error {
  function ValidateSuitesConfig (line 505) | func ValidateSuitesConfig(config *Config) error {
  function ValidateUniqueKeys (line 540) | func ValidateUniqueKeys(config *Config) error {
  function ValidateSecurityConfig (line 577) | func ValidateSecurityConfig(config *Config) error {
  function ValidateAlertingConfig (line 591) | func ValidateAlertingConfig(alertingConfig *alerting.Config, endpoints [...
  function ValidateAndSetConcurrencyDefaults (line 688) | func ValidateAndSetConcurrencyDefaults(config *Config) {

FILE: config/config_test.go
  function TestLoadConfiguration (line 65) | func TestLoadConfiguration(t *testing.T) {
  function TestConfig_HasLoadedConfigurationBeenModified (line 296) | func TestConfig_HasLoadedConfigurationBeenModified(t *testing.T) {
  function TestParseAndValidateConfigBytes (line 348) | func TestParseAndValidateConfigBytes(t *testing.T) {
  function TestParseAndValidateConfigBytesDefault (line 502) | func TestParseAndValidateConfigBytesDefault(t *testing.T) {
  function TestParseAndValidateConfigBytesWithAddress (line 542) | func TestParseAndValidateConfigBytesWithAddress(t *testing.T) {
  function TestParseAndValidateConfigBytesWithPort (line 575) | func TestParseAndValidateConfigBytesWithPort(t *testing.T) {
  function TestParseAndValidateConfigBytesWithPortAndHost (line 608) | func TestParseAndValidateConfigBytesWithPortAndHost(t *testing.T) {
  function TestParseAndValidateConfigBytesWithInvalidPort (line 642) | func TestParseAndValidateConfigBytesWithInvalidPort(t *testing.T) {
  function TestParseAndValidateConfigBytesWithMetricsAndCustomUserAgentHeader (line 658) | func TestParseAndValidateConfigBytesWithMetricsAndCustomUserAgentHeader(...
  function TestParseAndValidateConfigBytesWithMetricsAndHostAndPort (line 695) | func TestParseAndValidateConfigBytesWithMetricsAndHostAndPort(t *testing...
  function TestParseAndValidateBadConfigBytes (line 733) | func TestParseAndValidateBadConfigBytes(t *testing.T) {
  function TestParseAndValidateConfigBytesWithAlerting (line 749) | func TestParseAndValidateConfigBytesWithAlerting(t *testing.T) {
  function TestParseAndValidateConfigBytesWithAlertingAndDefaultAlert (line 937) | func TestParseAndValidateConfigBytesWithAlertingAndDefaultAlert(t *testi...
  function TestParseAndValidateConfigBytesWithAlertingAndDefaultAlertAndMultipleAlertsOfSameTypeWithOverriddenParameters (line 1369) | func TestParseAndValidateConfigBytesWithAlertingAndDefaultAlertAndMultip...
  function TestParseAndValidateConfigBytesWithInvalidPagerDutyAlertingConfig (line 1450) | func TestParseAndValidateConfigBytesWithInvalidPagerDutyAlertingConfig(t...
  function TestParseAndValidateConfigBytesWithInvalidPushoverAlertingConfig (line 1477) | func TestParseAndValidateConfigBytesWithInvalidPushoverAlertingConfig(t ...
  function TestParseAndValidateConfigBytesWithCustomAlertingConfig (line 1504) | func TestParseAndValidateConfigBytesWithCustomAlertingConfig(t *testing....
  function TestParseAndValidateConfigBytesWithCustomAlertingConfigAndCustomPlaceholderValues (line 1548) | func TestParseAndValidateConfigBytesWithCustomAlertingConfigAndCustomPla...
  function TestParseAndValidateConfigBytesWithCustomAlertingConfigAndOneCustomPlaceholderValue (line 1591) | func TestParseAndValidateConfigBytesWithCustomAlertingConfigAndOneCustom...
  function TestParseAndValidateConfigBytesWithInvalidEndpointName (line 1632) | func TestParseAndValidateConfigBytesWithInvalidEndpointName(t *testing.T) {
  function TestParseAndValidateConfigBytesWithDuplicateEndpointName (line 1645) | func TestParseAndValidateConfigBytesWithDuplicateEndpointName(t *testing...
  function TestParseAndValidateConfigBytesWithInvalidStorageConfig (line 1738) | func TestParseAndValidateConfigBytesWithInvalidStorageConfig(t *testing....
  function TestParseAndValidateConfigBytesWithInvalidYAML (line 1753) | func TestParseAndValidateConfigBytesWithInvalidYAML(t *testing.T) {
  function TestParseAndValidateConfigBytesWithInvalidSecurityConfig (line 1768) | func TestParseAndValidateConfigBytesWithInvalidSecurityConfig(t *testing...
  function TestParseAndValidateConfigBytesWithValidSecurityConfig (line 1785) | func TestParseAndValidateConfigBytesWithValidSecurityConfig(t *testing.T) {
  function TestParseAndValidateConfigBytesWithLiteralDollarSign (line 1822) | func TestParseAndValidateConfigBytesWithLiteralDollarSign(t *testing.T) {
  function TestParseAndValidateConfigBytesWithNoEndpoints (line 1849) | func TestParseAndValidateConfigBytesWithNoEndpoints(t *testing.T) {
  function TestGetAlertingProviderByAlertType (line 1856) | func TestGetAlertingProviderByAlertType(t *testing.T) {
  function TestConfig_GetUniqueExtraMetricLabels (line 1953) | func TestConfig_GetUniqueExtraMetricLabels(t *testing.T) {
  function TestParseAndValidateConfigBytesWithDuplicateKeysAcrossEntityTypes (line 2064) | func TestParseAndValidateConfigBytesWithDuplicateKeysAcrossEntityTypes(t...
  function TestParseAndValidateConfigBytesWithSuites (line 2231) | func TestParseAndValidateConfigBytesWithSuites(t *testing.T) {
  function TestValidateTunnelingConfig (line 2443) | func TestValidateTunnelingConfig(t *testing.T) {
  function TestResolveTunnelForClientConfig (line 2569) | func TestResolveTunnelForClientConfig(t *testing.T) {

FILE: config/connectivity/connectivity.go
  type Config (line 17) | type Config struct
    method ValidateAndSetDefaults (line 21) | func (c *Config) ValidateAndSetDefaults() error {
  type Checker (line 36) | type Checker struct
    method Check (line 44) | func (c *Checker) Check() bool {
    method IsConnected (line 49) | func (c *Checker) IsConnected() bool {

FILE: config/connectivity/connectivity_test.go
  function TestConfig (line 9) | func TestConfig(t *testing.T) {
  function TestChecker_IsConnected (line 57) | func TestChecker_IsConnected(t *testing.T) {

FILE: config/endpoint/common.go
  function validateEndpointNameGroupAndAlerts (line 19) | func validateEndpointNameGroupAndAlerts(name, group string, alerts []*al...

FILE: config/endpoint/common_test.go
  function TestValidateEndpointNameGroupAndAlerts (line 10) | func TestValidateEndpointNameGroupAndAlerts(t *testing.T) {

FILE: config/endpoint/condition.go
  constant maximumLengthBeforeTruncatingWhenComparedWithPattern (line 20) | maximumLengthBeforeTruncatingWhenComparedWithPattern = 25
  type Condition (line 24) | type Condition
    method Validate (line 27) | func (c Condition) Validate() error {
    method evaluate (line 37) | func (c Condition) evaluate(result *Result, dontResolveFailedCondition...
    method hasBodyPlaceholder (line 96) | func (c Condition) hasBodyPlaceholder() bool {
    method hasDomainExpirationPlaceholder (line 102) | func (c Condition) hasDomainExpirationPlaceholder() bool {
    method hasIPPlaceholder (line 108) | func (c Condition) hasIPPlaceholder() bool {
  function isEqual (line 117) | func isEqual(first, second string) bool {
  function sanitizeAndResolveWithContext (line 174) | func sanitizeAndResolveWithContext(elements []string, result *Result, co...
  function sanitizeAndResolveNumericalWithContext (line 194) | func sanitizeAndResolveNumericalWithContext(list []string, result *Resul...
  function prettifyNumericalParameters (line 217) | func prettifyNumericalParameters(parameters []string, resolvedParameters...
  function formatDuration (line 241) | func formatDuration(d time.Duration) string {
  function prettify (line 259) | func prettify(parameters []string, resolvedParameters []string, operator...

FILE: config/endpoint/condition_bench_test.go
  function BenchmarkCondition_evaluateWithBodyStringAny (line 7) | func BenchmarkCondition_evaluateWithBodyStringAny(b *testing.B) {
  function BenchmarkCondition_evaluateWithBodyStringAnyFailure (line 16) | func BenchmarkCondition_evaluateWithBodyStringAnyFailure(b *testing.B) {
  function BenchmarkCondition_evaluateWithBodyString (line 25) | func BenchmarkCondition_evaluateWithBodyString(b *testing.B) {
  function BenchmarkCondition_evaluateWithBodyStringFailure (line 34) | func BenchmarkCondition_evaluateWithBodyStringFailure(b *testing.B) {
  function BenchmarkCondition_evaluateWithBodyStringFailureInvalidPath (line 43) | func BenchmarkCondition_evaluateWithBodyStringFailureInvalidPath(b *test...
  function BenchmarkCondition_evaluateWithBodyStringLen (line 52) | func BenchmarkCondition_evaluateWithBodyStringLen(b *testing.B) {
  function BenchmarkCondition_evaluateWithBodyStringLenFailure (line 61) | func BenchmarkCondition_evaluateWithBodyStringLenFailure(b *testing.B) {
  function BenchmarkCondition_evaluateWithStatus (line 70) | func BenchmarkCondition_evaluateWithStatus(b *testing.B) {
  function BenchmarkCondition_evaluateWithStatusFailure (line 79) | func BenchmarkCondition_evaluateWithStatusFailure(b *testing.B) {

FILE: config/endpoint/condition_result.go
  type ConditionResult (line 4) | type ConditionResult struct

FILE: config/endpoint/condition_test.go
  function TestCondition_Validate (line 13) | func TestCondition_Validate(t *testing.T) {
  function TestCondition_evaluate (line 59) | func TestCondition_evaluate(t *testing.T) {
  function TestCondition_evaluateWithInvalidOperator (line 780) | func TestCondition_evaluateWithInvalidOperator(t *testing.T) {
  function TestConditionEvaluateWithInvalidContextPlaceholder (line 792) | func TestConditionEvaluateWithInvalidContextPlaceholder(t *testing.T) {
  function TestConditionEvaluateWithValidContextPlaceholder (line 818) | func TestConditionEvaluateWithValidContextPlaceholder(t *testing.T) {
  function TestConditionEvaluateWithMixedValidAndInvalidContext (line 841) | func TestConditionEvaluateWithMixedValidAndInvalidContext(t *testing.T) {

FILE: config/endpoint/dns/dns.go
  type Config (line 19) | type Config struct
    method ValidateAndSetDefault (line 27) | func (d *Config) ValidateAndSetDefault() error {

FILE: config/endpoint/dns/dns_test.go
  function TestConfig_ValidateAndSetDefault (line 7) | func TestConfig_ValidateAndSetDefault(t *testing.T) {
  function TestConfig_ValidateAndSetDefaultsWithInvalidDNSQueryType (line 18) | func TestConfig_ValidateAndSetDefaultsWithInvalidDNSQueryType(t *testing...

FILE: config/endpoint/endpoint.go
  type Type (line 31) | type Type
  constant HostHeader (line 35) | HostHeader = "Host"
  constant ContentTypeHeader (line 38) | ContentTypeHeader = "Content-Type"
  constant UserAgentHeader (line 41) | UserAgentHeader = "User-Agent"
  constant GatusUserAgent (line 44) | GatusUserAgent = "Gatus/1.0"
  constant TypeDNS (line 46) | TypeDNS      Type = "DNS"
  constant TypeTCP (line 47) | TypeTCP      Type = "TCP"
  constant TypeSCTP (line 48) | TypeSCTP     Type = "SCTP"
  constant TypeUDP (line 49) | TypeUDP      Type = "UDP"
  constant TypeICMP (line 50) | TypeICMP     Type = "ICMP"
  constant TypeSTARTTLS (line 51) | TypeSTARTTLS Type = "STARTTLS"
  constant TypeTLS (line 52) | TypeTLS      Type = "TLS"
  constant TypeHTTP (line 53) | TypeHTTP     Type = "HTTP"
  constant TypeGRPC (line 54) | TypeGRPC     Type = "GRPC"
  constant TypeWS (line 55) | TypeWS       Type = "WEBSOCKET"
  constant TypeSSH (line 56) | TypeSSH      Type = "SSH"
  constant TypeUNKNOWN (line 57) | TypeUNKNOWN  Type = "UNKNOWN"
  type Endpoint (line 81) | type Endpoint struct
    method IsEnabled (line 156) | func (e *Endpoint) IsEnabled() bool {
    method Type (line 164) | func (e *Endpoint) Type() Type {
    method ValidateAndSetDefaults (line 194) | func (e *Endpoint) ValidateAndSetDefaults() error {
    method DisplayName (line 267) | func (e *Endpoint) DisplayName() string {
    method Key (line 275) | func (e *Endpoint) Key() string {
    method Close (line 282) | func (e *Endpoint) Close() {
    method EvaluateHealth (line 289) | func (e *Endpoint) EvaluateHealth() *Result {
    method EvaluateHealthWithContext (line 294) | func (e *Endpoint) EvaluateHealthWithContext(context *gontext.Gontext)...
    method preprocessWithContext (line 370) | func (e *Endpoint) preprocessWithContext(result *Result, context *gont...
    method getParsedBody (line 418) | func (e *Endpoint) getParsedBody() string {
    method getIP (line 441) | func (e *Endpoint) getIP(result *Result) {
    method call (line 450) | func (e *Endpoint) call(result *Result) {
    method buildHTTPRequest (line 570) | func (e *Endpoint) buildHTTPRequest() *http.Request {
    method needsToReadBody (line 592) | func (e *Endpoint) needsToReadBody() bool {
    method needsToRetrieveDomainExpiration (line 610) | func (e *Endpoint) needsToRetrieveDomainExpiration() bool {
    method needsToRetrieveIP (line 620) | func (e *Endpoint) needsToRetrieveIP() bool {
  function replaceContextPlaceholders (line 396) | func replaceContextPlaceholders(input string, ctx *gontext.Gontext) (str...
  function hasHeader (line 630) | func hasHeader(headers map[string]string, name string) bool {

FILE: config/endpoint/endpoint_test.go
  function TestHasHeader (line 24) | func TestHasHeader(t *testing.T) {
  function TestEndpoint (line 48) | func TestEndpoint(t *testing.T) {
  function TestEndpoint_ResolveSuccessfulConditions (line 295) | func TestEndpoint_ResolveSuccessfulConditions(t *testing.T) {
  function TestEndpoint_IsEnabled (line 320) | func TestEndpoint_IsEnabled(t *testing.T) {
  function TestEndpoint_Type (line 332) | func TestEndpoint_Type(t *testing.T) {
  function TestEndpoint_ValidateAndSetDefaults (line 442) | func TestEndpoint_ValidateAndSetDefaults(t *testing.T) {
  function TestEndpoint_ValidateAndSetDefaultsWithInvalidCondition (line 498) | func TestEndpoint_ValidateAndSetDefaultsWithInvalidCondition(t *testing....
  function TestEndpoint_ValidateAndSetDefaultsWithClientConfig (line 509) | func TestEndpoint_ValidateAndSetDefaultsWithClientConfig(t *testing.T) {
  function TestEndpoint_ValidateAndSetDefaultsWithDNS (line 539) | func TestEndpoint_ValidateAndSetDefaultsWithDNS(t *testing.T) {
  function TestEndpoint_ValidateAndSetDefaultsWithSSH (line 558) | func TestEndpoint_ValidateAndSetDefaultsWithSSH(t *testing.T) {
  function TestEndpoint_ValidateAndSetDefaultsWithSimpleErrors (line 619) | func TestEndpoint_ValidateAndSetDefaultsWithSimpleErrors(t *testing.T) {
  function TestEndpoint_buildHTTPRequest (line 676) | func TestEndpoint_buildHTTPRequest(t *testing.T) {
  function TestEndpoint_buildHTTPRequestWithCustomUserAgent (line 699) | func TestEndpoint_buildHTTPRequestWithCustomUserAgent(t *testing.T) {
  function TestEndpoint_buildHTTPRequestWithHostHeader (line 725) | func TestEndpoint_buildHTTPRequestWithHostHeader(t *testing.T) {
  function TestEndpoint_buildHTTPRequestWithLowercaseUserAgent (line 749) | func TestEndpoint_buildHTTPRequestWithLowercaseUserAgent(t *testing.T) {
  function TestEndpoint_buildHTTPRequestWithLowercaseContentType (line 772) | func TestEndpoint_buildHTTPRequestWithLowercaseContentType(t *testing.T) {
  function TestEndpoint_buildHTTPRequestWithLowercaseHostHeader (line 798) | func TestEndpoint_buildHTTPRequestWithLowercaseHostHeader(t *testing.T) {
  function TestEndpoint_buildHTTPRequestWithGraphQLEnabled (line 819) | func TestEndpoint_buildHTTPRequestWithGraphQLEnabled(t *testing.T) {
  function TestIntegrationEvaluateHealth (line 853) | func TestIntegrationEvaluateHealth(t *testing.T) {
  function TestIntegrationEvaluateHealthWithErrorAndHideURL (line 880) | func TestIntegrationEvaluateHealthWithErrorAndHideURL(t *testing.T) {
  function TestIntegrationEvaluateHealthForDNS (line 908) | func TestIntegrationEvaluateHealthForDNS(t *testing.T) {
  function TestIntegrationEvaluateHealthForSSH (line 936) | func TestIntegrationEvaluateHealthForSSH(t *testing.T) {
  function TestIntegrationEvaluateHealthForICMP (line 985) | func TestIntegrationEvaluateHealthForICMP(t *testing.T) {
  function TestEndpoint_DisplayName (line 1007) | func TestEndpoint_DisplayName(t *testing.T) {
  function TestEndpoint_getIP (line 1016) | func TestEndpoint_getIP(t *testing.T) {
  function TestEndpoint_needsToReadBody (line 1029) | func TestEndpoint_needsToReadBody(t *testing.T) {
  function TestEndpoint_needsToRetrieveDomainExpiration (line 1087) | func TestEndpoint_needsToRetrieveDomainExpiration(t *testing.T) {
  function TestEndpoint_needsToRetrieveIP (line 1096) | func TestEndpoint_needsToRetrieveIP(t *testing.T) {
  function TestEndpoint_preprocessWithContext (line 1105) | func TestEndpoint_preprocessWithContext(t *testing.T) {
  function TestEndpoint_HideUIFeatures (line 1586) | func TestEndpoint_HideUIFeatures(t *testing.T) {

FILE: config/endpoint/event.go
  type Event (line 8) | type Event struct
  type EventType (line 17) | type EventType
  function NewEventFromResult (line 31) | func NewEventFromResult(result *Result) *Event {

FILE: config/endpoint/event_test.go
  function TestNewEventFromResult (line 7) | func TestNewEventFromResult(t *testing.T) {

FILE: config/endpoint/external_endpoint.go
  type ExternalEndpoint (line 24) | type ExternalEndpoint struct
    method ValidateAndSetDefaults (line 54) | func (externalEndpoint *ExternalEndpoint) ValidateAndSetDefaults() err...
    method IsEnabled (line 69) | func (externalEndpoint *ExternalEndpoint) IsEnabled() bool {
    method DisplayName (line 77) | func (externalEndpoint *ExternalEndpoint) DisplayName() string {
    method Key (line 85) | func (externalEndpoint *ExternalEndpoint) Key() string {
    method ToEndpoint (line 90) | func (externalEndpoint *ExternalEndpoint) ToEndpoint() *Endpoint {

FILE: config/endpoint/external_endpoint_test.go
  function TestExternalEndpoint_ValidateAndSetDefaults (line 12) | func TestExternalEndpoint_ValidateAndSetDefaults(t *testing.T) {
  function TestExternalEndpoint_IsEnabled (line 117) | func TestExternalEndpoint_IsEnabled(t *testing.T) {
  function TestExternalEndpoint_DisplayName (line 155) | func TestExternalEndpoint_DisplayName(t *testing.T) {
  function TestExternalEndpoint_Key (line 197) | func TestExternalEndpoint_Key(t *testing.T) {
  function TestExternalEndpoint_ToEndpoint (line 238) | func TestExternalEndpoint_ToEndpoint(t *testing.T) {
  function TestExternalEndpoint_ValidationEdgeCases (line 325) | func TestExternalEndpoint_ValidationEdgeCases(t *testing.T) {
  function boolPtr (line 378) | func boolPtr(b bool) *bool {

FILE: config/endpoint/heartbeat/heartbeat.go
  type Config (line 7) | type Config struct

FILE: config/endpoint/placeholder.go
  constant StatusPlaceholder (line 17) | StatusPlaceholder = "[STATUS]"
  constant IPPlaceholder (line 22) | IPPlaceholder = "[IP]"
  constant DNSRCodePlaceholder (line 27) | DNSRCodePlaceholder = "[DNS_RCODE]"
  constant ResponseTimePlaceholder (line 32) | ResponseTimePlaceholder = "[RESPONSE_TIME]"
  constant BodyPlaceholder (line 37) | BodyPlaceholder = "[BODY]"
  constant ConnectedPlaceholder (line 42) | ConnectedPlaceholder = "[CONNECTED]"
  constant CertificateExpirationPlaceholder (line 47) | CertificateExpirationPlaceholder = "[CERTIFICATE_EXPIRATION]"
  constant DomainExpirationPlaceholder (line 50) | DomainExpirationPlaceholder = "[DOMAIN_EXPIRATION]"
  constant ContextPlaceholder (line 54) | ContextPlaceholder = "[CONTEXT]"
  constant LengthFunctionPrefix (line 62) | LengthFunctionPrefix = "len("
  constant HasFunctionPrefix (line 67) | HasFunctionPrefix = "has("
  constant PatternFunctionPrefix (line 72) | PatternFunctionPrefix = "pat("
  constant AnyFunctionPrefix (line 77) | AnyFunctionPrefix = "any("
  constant FunctionSuffix (line 80) | FunctionSuffix = ")"
  constant InvalidConditionElementSuffix (line 86) | InvalidConditionElementSuffix = "(INVALID)"
  type functionType (line 90) | type functionType
  constant noFunction (line 96) | noFunction functionType = iota
  constant functionLen (line 97) | functionLen
  constant functionHas (line 98) | functionHas
  function ResolvePlaceholder (line 126) | func ResolvePlaceholder(placeholder string, result *Result, ctx *gontext...
  function extractFunctionWrapper (line 194) | func extractFunctionWrapper(placeholder string) (functionType, string) {
  function resolveJSONPathPlaceholder (line 207) | func resolveJSONPathPlaceholder(placeholder string, fn functionType, ori...
  function resolveContextPlaceholder (line 232) | func resolveContextPlaceholder(placeholder string, fn functionType, orig...
  function formatWithFunction (line 264) | func formatWithFunction(value string, fn functionType) string {

FILE: config/endpoint/placeholder_test.go
  function TestResolvePlaceholder (line 10) | func TestResolvePlaceholder(t *testing.T) {
  function TestResolvePlaceholderWithoutContext (line 97) | func TestResolvePlaceholderWithoutContext(t *testing.T) {

FILE: config/endpoint/result.go
  type Result (line 9) | type Result struct
    method AddError (line 69) | func (r *Result) AddError(error string) {

FILE: config/endpoint/result_test.go
  function TestResult_AddError (line 7) | func TestResult_AddError(t *testing.T) {

FILE: config/endpoint/ssh/ssh.go
  type Config (line 15) | type Config struct
    method Validate (line 22) | func (cfg *Config) Validate() error {

FILE: config/endpoint/ssh/ssh_test.go
  function TestSSH_validatePasswordCfg (line 8) | func TestSSH_validatePasswordCfg(t *testing.T) {
  function TestSSH_validatePrivateKeyCfg (line 25) | func TestSSH_validatePrivateKeyCfg(t *testing.T) {

FILE: config/endpoint/status.go
  type Status (line 7) | type Status struct
  function NewStatus (line 32) | func NewStatus(group, name string) *Status {

FILE: config/endpoint/status_test.go
  function TestNewEndpointStatus (line 7) | func TestNewEndpointStatus(t *testing.T) {

FILE: config/endpoint/ui/ui.go
  type Config (line 6) | type Config struct
    method ValidateAndSetDefaults (line 45) | func (config *Config) ValidateAndSetDefaults() error {
  type Badge (line 32) | type Badge struct
  type ResponseTime (line 36) | type ResponseTime struct
  function GetDefaultConfig (line 62) | func GetDefaultConfig() *Config {

FILE: config/endpoint/ui/ui_test.go
  function TestValidateAndSetDefaults (line 8) | func TestValidateAndSetDefaults(t *testing.T) {

FILE: config/endpoint/uptime.go
  type Uptime (line 5) | type Uptime struct
  type HourlyUptimeStatistics (line 13) | type HourlyUptimeStatistics struct
  function NewUptime (line 20) | func NewUptime() *Uptime {

FILE: config/gontext/gontext.go
  type Gontext (line 16) | type Gontext struct
    method Get (line 37) | func (g *Gontext) Get(path string) (interface{}, error) {
    method Set (line 58) | func (g *Gontext) Set(path string, value interface{}) error {
    method GetAll (line 91) | func (g *Gontext) GetAll() map[string]interface{} {
  function New (line 22) | func New(initial map[string]interface{}) *Gontext {
  function deepCopyValue (line 103) | func deepCopyValue(v interface{}) interface{} {

FILE: config/gontext/gontext_test.go
  function TestNew (line 8) | func TestNew(t *testing.T) {
  function TestGontext_Get (line 73) | func TestGontext_Get(t *testing.T) {
  function TestGontext_Set (line 181) | func TestGontext_Set(t *testing.T) {
  function TestGontext_SetOverrideBehavior (line 254) | func TestGontext_SetOverrideBehavior(t *testing.T) {
  function TestGontext_GetAll (line 292) | func TestGontext_GetAll(t *testing.T) {
  function TestGontext_ConcurrentAccess (line 335) | func TestGontext_ConcurrentAccess(t *testing.T) {
  function TestDeepCopyValue (line 374) | func TestDeepCopyValue(t *testing.T) {

FILE: config/key/key.go
  function ConvertGroupAndNameToKey (line 6) | func ConvertGroupAndNameToKey(groupName, name string) string {
  function sanitize (line 10) | func sanitize(s string) string {

FILE: config/key/key_bench_test.go
  function BenchmarkConvertGroupAndNameToKey (line 7) | func BenchmarkConvertGroupAndNameToKey(b *testing.B) {

FILE: config/key/key_test.go
  function TestConvertGroupAndNameToKey (line 5) | func TestConvertGroupAndNameToKey(t *testing.T) {

FILE: config/maintenance/maintenance.go
  type Config (line 34) | type Config struct
    method IsEnabled (line 57) | func (c *Config) IsEnabled() bool {
    method ValidateAndSetDefaults (line 68) | func (c *Config) ValidateAndSetDefaults() error {
    method IsUnderMaintenance (line 100) | func (c *Config) IsUnderMaintenance() bool {
  function GetDefaultConfig (line 49) | func GetDefaultConfig() *Config {
  function hhmmToDuration (line 127) | func hhmmToDuration(s string) (time.Duration, error) {
  function extractNumericalValueFromPotentiallyZeroPaddedString (line 146) | func extractNumericalValueFromPotentiallyZeroPaddedString(s string) (int...

FILE: config/maintenance/maintenance_test.go
  function TestGetDefaultConfig (line 11) | func TestGetDefaultConfig(t *testing.T) {
  function TestConfig_ValidateAndSetDefaults (line 17) | func TestConfig_ValidateAndSetDefaults(t *testing.T) {
  function TestConfig_IsUnderMaintenance (line 176) | func TestConfig_IsUnderMaintenance(t *testing.T) {
  function normalizeHour (line 346) | func normalizeHour(hour int) int {
  function inTimezone (line 353) | func inTimezone(passedTime time.Time, timezone string, t *testing.T) tim...

FILE: config/remote/remote.go
  type Config (line 11) | type Config struct
    method ValidateAndSetDefaults (line 24) | func (c *Config) ValidateAndSetDefaults() error {
  type Instance (line 19) | type Instance struct

FILE: config/suite/result.go
  type Result (line 10) | type Result struct
    method AddError (line 37) | func (r *Result) AddError(err string) {
    method CalculateSuccess (line 42) | func (r *Result) CalculateSuccess() {

FILE: config/suite/suite.go
  type Suite (line 36) | type Suite struct
    method IsEnabled (line 60) | func (s *Suite) IsEnabled() bool {
    method Key (line 68) | func (s *Suite) Key() string {
    method ValidateAndSetDefaults (line 73) | func (s *Suite) ValidateAndSetDefaults() error {
    method Execute (line 116) | func (s *Suite) Execute() *Result {
  function StoreResultValues (line 174) | func StoreResultValues(ctx *gontext.Gontext, mappings map[string]string,...
  function extractValueForStorage (line 201) | func extractValueForStorage(placeholder string, result *endpoint.Result)...

FILE: config/suite/suite_status.go
  type Status (line 4) | type Status struct
  function NewStatus (line 19) | func NewStatus(s *Suite) *Status {

FILE: config/suite/suite_test.go
  function TestSuite_ValidateAndSetDefaults (line 12) | func TestSuite_ValidateAndSetDefaults(t *testing.T) {
  function TestSuite_IsEnabled (line 100) | func TestSuite_IsEnabled(t *testing.T) {
  function TestSuite_Key (line 132) | func TestSuite_Key(t *testing.T) {
  function TestSuite_DefaultValues (line 164) | func TestSuite_DefaultValues(t *testing.T) {
  function boolPtr (line 193) | func boolPtr(b bool) *bool {
  function TestStoreResultValues (line 197) | func TestStoreResultValues(t *testing.T) {
  function TestStoreResultValuesWithInvalidPath (line 244) | func TestStoreResultValuesWithInvalidPath(t *testing.T) {
  function TestSuite_ExecuteWithAlwaysRunEndpoints (line 288) | func TestSuite_ExecuteWithAlwaysRunEndpoints(t *testing.T) {
  function TestSuite_ExecuteWithoutAlwaysRunEndpoints (line 346) | func TestSuite_ExecuteWithoutAlwaysRunEndpoints(t *testing.T) {
  function TestResult_AddError (line 391) | func TestResult_AddError(t *testing.T) {
  function TestResult_CalculateSuccess (line 415) | func TestResult_CalculateSuccess(t *testing.T) {

FILE: config/tunneling/sshtunnel/sshtunnel.go
  type Config (line 13) | type Config struct
    method ValidateAndSetDefaults (line 23) | func (c *Config) ValidateAndSetDefaults() error {
  type SSHTunnel (line 43) | type SSHTunnel struct
    method Connect (line 72) | func (t *SSHTunnel) Connect() error {
    method connectUnsafe (line 80) | func (t *SSHTunnel) connectUnsafe() error {
    method Close (line 102) | func (t *SSHTunnel) Close() error {
    method Dial (line 114) | func (t *SSHTunnel) Dial(network, addr string) (net.Conn, error) {
  function New (line 53) | func New(config *Config) *SSHTunnel {

FILE: config/tunneling/sshtunnel/sshtunnel_test.go
  function TestConfig_ValidateAndSetDefaults (line 7) | func TestConfig_ValidateAndSetDefaults(t *testing.T) {
  function TestNew (line 123) | func TestNew(t *testing.T) {
  function TestSSHTunnel_Close (line 140) | func TestSSHTunnel_Close(t *testing.T) {

FILE: config/tunneling/tunneling.go
  type Config (line 12) | type Config struct
    method ValidateAndSetDefaults (line 21) | func (tc *Config) ValidateAndSetDefaults() error {
    method GetTunnel (line 34) | func (tc *Config) GetTunnel(name string) (*sshtunnel.SSHTunnel, error) {
    method Close (line 56) | func (tc *Config) Close() error {

FILE: config/tunneling/tunneling_test.go
  function TestConfig_ValidateAndSetDefaults (line 9) | func TestConfig_ValidateAndSetDefaults(t *testing.T) {
  function TestConfig_GetTunnel (line 105) | func TestConfig_GetTunnel(t *testing.T) {
  function TestConfig_Close (line 151) | func TestConfig_Close(t *testing.T) {

FILE: config/ui/ui.go
  constant defaultTitle (line 13) | defaultTitle                = "Health Dashboard | Gatus"
  constant defaultDescription (line 14) | defaultDescription          = "Gatus is an advanced automated status pag...
  constant defaultHeader (line 15) | defaultHeader               = "Gatus"
  constant defaultDashboardHeading (line 16) | defaultDashboardHeading     = "Health Dashboard"
  constant defaultDashboardSubheading (line 17) | defaultDashboardSubheading  = "Monitor the health of your endpoints in r...
  constant defaultLogo (line 18) | defaultLogo                 = ""
  constant defaultLink (line 19) | defaultLink                 = ""
  constant defaultFavicon (line 20) | defaultFavicon              = "/favicon.ico"
  constant defaultFavicon16 (line 21) | defaultFavicon16            = "/favicon-16x16.png"
  constant defaultFavicon32 (line 22) | defaultFavicon32            = "/favicon-32x32.png"
  constant defaultCustomCSS (line 23) | defaultCustomCSS            = ""
  constant defaultSortBy (line 24) | defaultSortBy               = "name"
  constant defaultFilterBy (line 25) | defaultFilterBy             = "none"
  type Config (line 37) | type Config struct
    method IsDarkMode (line 57) | func (cfg *Config) IsDarkMode() bool {
    method ValidateAndSetDefaults (line 108) | func (cfg *Config) ValidateAndSetDefaults() error {
  type Button (line 65) | type Button struct
    method Validate (line 71) | func (btn *Button) Validate() error {
  type Favicon (line 78) | type Favicon struct
  function GetDefaultConfig (line 85) | func GetDefaultConfig() *Config {
  type ViewData (line 169) | type ViewData struct

FILE: config/ui/ui_test.go
  function TestConfig_ValidateAndSetDefaults (line 9) | func TestConfig_ValidateAndSetDefaults(t *testing.T) {
  function TestButton_Validate (line 125) | func TestButton_Validate(t *testing.T) {
  function TestGetDefaultConfig (line 164) | func TestGetDefaultConfig(t *testing.T) {
  function TestConfig_ValidateAndSetDefaults_DefaultSortBy (line 186) | func TestConfig_ValidateAndSetDefaults_DefaultSortBy(t *testing.T) {
  function TestConfig_ValidateAndSetDefaults_DefaultFilterBy (line 238) | func TestConfig_ValidateAndSetDefaults_DefaultFilterBy(t *testing.T) {

FILE: config/util.go
  function toPtr (line 4) | func toPtr[T any](value T) *T {

FILE: config/web/web.go
  constant DefaultAddress (line 12) | DefaultAddress = "0.0.0.0"
  constant DefaultPort (line 15) | DefaultPort = 8080
  constant DefaultReadBufferSize (line 18) | DefaultReadBufferSize = 8192
  constant MinimumReadBufferSize (line 22) | MinimumReadBufferSize = 4096
  type Config (line 26) | type Config struct
    method ValidateAndSetDefaults (line 63) | func (web *Config) ValidateAndSetDefaults() error {
    method HasTLS (line 89) | func (web *Config) HasTLS() bool {
    method SocketAddress (line 94) | func (web *Config) SocketAddress() string {
  type TLSConfig (line 45) | type TLSConfig struct
    method isValid (line 98) | func (t *TLSConfig) isValid() error {
  function GetDefaultConfig (line 54) | func GetDefaultConfig() *Config {

FILE: config/web/web_test.go
  function TestGetDefaultConfig (line 7) | func TestGetDefaultConfig(t *testing.T) {
  function TestConfig_ValidateAndSetDefaults (line 23) | func TestConfig_ValidateAndSetDefaults(t *testing.T) {
  function TestConfig_SocketAddress (line 116) | func TestConfig_SocketAddress(t *testing.T) {
  function TestConfig_isValid (line 126) | func TestConfig_isValid(t *testing.T) {

FILE: controller/controller.go
  function Handle (line 18) | func Handle(cfg *config.Config) {
  function Shutdown (line 44) | func Shutdown() {

FILE: controller/controller_test.go
  function TestHandle (line 16) | func TestHandle(t *testing.T) {
  function TestHandleTLS (line 51) | func TestHandleTLS(t *testing.T) {
  function TestShutdown (line 95) | func TestShutdown(t *testing.T) {

FILE: jsonpath/jsonpath.go
  function Eval (line 11) | func Eval(path string, b []byte) (string, int, error) {
  function walk (line 24) | func walk(path string, object interface{}) (string, int, error) {
  function extractValue (line 69) | func extractValue(currentKey string, value interface{}) interface{} {

FILE: jsonpath/jsonpath_bench_test.go
  function BenchmarkEval (line 5) | func BenchmarkEval(b *testing.B) {

FILE: jsonpath/jsonpath_test.go
  function TestEval (line 7) | func TestEval(t *testing.T) {

FILE: main.go
  constant GatusConfigPathEnvVar (line 19) | GatusConfigPathEnvVar = "GATUS_CONFIG_PATH"
  constant GatusConfigFileEnvVar (line 20) | GatusConfigFileEnvVar = "GATUS_CONFIG_FILE"
  constant GatusLogLevelEnvVar (line 21) | GatusLogLevelEnvVar   = "GATUS_LOG_LEVEL"
  function main (line 24) | func main() {
  function start (line 51) | func start(cfg *config.Config) {
  function stop (line 58) | func stop(cfg *config.Config) {
  function save (line 65) | func save() {
  function configureLogging (line 71) | func configureLogging() {
  function loadConfiguration (line 86) | func loadConfiguration() (*config.Config, error) {
  function initializeStorage (line 102) | func initializeStorage(cfg *config.Config) {
  function closeTunnels (line 218) | func closeTunnels(cfg *config.Config) {
  function listenToConfigurationFileChanges (line 226) | func listenToConfigurationFileChanges(cfg *config.Config) {

FILE: metrics/metrics.go
  constant namespace (line 12) | namespace = "gatus"
  function UnregisterPrometheusMetrics (line 34) | func UnregisterPrometheusMetrics() {
  function InitializePrometheusMetrics (line 77) | func InitializePrometheusMetrics(cfg *config.Config, reg prometheus.Regi...
  function PublishMetricsForEndpoint (line 168) | func PublishMetricsForEndpoint(ep *endpoint.Endpoint, result *endpoint.R...
  function PublishMetricsForSuite (line 204) | func PublishMetricsForSuite(s *suite.Suite, result *suite.Result, extraL...

FILE: metrics/metrics_test.go
  function TestInitializePrometheusMetrics (line 19) | func TestInitializePrometheusMetrics(t *testing.T) {
  function TestPublishMetricsForEndpoint_withExtraLabels (line 67) | func TestPublishMetricsForEndpoint_withExtraLabels(t *testing.T) {
  function TestPublishMetricsForEndpoint (line 114) | func TestPublishMetricsForEndpoint(t *testing.T) {
  function TestPublishMetricsForSuite (line 244) | func TestPublishMetricsForSuite(t *testing.T) {
  function TestPublishMetricsForSuite_NoGroup (line 302) | func TestPublishMetricsForSuite_NoGroup(t *testing.T) {

FILE: pattern/pattern.go
  function Match (line 9) | func Match(pattern, s string) bool {

FILE: pattern/pattern_bench_test.go
  function BenchmarkMatch (line 5) | func BenchmarkMatch(b *testing.B) {
  function BenchmarkMatchWithBackslash (line 14) | func BenchmarkMatchWithBackslash(b *testing.B) {

FILE: pattern/pattern_test.go
  function TestMatch (line 8) | func TestMatch(t *testing.T) {
  function testMatch (line 30) | func testMatch(t *testing.T, pattern, key string, expectedToMatch bool) {

FILE: security/basic.go
  type BasicConfig (line 4) | type BasicConfig struct
    method isValid (line 14) | func (c *BasicConfig) isValid() bool {

FILE: security/basic_test.go
  function TestBasicConfig_IsValidUsingBcrypt (line 5) | func TestBasicConfig_IsValidUsingBcrypt(t *testing.T) {
  function TestBasicConfig_IsValidWhenPasswordIsInvalidUsingBcrypt (line 15) | func TestBasicConfig_IsValidWhenPasswordIsInvalidUsingBcrypt(t *testing....

FILE: security/config.go
  constant cookieNameState (line 16) | cookieNameState   = "gatus_state"
  constant cookieNameNonce (line 17) | cookieNameNonce   = "gatus_nonce"
  constant cookieNameSession (line 18) | cookieNameSession = "gatus_session"
  type Config (line 22) | type Config struct
    method ValidateAndSetDefaults (line 30) | func (c *Config) ValidateAndSetDefaults() bool {
    method RegisterHandlers (line 35) | func (c *Config) RegisterHandlers(router fiber.Router) error {
    method ApplySecurityMiddleware (line 48) | func (c *Config) ApplySecurityMiddleware(router fiber.Router) error {
    method IsAuthenticated (line 97) | func (c *Config) IsAuthenticated(ctx *fiber.Ctx) bool {

FILE: security/config_test.go
  function TestConfig_ValidateAndSetDefaults (line 12) | func TestConfig_ValidateAndSetDefaults(t *testing.T) {
  function TestConfig_ApplySecurityMiddleware (line 22) | func TestConfig_ApplySecurityMiddleware(t *testing.T) {
  function TestConfig_RegisterHandlers (line 101) | func TestConfig_RegisterHandlers(t *testing.T) {

FILE: security/oidc.go
  constant DefaultOIDCSessionTTL (line 17) | DefaultOIDCSessionTTL = 8 * time.Hour
  type OIDCConfig (line 21) | type OIDCConfig struct
    method ValidateAndSetDefaults (line 35) | func (c *OIDCConfig) ValidateAndSetDefaults() bool {
    method initialize (line 42) | func (c *OIDCConfig) initialize() error {
    method loginHandler (line 59) | func (c *OIDCConfig) loginHandler(ctx *fiber.Ctx) error {
    method callbackHandler (line 80) | func (c *OIDCConfig) callbackHandler(w http.ResponseWriter, r *http.Re...
    method setSessionCookie (line 139) | func (c *OIDCConfig) setSessionCookie(w http.ResponseWriter, idToken *...

FILE: security/oidc_test.go
  function TestOIDCConfig_ValidateAndSetDefaults (line 12) | func TestOIDCConfig_ValidateAndSetDefaults(t *testing.T) {
  function TestOIDCConfig_callbackHandler (line 30) | func TestOIDCConfig_callbackHandler(t *testing.T) {
  function TestOIDCConfig_setSessionCookie (line 68) | func TestOIDCConfig_setSessionCookie(t *testing.T) {
  function TestOIDCConfig_setSessionCookieWithCustomTTL (line 77) | func TestOIDCConfig_setSessionCookieWithCustomTTL(t *testing.T) {

FILE: storage/config.go
  constant DefaultMaximumNumberOfResults (line 8) | DefaultMaximumNumberOfResults = 100
  constant DefaultMaximumNumberOfEvents (line 9) | DefaultMaximumNumberOfEvents  = 50
  type Config (line 18) | type Config struct
    method ValidateAndSetDefaults (line 42) | func (c *Config) ValidateAndSetDefaults() error {

FILE: storage/store/common/paging/endpoint_status_params.go
  type EndpointStatusParams (line 4) | type EndpointStatusParams struct
    method WithEvents (line 17) | func (params *EndpointStatusParams) WithEvents(page, pageSize int) *En...
    method WithResults (line 24) | func (params *EndpointStatusParams) WithResults(page, pageSize int) *E...
  function NewEndpointStatusParams (line 12) | func NewEndpointStatusParams() *EndpointStatusParams {

FILE: storage/store/common/paging/endpoint_status_params_test.go
  function TestNewEndpointStatusParams (line 5) | func TestNewEndpointStatusParams(t *testing.T) {

FILE: storage/store/common/paging/suite_status_params.go
  type SuiteStatusParams (line 4) | type SuiteStatusParams struct
    method WithPagination (line 18) | func (params *SuiteStatusParams) WithPagination(page, pageSize int) *S...
  function NewSuiteStatusParams (line 10) | func NewSuiteStatusParams() *SuiteStatusParams {

FILE: storage/store/common/paging/suite_status_params_test.go
  function TestNewSuiteStatusParams (line 7) | func TestNewSuiteStatusParams(t *testing.T) {
  function TestSuiteStatusParams_WithPagination (line 20) | func TestSuiteStatusParams_WithPagination(t *testing.T) {
  function TestSuiteStatusParams_ChainedMethods (line 85) | func TestSuiteStatusParams_ChainedMethods(t *testing.T) {
  function TestSuiteStatusParams_OverwritePagination (line 97) | func TestSuiteStatusParams_OverwritePagination(t *testing.T) {
  function TestSuiteStatusParams_ReturnsSelf (line 116) | func TestSuiteStatusParams_ReturnsSelf(t *testing.T) {

FILE: storage/store/memory/memory.go
  type Store (line 20) | type Store struct
    method GetAllEndpointStatuses (line 46) | func (s *Store) GetAllEndpointStatuses(params *paging.EndpointStatusPa...
    method GetAllSuiteStatuses (line 63) | func (s *Store) GetAllSuiteStatuses(params *paging.SuiteStatusParams) ...
    method GetEndpointStatus (line 79) | func (s *Store) GetEndpointStatus(groupName, endpointName string, para...
    method GetEndpointStatusByKey (line 84) | func (s *Store) GetEndpointStatusByKey(key string, params *paging.Endp...
    method GetSuiteStatusByKey (line 95) | func (s *Store) GetSuiteStatusByKey(key string, params *paging.SuiteSt...
    method GetUptimeByKey (line 106) | func (s *Store) GetUptimeByKey(key string, from, to time.Time) (float6...
    method GetAverageResponseTimeByKey (line 137) | func (s *Store) GetAverageResponseTimeByKey(key string, from, to time....
    method GetHourlyAverageResponseTimeByKey (line 167) | func (s *Store) GetHourlyAverageResponseTimeByKey(key string, from, to...
    method InsertEndpointResult (line 193) | func (s *Store) InsertEndpointResult(ep *endpoint.Endpoint, result *en...
    method InsertSuiteResult (line 211) | func (s *Store) InsertSuiteResult(su *suite.Suite, result *suite.Resul...
    method DeleteAllEndpointStatusesNotInKeys (line 238) | func (s *Store) DeleteAllEndpointStatusesNotInKeys(keys []string) int {
    method DeleteAllSuiteStatusesNotInKeys (line 250) | func (s *Store) DeleteAllSuiteStatusesNotInKeys(keys []string) int {
    method GetTriggeredEndpointAlert (line 269) | func (s *Store) GetTriggeredEndpointAlert(ep *endpoint.Endpoint, alert...
    method UpsertTriggeredEndpointAlert (line 277) | func (s *Store) UpsertTriggeredEndpointAlert(ep *endpoint.Endpoint, tr...
    method DeleteTriggeredEndpointAlert (line 284) | func (s *Store) DeleteTriggeredEndpointAlert(ep *endpoint.Endpoint, tr...
    method DeleteAllTriggeredAlertsNotInChecksumsByEndpoint (line 293) | func (s *Store) DeleteAllTriggeredAlertsNotInChecksumsByEndpoint(ep *e...
    method HasEndpointStatusNewerThan (line 298) | func (s *Store) HasEndpointStatusNewerThan(key string, timestamp time....
    method Clear (line 319) | func (s *Store) Clear() {
    method Save (line 325) | func (s *Store) Save() error {
    method Close (line 330) | func (s *Store) Close() {
  function NewStore (line 34) | func NewStore(maximumNumberOfResults, maximumNumberOfEvents int) (*Store...

FILE: storage/store/memory/memory_test.go
  function TestStore_SanityCheck (line 87) | func TestStore_SanityCheck(t *testing.T) {
  function TestStore_Save (line 128) | func TestStore_Save(t *testing.T) {
  function TestStore_HasEndpointStatusNewerThan (line 141) | func TestStore_HasEndpointStatusNewerThan(t *testing.T) {
  function TestStore_MixedEndpointsAndSuites (line 170) | func TestStore_MixedEndpointsAndSuites(t *testing.T) {
  function TestStore_EndpointStatusCastingSafety (line 642) | func TestStore_EndpointStatusCastingSafety(t *testing.T) {
  function TestStore_MaximumLimits (line 690) | func TestStore_MaximumLimits(t *testing.T) {
  function TestSuiteResultOrdering (line 787) | func TestSuiteResultOrdering(t *testing.T) {
  function TestStore_ConcurrentAccess (line 914) | func TestStore_ConcurrentAccess(t *testing.T) {

FILE: storage/store/memory/uptime.go
  constant uptimeCleanUpThreshold (line 10) | uptimeCleanUpThreshold = 32 * 24
  constant uptimeRetention (line 11) | uptimeRetention        = 30 * 24 * time.Hour
  function processUptimeAfterResult (line 16) | func processUptimeAfterResult(uptime *endpoint.Uptime, result *endpoint....

FILE: storage/store/memory/uptime_bench_test.go
  function BenchmarkProcessUptimeAfterResult (line 10) | func BenchmarkProcessUptimeAfterResult(b *testing.B) {

FILE: storage/store/memory/uptime_test.go
  function TestProcessUptimeAfterResult (line 11) | func TestProcessUptimeAfterResult(t *testing.T) {
  function TestAddResultUptimeIsCleaningUpAfterItself (line 46) | func TestAddResultUptimeIsCleaningUpAfterItself(t *testing.T) {
  function checkHourlyStatistics (line 63) | func checkHourlyStatistics(t *testing.T, hourlyUptimeStatistics *endpoin...

FILE: storage/store/memory/util.go
  function ShallowCopyEndpointStatus (line 11) | func ShallowCopyEndpointStatus(ss *endpoint.Status, params *paging.Endpo...
  function ShallowCopySuiteStatus (line 42) | func ShallowCopySuiteStatus(ss *suite.Status, params *paging.SuiteStatus...
  function getStartAndEndIndex (line 62) | func getStartAndEndIndex(numberOfResults int, page, pageSize int) (int, ...
  function AddResult (line 81) | func AddResult(ss *endpoint.Status, result *endpoint.Result, maximumNumb...

FILE: storage/store/memory/util_bench_test.go
  function BenchmarkShallowCopyEndpointStatus (line 11) | func BenchmarkShallowCopyEndpointStatus(b *testing.B) {

FILE: storage/store/memory/util_test.go
  function TestAddResult (line 13) | func TestAddResult(t *testing.T) {
  function TestShallowCopyEndpointStatus (line 29) | func TestShallowCopyEndpointStatus(t *testing.T) {
  function TestShallowCopySuiteStatus (line 69) | func TestShallowCopySuiteStatus(t *testing.T) {

FILE: storage/store/sql/specific_postgres.go
  method createPostgresSchema (line 3) | func (s *Store) createPostgresSchema() error {

FILE: storage/store/sql/specific_sqlite.go
  method createSQLiteSchema (line 3) | func (s *Store) createSQLiteSchema() error {

FILE: storage/store/sql/sql.go
  constant arraySeparator (line 31) | arraySeparator = "|~|"
  constant eventsAboveMaximumCleanUpThreshold (line 33) | eventsAboveMaximumCleanUpThreshold  = 10
  constant resultsAboveMaximumCleanUpThreshold (line 34) | resultsAboveMaximumCleanUpThreshold = 10
  constant uptimeTotalEntriesMergeThreshold (line 36) | uptimeTotalEntriesMergeThreshold = 100
  constant uptimeAgeCleanUpThreshold (line 37) | uptimeAgeCleanUpThreshold        = 32 * 24 * time.Hour
  constant uptimeRetention (line 38) | uptimeRetention                  = 30 * 24 * time.Hour
  constant uptimeHourlyBuffer (line 39) | uptimeHourlyBuffer               = 48 * time.Hour
  constant cacheTTL (line 41) | cacheTTL = 10 * time.Minute
  type Store (line 55) | type Store struct
    method createSchema (line 108) | func (s *Store) createSchema() error {
    method GetAllEndpointStatuses (line 117) | func (s *Store) GetAllEndpointStatuses(params *paging.EndpointStatusPa...
    method GetEndpointStatus (line 142) | func (s *Store) GetEndpointStatus(groupName, endpointName string, para...
    method GetEndpointStatusByKey (line 147) | func (s *Store) GetEndpointStatusByKey(key string, params *paging.Endp...
    method GetUptimeByKey (line 164) | func (s *Store) GetUptimeByKey(key string, from, to time.Time) (float6...
    method GetAverageResponseTimeByKey (line 189) | func (s *Store) GetAverageResponseTimeByKey(key string, from, to time....
    method GetHourlyAverageResponseTimeByKey (line 214) | func (s *Store) GetHourlyAverageResponseTimeByKey(key string, from, to...
    method InsertEndpointResult (line 239) | func (s *Store) InsertEndpointResult(ep *endpoint.Endpoint, result *en...
    method DeleteAllEndpointStatusesNotInKeys (line 384) | func (s *Store) DeleteAllEndpointStatusesNotInKeys(keys []string) int {
    method GetTriggeredEndpointAlert (line 442) | func (s *Store) GetTriggeredEndpointAlert(ep *endpoint.Endpoint, alert...
    method UpsertTriggeredEndpointAlert (line 460) | func (s *Store) UpsertTriggeredEndpointAlert(ep *endpoint.Endpoint, tr...
    method DeleteTriggeredEndpointAlert (line 507) | func (s *Store) DeleteTriggeredEndpointAlert(ep *endpoint.Endpoint, tr...
    method DeleteAllTriggeredAlertsNotInChecksumsByEndpoint (line 516) | func (s *Store) DeleteAllTriggeredAlertsNotInChecksumsByEndpoint(ep *e...
    method HasEndpointStatusNewerThan (line 547) | func (s *Store) HasEndpointStatusNewerThan(key string, timestamp time....
    method Clear (line 565) | func (s *Store) Clear() {
    method Save (line 573) | func (s *Store) Save() error {
    method Close (line 578) | func (s *Store) Close() {
    method insertEndpoint (line 587) | func (s *Store) insertEndpoint(tx *sql.Tx, ep *endpoint.Endpoint) (int...
    method insertEndpointEvent (line 603) | func (s *Store) insertEndpointEvent(tx *sql.Tx, endpointID int64, even...
    method insertEndpointResult (line 617) | func (s *Store) insertEndpointResult(tx *sql.Tx, endpointID int64, res...
    method insertEndpointResultWithSuiteID (line 622) | func (s *Store) insertEndpointResultWithSuiteID(tx *sql.Tx, endpointID...
    method insertConditionResults (line 650) | func (s *Store) insertConditionResults(tx *sql.Tx, endpointResultID in...
    method updateEndpointUptime (line 665) | func (s *Store) updateEndpointUptime(tx *sql.Tx, endpointID int64, res...
    method getAllEndpointKeys (line 689) | func (s *Store) getAllEndpointKeys(tx *sql.Tx) (keys []string, err err...
    method getEndpointStatusByKey (line 711) | func (s *Store) getEndpointStatusByKey(tx *sql.Tx, key string, paramet...
    method getEndpointIDGroupAndNameByKey (line 742) | func (s *Store) getEndpointIDGroupAndNameByKey(tx *sql.Tx, key string)...
    method getEndpointEventsByEndpointID (line 761) | func (s *Store) getEndpointEventsByEndpointID(tx *sql.Tx, endpointID i...
    method getEndpointResultsByEndpointID (line 791) | func (s *Store) getEndpointResultsByEndpointID(tx *sql.Tx, endpointID ...
    method getEndpointUptime (line 856) | func (s *Store) getEndpointUptime(tx *sql.Tx, endpointID int64, from, ...
    method getEndpointAverageResponseTime (line 883) | func (s *Store) getEndpointAverageResponseTime(tx *sql.Tx, endpointID ...
    method getEndpointHourlyAverageResponseTimes (line 910) | func (s *Store) getEndpointHourlyAverageResponseTimes(tx *sql.Tx, endp...
    method getEndpointID (line 937) | func (s *Store) getEndpointID(tx *sql.Tx, ep *endpoint.Endpoint) (int6...
    method getNumberOfEventsByEndpointID (line 949) | func (s *Store) getNumberOfEventsByEndpointID(tx *sql.Tx, endpointID i...
    method getNumberOfResultsByEndpointID (line 955) | func (s *Store) getNumberOfResultsByEndpointID(tx *sql.Tx, endpointID ...
    method getNumberOfUptimeEntriesByEndpointID (line 961) | func (s *Store) getNumberOfUptimeEntriesByEndpointID(tx *sql.Tx, endpo...
    method getAgeOfOldestEndpointUptimeEntry (line 967) | func (s *Store) getAgeOfOldestEndpointUptimeEntry(tx *sql.Tx, endpoint...
    method getLastEndpointResultSuccessValue (line 993) | func (s *Store) getLastEndpointResultSuccessValue(tx *sql.Tx, endpoint...
    method deleteOldEndpointEvents (line 1006) | func (s *Store) deleteOldEndpointEvents(tx *sql.Tx, endpointID int64) ...
    method deleteOldEndpointResults (line 1026) | func (s *Store) deleteOldEndpointResults(tx *sql.Tx, endpointID int64)...
    method deleteOldUptimeEntries (line 1045) | func (s *Store) deleteOldUptimeEntries(tx *sql.Tx, endpointID int64, m...
    method mergeHourlyUptimeEntriesOlderThanMergeThresholdIntoDailyUptimeEntries (line 1057) | func (s *Store) mergeHourlyUptimeEntriesOlderThanMergeThresholdIntoDai...
    method GetAllSuiteStatuses (line 1163) | func (s *Store) GetAllSuiteStatuses(params *paging.SuiteStatusParams) ...
    method GetSuiteStatusByKey (line 1228) | func (s *Store) GetSuiteStatusByKey(key string, params *paging.SuiteSt...
    method InsertSuiteResult (line 1285) | func (s *Store) InsertSuiteResult(su *suite.Suite, result *suite.Resul...
    method DeleteAllSuiteStatusesNotInKeys (line 1367) | func (s *Store) DeleteAllSuiteStatusesNotInKeys(keys []string) int {
    method getSuiteID (line 1425) | func (s *Store) getSuiteID(tx *sql.Tx, su *suite.Suite) (int64, error) {
    method insertSuite (line 1438) | func (s *Store) insertSuite(tx *sql.Tx, su *suite.Suite) (int64, error) {
    method getSuiteResults (line 1453) | func (s *Store) getSuiteResults(tx *sql.Tx, suiteID int64, page, pageS...
    method getNumberOfSuiteResultsByID (line 1605) | func (s *Store) getNumberOfSuiteResultsByID(tx *sql.Tx, suiteID int64)...
    method deleteOldSuiteResults (line 1612) | func (s *Store) deleteOldSuiteResults(tx *sql.Tx, suiteID int64) error {
  function NewStore (line 69) | func NewStore(driver, path string, caching bool, maximumNumberOfResults,...
  function generateCacheKey (line 1136) | func generateCacheKey(endpointKey string, p *paging.EndpointStatusParams...
  function extractKeyAndParamsFromCacheKey (line 1140) | func extractKeyAndParamsFromCacheKey(cacheKey string) (string, *paging.E...

FILE: storage/store/sql/sql_test.go
  function TestNewStore (line 86) | func TestNewStore(t *testing.T) {
  function TestStore_InsertCleansUpOldUptimeEntriesProperly (line 100) | func TestStore_InsertCleansUpOldUptimeEntriesProperly(t *testing.T) {
  function TestStore_HourlyUptimeEntriesAreMergedIntoDailyUptimeEntriesProperly (line 157) | func TestStore_HourlyUptimeEntriesAreMergedIntoDailyUptimeEntriesProperl...
  function TestStore_getEndpointUptime (line 214) | func TestStore_getEndpointUptime(t *testing.T) {
  function TestStore_InsertCleansUpEventsAndResultsProperly (line 276) | func TestStore_InsertCleansUpEventsAndResultsProperly(t *testing.T) {
  function TestStore_InsertWithCaching (line 295) | func TestStore_InsertWithCaching(t *testing.T) {
  function TestStore_Persistence (line 329) | func TestStore_Persistence(t *testing.T) {
  function TestStore_Save (line 415) | func TestStore_Save(t *testing.T) {
  function TestStore_SanityCheck (line 425) | func TestStore_SanityCheck(t *testing.T) {
  function TestStore_InvalidTransaction (line 469) | func TestStore_InvalidTransaction(t *testing.T) {
  function TestStore_NoRows (line 527) | func TestStore_NoRows(t *testing.T) {
  function TestStore_BrokenSchema (line 541) | func TestStore_BrokenSchema(t *testing.T) {
  function TestCacheKey (line 643) | func TestCacheKey(t *testing.T) {
  function TestTriggeredEndpointAlertsPersistence (line 729) | func TestTriggeredEndpointAlertsPersistence(t *testing.T) {
  function TestStore_DeleteAllTriggeredAlertsNotInChecksumsByEndpoint (line 795) | func TestStore_DeleteAllTriggeredAlertsNotInChecksumsByEndpoint(t *testi...
  function TestStore_HasEndpointStatusNewerThan (line 857) | func TestStore_HasEndpointStatusNewerThan(t *testing.T) {
  function TestEventOrderingFix (line 893) | func TestEventOrderingFix(t *testing.T) {

FILE: storage/store/store.go
  type Store (line 18) | type Store interface
  function Get (line 107) | func Get() Store {
  function Initialize (line 120) | func Initialize(cfg *storage.Config) error {
  function autoSave (line 154) | func autoSave(ctx context.Context, store Store, interval time.Duration) {

FILE: storage/store/store_bench_test.go
  function BenchmarkStore_GetAllEndpointStatuses (line 15) | func BenchmarkStore_GetAllEndpointStatuses(b *testing.B) {
  function BenchmarkStore_Insert (line 84) | func BenchmarkStore_Insert(b *testing.B) {
  function BenchmarkStore_GetEndpointStatusByKey (line 156) | func BenchmarkStore_GetEndpointStatusByKey(b *testing.B) {

FILE: storage/store/store_test.go
  type Scenario (line 88) | type Scenario struct
  function initStoresAndBaseScenarios (line 93) | func initStoresAndBaseScenarios(t *testing.T, testName string) []*Scenar...
  function cleanUp (line 122) | func cleanUp(scenarios []*Scenario) {
  function TestStore_GetEndpointStatusByKey (line 128) | func TestStore_GetEndpointStatusByKey(t *testing.T) {
  function TestStore_GetEndpointStatusForMissingStatusReturnsNil (line 173) | func TestStore_GetEndpointStatusForMissingStatusReturnsNil(t *testing.T) {
  function TestStore_GetAllEndpointStatuses (line 204) | func TestStore_GetAllEndpointStatuses(t *testing.T) {
  function TestStore_GetAllEndpointStatusesWithResultsAndEvents (line 264) | func TestStore_GetAllEndpointStatusesWithResultsAndEvents(t *testing.T) {
  function TestStore_GetEndpointStatusPage1IsHasMoreRecentResultsThanPage2 (line 296) | func TestStore_GetEndpointStatusPage1IsHasMoreRecentResultsThanPage2(t *...
  function TestStore_GetUptimeByKey (line 336) | func TestStore_GetUptimeByKey(t *testing.T) {
  function TestStore_GetAverageResponseTimeByKey (line 366) | func TestStore_GetAverageResponseTimeByKey(t *testing.T) {
  function TestStore_GetHourlyAverageResponseTimeByKey (line 423) | func TestStore_GetHourlyAverageResponseTimeByKey(t *testing.T) {
  function TestStore_Insert (line 462) | func TestStore_Insert(t *testing.T) {
  function TestStore_DeleteAllEndpointStatusesNotInKeys (line 540) | func TestStore_DeleteAllEndpointStatusesNotInKeys(t *testing.T) {
  function TestGet (line 573) | func TestGet(t *testing.T) {
  function TestInitialize (line 580) | func TestInitialize(t *testing.T) {
  function TestAutoSave (line 644) | func TestAutoSave(t *testing.T) {

FILE: storage/type.go
  type Type (line 4) | type Type
  constant TypeMemory (line 7) | TypeMemory   Type = "memory"
  constant TypeSQLite (line 8) | TypeSQLite   Type = "sqlite"
  constant TypePostgres (line 9) | TypePostgres Type = "postgres"

FILE: test/mock.go
  type MockRoundTripper (line 5) | type MockRoundTripper
    method RoundTrip (line 7) | func (f MockRoundTripper) RoundTrip(r *http.Request) (*http.Response, ...

FILE: watchdog/alerting.go
  function HandleAlerting (line 16) | func HandleAlerting(ep *endpoint.Endpoint, result *endpoint.Result, aler...
  function handleAlertsToTrigger (line 27) | func handleAlertsToTrigger(ep *endpoint.Endpoint, result *endpoint.Resul...
  function handleAlertsToResolve (line 83) | func handleAlertsToResolve(ep *endpoint.Endpoint, result *endpoint.Resul...

FILE: watchdog/alerting_test.go
  function TestHandleAlerting (line 35) | func TestHandleAlerting(t *testing.T) {
  function TestHandleAlertingWhenAlertingConfigIsNil (line 83) | func TestHandleAlertingWhenAlertingConfigIsNil(t *testing.T) {
  function TestHandleAlertingWithBadAlertProvider (line 89) | func TestHandleAlertingWithBadAlertProvider(t *testing.T) {
  function TestHandleAlertingWhenTriggeredAlertIsAlmostResolvedButendpointStartFailingAgain (line 115) | func TestHandleAlertingWhenTriggeredAlertIsAlmostResolvedButendpointStar...
  function TestHandleAlertingWhenTriggeredAlertIsResolvedButSendOnResolvedIsFalse (line 150) | func TestHandleAlertingWhenTriggeredAlertIsResolvedButSendOnResolvedIsFa...
  function TestHandleAlertingWhenTriggeredAlertIsResolvedPagerDuty (line 185) | func TestHandleAlertingWhenTriggeredAlertIsResolvedPagerDuty(t *testing....
  function TestHandleAlertingWhenTriggeredAlertIsResolvedPushover (line 221) | func TestHandleAlertingWhenTriggeredAlertIsResolvedPushover(t *testing.T) {
  function TestHandleAlertingWithProviderThatReturnsAnError (line 258) | func TestHandleAlertingWithProviderThatReturnsAnError(t *testing.T) {
  function TestHandleAlertingWithProviderThatOnlyReturnsErrorOnResolve (line 572) | func TestHandleAlertingWithProviderThatOnlyReturnsErrorOnResolve(t *test...
  function TestHandleAlertingWithMinimumReminderInterval (line 625) | func TestHandleAlertingWithMinimumReminderInterval(t *testing.T) {
  function verify (line 667) | func verify(t *testing.T, ep *endpoint.Endpoint, expectedNumberOfFailure...

FILE: watchdog/endpoint.go
  function monitorEndpoint (line 15) | func monitorEndpoint(ep *endpoint.Endpoint, cfg *config.Config, extraLab...
  function executeEndpoint (line 35) | func executeEndpoint(ep *endpoint.Endpoint, cfg *config.Config, extraLab...
  function UpdateEndpointStatus (line 75) | func UpdateEndpointStatus(ep *endpoint.Endpoint, result *endpoint.Result) {

FILE: watchdog/external_endpoint.go
  function monitorExternalEndpointHeartbeat (line 14) | func monitorExternalEndpointHeartbeat(ee *endpoint.ExternalEndpoint, cfg...
  function executeExternalEndpointHeartbeat (line 28) | func executeExternalEndpointHeartbeat(ee *endpoint.ExternalEndpoint, cfg...

FILE: watchdog/suite.go
  function monitorSuite (line 15) | func monitorSuite(s *suite.Suite, cfg *config.Config, extraLabels []stri...
  function executeSuite (line 33) | func executeSuite(s *suite.Suite, cfg *config.Config, extraLabels []stri...
  function UpdateSuiteStatus (line 80) | func UpdateSuiteStatus(s *suite.Suite, result *suite.Result) {

FILE: watchdog/watchdog.go
  constant UnlimitedConcurrencyWeight (line 14) | UnlimitedConcurrencyWeight = 10000
  function Monitor (line 27) | func Monitor(cfg *config.Config) {
  function Shutdown (line 62) | func Shutdown(cfg *config.Config) {

FILE: web/app/src/utils/misc.js
  function combineClasses (line 4) | function combineClasses(...inputs) {

FILE: web/static.go
  constant RootPath (line 11) | RootPath  = "static"
  constant IndexPath (line 12) | IndexPath = RootPath + "/index.html"

FILE: web/static/js/app.js
  function v (line 1) | function v(...e){return(0,p.m6)((0,m.W)(e))}
  method setup (line 1) | setup(e){const t=(0,g.j)("inline-flex items-center justify-center whites...
  method setup (line 1) | setup(e){return(e,t)=>((0,l.wg)(),(0,l.iD)("div",{class:(0,n.C_)((0,r.SU...
  method setup (line 1) | setup(e){return(e,t)=>((0,l.wg)(),(0,l.iD)("div",{class:(0,n.C_)((0,r.SU...
  method setup (line 1) | setup(e){return(e,t)=>((0,l.wg)(),(0,l.iD)("h3",{class:(0,n.C_)((0,r.SU)...
  method setup (line 1) | setup(e){return(e,t)=>((0,l.wg)(),(0,l.iD)("div",{class:(0,n.C_)((0,r.SU...
  function F (line 1) | function F(e,t){return(0,l.wg)(),(0,l.iD)("div",R,t[0]||(t[0]=[(0,l._)("...
  method setup (line 1) | setup(e){const t=(0,i.yj)(),s=e,a=(0,r.iH)(!0),o=(0,r.iH)(0),u=(0,r.iH)(...
  method setup (line 1) | setup(e){const t=e,s=(0,l.Fl)((()=>{const e={xs:"w-4 h-4",sm:"w-6 h-6",m...
  method setup (line 1) | setup(e){const t=(0,i.yj)(),s=(0,r.iH)(!1),a=(0,r.iH)({oidc:!1,authentic...
  method setup (line 1) | setup(e){const t=(0,g.j)("inline-flex items-center rounded-full border p...
  method setup (line 1) | setup(e){const t=e,s=(0,l.Fl)((()=>{switch(t.status){case"healthy":retur...
  method setup (line 1) | setup(e,{emit:t}){const s=(0,i.tv)(),o=e,u=t,d=(0,r.iH)(null),c=(0,l.Fl)...
  method setup (line 1) | setup(e,{emit:t}){const s=(0,i.tv)(),o=e,u=t,d=(0,r.iH)(null),c=(0,l.Fl)...
  method setup (line 1) | setup(e){return(t,s)=>((0,l.wg)(),(0,l.iD)("input",{class:(0,n.C_)((0,r....
  method setup (line 1) | setup(e,{emit:t}){const s=e,a=t,o=(0,r.iH)(!1),i=(0,r.iH)(null),u=(0,r.i...
  method setup (line 1) | setup(e,{emit:t}){const s=(0,r.iH)(""),a=(0,r.iH)(localStorage.getItem("...
  method setup (line 1) | setup(e,{emit:t}){const s=t,o=[{value:"10",label:"10s"},{value:"30",labe...
  method setup (line 1) | setup(e){const t=e,s=(0,r.iH)(!1),a=()=>{s.value=!s.value},o={outage:{ic...
  method setup (line 1) | setup(e){const t=e,s=(0,r.iH)(!1),a={outage:{icon:ks.Z,background:"bg-re...
  method setup (line 1) | setup(e,{emit:t}){const s=e,a=(0,l.Fl)((()=>s.announcements?s.announceme...
  method setup (line 1) | setup(e,{emit:t}){const s=e,a=t,o=(0,r.iH)(s.currentPageProp),i=(0,l.Fl)...
  method setup (line 1) | setup(e){al.kL.register(al.uw,al.f$,al.od,al.jn,al.Dx,al.u,al.De,al.Gu,a...
  method setup (line 1) | setup(e,{emit:t}){const s=(0,i.tv)(),o=(0,i.yj)(),u=t,d=(0,r.iH)(null),c...
  method setup (line 1) | setup(e){const t=e,s=(0,l.Fl)((()=>{switch(t.step.status){case"success":...
  method setup (line 1) | setup(e){const t=e,s=(0,l.Fl)((()=>t.completedSteps)),a=(0,l.Fl)((()=>t....
  method setup (line 1) | setup(e){const t=e,s=(0,l.Fl)((()=>{switch(t.step.status){case"success":...
  method setup (line 1) | setup(e){const t=(0,i.tv)(),s=(0,i.yj)(),a=(0,r.iH)(!1),o=(0,r.iH)(null)...
  function s (line 1) | function s(a){var l=t[a];if(void 0!==l)return l.exports;var n=t[a]={expo...

FILE: web/static/js/chunk-vendors.js
  class s (line 6) | class s{constructor(t=!1){this.detached=t,this._active=!0,this._on=0,thi...
    method constructor (line 6) | constructor(t=!1){this.detached=t,this._active=!0,this._on=0,this.effe...
    method active (line 6) | get active(){return this._active}
    method pause (line 6) | pause(){if(this._active){let t,e;if(this._isPaused=!0,this.scopes)for(...
    method resume (line 6) | resume(){if(this._active&&this._isPaused){let t,e;if(this._isPaused=!1...
    method run (line 6) | run(t){if(this._active){const e=r;try{return r=this,t()}finally{r=e}}e...
    method on (line 6) | on(){1===++this._on&&(this.prevScope=r,r=this)}
    method off (line 6) | off(){this._on>0&&0===--this._on&&(r=this.prevScope,this.prevScope=voi...
    method stop (line 6) | stop(t){if(this._active){let e,n;for(this._active=!1,e=0,n=this.effect...
  function a (line 6) | function a(){return r}
  class c (line 6) | class c{constructor(t){this.fn=t,this.deps=void 0,this.depsTail=void 0,t...
    method constructor (line 6) | constructor(t){this.fn=t,this.deps=void 0,this.depsTail=void 0,this.fl...
    method pause (line 6) | pause(){this.flags|=64}
    method resume (line 6) | resume(){64&this.flags&&(this.flags&=-65,l.has(this)&&(l.delete(this),...
    method notify (line 6) | notify(){2&this.flags&&!(32&this.flags)||8&this.flags||f(this)}
    method run (line 6) | run(){if(!(1&this.flags))return this.fn();this.flags|=2,T(this),m(this...
    method stop (line 6) | stop(){if(1&this.flags){for(let t=this.deps;t;t=t.nextDep)v(t);this.de...
    method trigger (line 6) | trigger(){64&this.flags?l.add(this):this.scheduler?this.scheduler():th...
    method runIfDirty (line 6) | runIfDirty(){x(this)&&this.run()}
    method dirty (line 6) | get dirty(){return x(this)}
    method constructor (line 230) | constructor(t,e){this._chart=t,this._properties=new Map,this.configure...
    method configure (line 230) | configure(t){if(!(0,i.i)(t))return;const e=Object.keys(i.d.animation),...
    method _animateOptions (line 230) | _animateOptions(t,e){const n=e.options,i=h(t,n);if(!i)return[];const r...
    method _createAnimations (line 230) | _createAnimations(t,e){const n=this._properties,i=[],r=t.$animations||...
    method update (line 230) | update(t,e){if(0===this._properties.size)return void Object.assign(t,e...
  function f (line 6) | function f(t,e=!1){if(t.flags|=8,e)return t.next=h,void(h=t);t.next=u,u=t}
  function p (line 6) | function p(){d++}
  function g (line 6) | function g(){if(--d>0)return;if(h){let t=h;h=void 0;while(t){const e=t.n...
  function m (line 6) | function m(t){for(let e=t.deps;e;e=e.nextDep)e.version=-1,e.prevActiveLi...
  function b (line 6) | function b(t){let e,n=t.depsTail,i=n;while(i){const t=i.prevDep;-1===i.v...
  function x (line 6) | function x(t){for(let e=t.deps;e;e=e.nextDep)if(e.dep.version!==e.versio...
  function y (line 6) | function y(t){if(4&t.flags&&!(16&t.flags))return;if(t.flags&=-17,t.globa...
  function v (line 6) | function v(t,e=!1){const{dep:n,prevSub:i,nextSub:r}=t;if(i&&(i.nextSub=r...
  function w (line 6) | function w(t){const{prevDep:e,nextDep:n}=t;e&&(e.nextDep=n,t.prevDep=voi...
  function M (line 6) | function M(){_.push(k),k=!1}
  function S (line 6) | function S(){const t=_.pop();k=void 0===t||t}
  function T (line 6) | function T(t){const{cleanup:e}=t;if(t.cleanup=void 0,e){const t=o;o=void...
  class C (line 6) | class C{constructor(t,e){this.sub=t,this.dep=e,this.version=e.version,th...
    method constructor (line 6) | constructor(t,e){this.sub=t,this.dep=e,this.version=e.version,this.nex...
  class A (line 6) | class A{constructor(t){this.computed=t,this.version=0,this.activeLink=vo...
    method constructor (line 6) | constructor(t){this.computed=t,this.version=0,this.activeLink=void 0,t...
    method track (line 6) | track(t){if(!o||!k||o===this.computed)return;let e=this.activeLink;if(...
    method trigger (line 6) | trigger(t){this.version++,D++,this.notify(t)}
    method notify (line 6) | notify(t){p();try{0;for(let t=this.subs;t;t=t.prevSub)t.sub.notify()&&...
  function O (line 6) | function O(t){if(t.dep.sc++,4&t.sub.flags){const e=t.dep.computed;if(e&&...
  function L (line 6) | function L(t,e,n){if(k&&o){let e=P.get(t);e||P.set(t,e=new Map);let i=e....
  function z (line 6) | function z(t,e,n,r,o,s){const a=P.get(t);if(!a)return void D++;const l=t...
    method override (line 230) | static override(t){Object.assign(z.prototype,t)}
    method constructor (line 230) | constructor(t){this.options=t||{}}
    method init (line 230) | init(){}
    method formats (line 230) | formats(){return L()}
    method parse (line 230) | parse(){return L()}
    method format (line 230) | format(){return L()}
    method add (line 230) | add(){return L()}
    method diff (line 230) | diff(){return L()}
    method startOf (line 230) | startOf(){return L()}
    method endOf (line 230) | endOf(){return L()}
  function N (line 6) | function N(t){const e=Mt(t);return e===t?e:(L(e,"iterate",I),kt(t)?e:e.m...
  function F (line 6) | function F(t){return L(t=Mt(t),"iterate",I),t}
  method [Symbol.iterator] (line 6) | [Symbol.iterator](){return H(this,Symbol.iterator,Tt)}
  method concat (line 6) | concat(...t){return N(this).concat(...t.map((t=>(0,i.kJ)(t)?N(t):t)))}
  method entries (line 6) | entries(){return H(this,"entries",(t=>(t[1]=Tt(t[1]),t)))}
  method every (line 6) | every(t,e){return $(this,"every",t,e,void 0,arguments)}
  method filter (line 6) | filter(t,e){return $(this,"filter",t,e,(t=>t.map(Tt)),arguments)}
  method find (line 6) | find(t,e){return $(this,"find",t,e,Tt,arguments)}
  method findIndex (line 6) | findIndex(t,e){return $(this,"findIndex",t,e,void 0,arguments)}
  method findLast (line 6) | findLast(t,e){return $(this,"findLast",t,e,Tt,arguments)}
  method findLastIndex (line 6) | findLastIndex(t,e){return $(this,"findLastIndex",t,e,void 0,arguments)}
  method forEach (line 6) | forEach(t,e){return $(this,"forEach",t,e,void 0,arguments)}
  method includes (line 6) | includes(...t){return Y(this,"includes",t)}
  method indexOf (line 6) | indexOf(...t){return Y(this,"indexOf",t)}
  method join (line 6) | join(t){return N(this).join(t)}
  method lastIndexOf (line 6) | lastIndexOf(...t){return Y(this,"lastIndexOf",t)}
  method map (line 6) | map(t,e){return $(this,"map",t,e,void 0,arguments)}
  method pop (line 6) | pop(){return V(this,"pop")}
  method push (line 6) | push(...t){return V(this,"push",t)}
  method reduce (line 6) | reduce(t,...e){return B(this,"reduce",t,e)}
  method reduceRight (line 6) | reduceRight(t,...e){return B(this,"reduceRight",t,e)}
  method shift (line 6) | shift(){return V(this,"shift")}
  method some (line 6) | some(t,e){return $(this,"some",t,e,void 0,arguments)}
  method splice (line 6) | splice(...t){return V(this,"splice",t)}
  method toReversed (line 6) | toReversed(){return N(this).toReversed()}
  method toSorted (line 6) | toSorted(t){return N(this).toSorted(t)}
  method toSpliced (line 6) | toSpliced(...t){return N(this).toSpliced(...t)}
  method unshift (line 6) | unshift(...t){return V(this,"unshift",t)}
  method values (line 6) | values(){return H(this,"values",Tt)}
  function H (line 6) | function H(t,e,n){const i=F(t),r=i[e]();return i===t||kt(t)||(r._next=r....
  function $ (line 6) | function $(t,e,n,i,r,o){const s=F(t),a=s!==t&&!kt(t),l=s[e];if(l!==W[e])...
  function B (line 6) | function B(t,e,n,i){const r=F(t);let o=n;return r!==t&&(kt(t)?n.length>3...
  function Y (line 6) | function Y(t,e,n){const i=Mt(t);L(i,"iterate",I);const r=i[e](...n);retu...
  function V (line 6) | function V(t,e,n=[]){M(),p();const i=Mt(t)[e].apply(t,n);return g(),S(),i}
  function X (line 6) | function X(t){(0,i.yk)(t)||(t=String(t));const e=Mt(this);return L(e,"ha...
  class G (line 6) | class G{constructor(t=!1,e=!1){this._isReadonly=t,this._isShallow=e}get(...
    method constructor (line 6) | constructor(t=!1,e=!1){this._isReadonly=t,this._isShallow=e}
    method get (line 6) | get(t,e,n){if("__v_skip"===e)return t["__v_skip"];const r=this._isRead...
  class Z (line 6) | class Z extends G{constructor(t=!1){super(!1,t)}set(t,e,n,r){let o=t[e];...
    method constructor (line 6) | constructor(t=!1){super(!1,t)}
    method set (line 6) | set(t,e,n,r){let o=t[e];if(!this._isShallow){const e=wt(o);if(kt(n)||w...
    method deleteProperty (line 6) | deleteProperty(t,e){const n=(0,i.RI)(t,e),r=t[e],o=Reflect.deletePrope...
    method has (line 6) | has(t,e){const n=Reflect.has(t,e);return(0,i.yk)(e)&&q.has(e)||L(t,"ha...
    method ownKeys (line 6) | ownKeys(t){return L(t,"iterate",(0,i.kJ)(t)?"length":E),Reflect.ownKey...
  class Q (line 6) | class Q extends G{constructor(t=!1){super(!0,t)}set(t,e){return!0}delete...
    method constructor (line 6) | constructor(t=!1){super(!0,t)}
    method set (line 6) | set(t,e){return!0}
    method deleteProperty (line 6) | deleteProperty(t,e){return!0}
  function it (line 6) | function it(t,e,n){return function(...r){const o=this["__v_raw"],s=Mt(o)...
  function rt (line 6) | function rt(t){return function(...e){return"delete"!==t&&("clear"===t?vo...
  function ot (line 6) | function ot(t,e){const n={get(n){const r=this["__v_raw"],o=Mt(r),s=Mt(n)...
  function st (line 6) | function st(t,e){const n=ot(t,e);return(e,r,o)=>"__v_isReactive"===r?!t:...
  function pt (line 6) | function pt(t){switch(t){case"Object":case"Array":return 1;case"Map":cas...
    method run (line 244) | run(t,e,n,i){const r=this.parse(t,e,n,i);return r?{setter:new dt(r.val...
    method validate (line 244) | validate(t,e,n){return!0}
  function gt (line 6) | function gt(t){return t["__v_skip"]||!Object.isExtensible(t)?0:pt((0,i.W...
    method parse (line 244) | parse(t,e,n){switch(e){case"G":case"GG":case"GGG":return n.era(t,{widt...
    method set (line 244) | set(t,e,n){return e.era=n,t.setFullYear(n,0,1),t.setHours(0,0,0,0),t}
  function mt (line 6) | function mt(t){return wt(t)?t:yt(t,!1,J,at,ut)}
  function bt (line 6) | function bt(t){return yt(t,!1,tt,lt,ht)}
  function xt (line 6) | function xt(t){return yt(t,!0,K,ct,dt)}
  function yt (line 6) | function yt(t,e,n,r,o){if(!(0,i.Kn)(t))return t;if(t["__v_raw"]&&(!e||!t...
  function vt (line 6) | function vt(t){return wt(t)?vt(t["__v_raw"]):!(!t||!t["__v_isReactive"])}
  function wt (line 6) | function wt(t){return!(!t||!t["__v_isReadonly"])}
  function kt (line 6) | function kt(t){return!(!t||!t["__v_isShallow"])}
  function _t (line 6) | function _t(t){return!!t&&!!t["__v_raw"]}
  function Mt (line 6) | function Mt(t){const e=t&&t["__v_raw"];return e?Mt(e):t}
  function St (line 6) | function St(t){return!(0,i.RI)(t,"__v_skip")&&Object.isExtensible(t)&&(0...
  function Ct (line 6) | function Ct(t){return!!t&&!0===t["__v_isRef"]}
  function At (line 6) | function At(t){return Pt(t,!1)}
  function Ot (line 6) | function Ot(t){return Pt(t,!0)}
    method parse (line 244) | parse(t,e,n){const i=t=>({year:t,isTwoDigitYear:"YY"===e});switch(e){c...
    method validate (line 244) | validate(t,e){return e.isTwoDigitYear||e.year>0}
    method set (line 244) | set(t,e,n,i){const r=At(t,i);if(n.isTwoDigitYear){const e=St(n.year,r)...
  function Pt (line 6) | function Pt(t,e){return Ct(t)?t:new Et(t,e)}
    method acquireContext (line 230) | acquireContext(t,e){const n=t&&t.getContext&&t.getContext("2d");return...
    method releaseContext (line 230) | releaseContext(t){const e=t.canvas;if(!e[dt])return!1;const n=e[dt].in...
    method addEventListener (line 230) | addEventListener(t,e,n){this.removeEventListener(t,e);const i=t.$proxi...
    method removeEventListener (line 230) | removeEventListener(t,e){const n=t.$proxies||(t.$proxies={}),i=n[e];if...
    method getDevicePixelRatio (line 230) | getDevicePixelRatio(){return window.devicePixelRatio}
    method getMaximumSize (line 230) | getMaximumSize(t,e,n,r){return(0,i.G)(t,e,n,r)}
    method isAttached (line 230) | isAttached(t){const e=t&&(0,i.I)(t);return!(!e||!e.isConnected)}
  class Et (line 6) | class Et{constructor(t,e){this.dep=new A,this["__v_isRef"]=!0,this["__v_...
    method constructor (line 6) | constructor(t,e){this.dep=new A,this["__v_isRef"]=!0,this["__v_isShall...
    method value (line 6) | get value(){return this.dep.track(),this._value}
    method value (line 6) | set value(t){const e=this._rawValue,n=this["__v_isShallow"]||kt(t)||wt...
    method parse (line 244) | parse(t,e){return _t("R"===e?4:e.length,t)}
    method set (line 244) | set(t,e,n){const i=u(t,0);return i.setFullYear(n,0,4),i.setHours(0,0,0...
  function Rt (line 6) | function Rt(t){return Ct(t)?t.value:t}
    method tooltipPosition (line 230) | tooltipPosition(t){const{x:e,y:n}=this.getProps(["x","y"],t);return{x:...
    method hasValue (line 230) | hasValue(){return(0,i.x)(this.x)&&(0,i.x)(this.y)}
    method getProps (line 230) | getProps(t,e){const n=this.$animations;if(!e||!n)return this;const i={...
    method parse (line 244) | parse(t,e){return _t("u"===e?4:e.length,t)}
    method set (line 244) | set(t,e,n){return t.setFullYear(n,0,1),t.setHours(0,0,0,0),t}
  function Lt (line 6) | function Lt(t){return vt(t)?t:new Proxy(t,It)}
    method parse (line 244) | parse(t,e,n){switch(e){case"q":case"qq":return kt(e.length,t);case"qo"...
    method validate (line 244) | validate(t,e){return e>=1&&e<=4}
    method set (line 244) | set(t,e,n){return t.setMonth(3*(n-1),1),t.setHours(0,0,0,0),t}
  class zt (line 6) | class zt{constructor(t,e,n){this.fn=t,this.setter=e,this._value=void 0,t...
    method constructor (line 6) | constructor(t,e,n){this.fn=t,this.setter=e,this._value=void 0,this.dep...
    method notify (line 6) | notify(){if(this.flags|=16,!(8&this.flags||o===this))return f(this,!0)...
    method value (line 6) | get value(){const t=this.dep.track();return y(this),t&&(t.version=this...
    method value (line 6) | set value(t){this.setter&&this.setter(t)}
    method parse (line 244) | parse(t,e,n){const i=t=>t-1;switch(e){case"M":return xt(yt(mt.month,t)...
    method validate (line 244) | validate(t,e){return e>=0&&e<=11}
    method set (line 244) | set(t,e,n){return t.setMonth(n,1),t.setHours(0,0,0,0),t}
  function Nt (line 6) | function Nt(t,e,n=!1){let r,o;(0,i.mf)(t)?r=t:(r=t.get,o=t.set);const s=...
    method parse (line 244) | parse(t,e,n){const i=t=>t-1;switch(e){case"L":return xt(yt(mt.month,t)...
    method validate (line 244) | validate(t,e){return e>=0&&e<=11}
    method set (line 244) | set(t,e,n){return t.setMonth(n,1),t.setHours(0,0,0,0),t}
  function Wt (line 6) | function Wt(t,e=!1,n=Ht){if(n){let e=jt.get(n);e||jt.set(n,e=[]),e.push(...
    method parse (line 244) | parse(t,e,n){switch(e){case"w":return yt(mt.week,t);case"wo":return n....
    method validate (line 244) | validate(t,e){return e>=1&&e<=53}
    method set (line 244) | set(t,e,n,i){return Ct(Ht(t,n,i),i)}
  function $t (line 6) | function $t(t,e,n=i.kT){const{immediate:r,deep:o,once:s,scheduler:l,augm...
  function Bt (line 6) | function Bt(t,e=1/0,n){if(e<=0||!(0,i.Kn)(t)||t["__v_skip"])return t;if(...
  function o (line 6) | function o(t,e,n,i){try{return i?t(...i):t()}catch(r){a(r,e,n)}}
  function s (line 6) | function s(t,e,n,i){if((0,r.mf)(t)){const s=o(t,e,n,i);return s&&(0,r.tI...
    method constructor (line 6) | constructor(t=!1){this.detached=t,this._active=!0,this._on=0,this.effe...
    method active (line 6) | get active(){return this._active}
    method pause (line 6) | pause(){if(this._active){let t,e;if(this._isPaused=!0,this.scopes)for(...
    method resume (line 6) | resume(){if(this._active&&this._isPaused){let t,e;if(this._isPaused=!1...
    method run (line 6) | run(t){if(this._active){const e=r;try{return r=this,t()}finally{r=e}}e...
    method on (line 6) | on(){1===++this._on&&(this.prevScope=r,r=this)}
    method off (line 6) | off(){this._on>0&&0===--this._on&&(r=this.prevScope,this.prevScope=voi...
    method stop (line 6) | stop(t){if(this._active){let e,n;for(this._active=!1,e=0,n=this.effect...
  function a (line 6) | function a(t,e,n,s=!0){const a=e?e.vnode:null,{errorHandler:c,throwUnhan...
  function l (line 6) | function l(t,e,n,i=!0,r=!1){if(r)throw t;console.error(t)}
    method constructor (line 230) | constructor(t,e,n,r){const o=e[n];r=(0,i.a)([t.to,r,o,t.from]);const s...
    method active (line 230) | active(){return this._active}
    method update (line 230) | update(t,e,n){if(this._active){this._notify(!1);const r=this._target[t...
    method cancel (line 230) | cancel(){this._active&&(this.tick(Date.now()),this._active=!1,this._no...
    method tick (line 230) | tick(t){const e=t-this._start,n=this._duration,i=this._prop,r=this._fr...
    method wait (line 230) | wait(){const t=this._promises||(this._promises=[]);return new Promise(...
    method _notify (line 230) | _notify(t){const e=t?"res":"rej",n=this._promises||[];for(let i=0;i<n....
  function m (line 6) | function m(t){const e=g||p;return t?e.then(this?t.bind(this):t):e}
  function b (line 6) | function b(t){let e=u+1,n=c.length;while(e<n){const i=e+n>>>1,r=c[i],o=_...
  function x (line 6) | function x(t){if(!(1&t.flags)){const e=_(t),n=c[c.length-1];!n||!(2&t.fl...
  function y (line 6) | function y(){g||(g=p.then(M))}
  function v (line 6) | function v(t){(0,r.kJ)(t)?h.push(...t):d&&-1===t.id?d.splice(f+1,0,t):1&...
  function w (line 6) | function w(t,e,n=u+1){for(0;n<c.length;n++){const e=c[n];if(e&&2&e.flags...
  function k (line 6) | function k(t){if(h.length){const t=[...new Set(h)].sort(((t,e)=>_(t)-_(e...
  function M (line 6) | function M(t){r.dG;try{for(u=0;u<c.length;u++){const t=c[u];!t||8&t.flag...
  function D (line 6) | function D(t){const e=S;return S=t,T=t&&t.type.__scopeId||null,e}
  function C (line 6) | function C(t,e=S,n){if(!e)return t;if(t._n)return t;const i=(...n)=>{i._...
    method constructor (line 6) | constructor(t,e){this.sub=t,this.dep=e,this.version=e.version,this.nex...
  function A (line 6) | function A(t,e){if(null===S)return t;const n=Ln(S),o=t.dirs||(t.dirs=[])...
    method constructor (line 6) | constructor(t){this.computed=t,this.version=0,this.activeLink=void 0,t...
    method track (line 6) | track(t){if(!o||!k||o===this.computed)return;let e=this.activeLink;if(...
    method trigger (line 6) | trigger(t){this.version++,D++,this.notify(t)}
    method notify (line 6) | notify(t){p();try{0;for(let t=this.subs;t;t=t.prevSub)t.sub.notify()&&...
  function O (line 6) | function O(t,e,n,r){const o=t.dirs,a=e&&e.dirs;for(let l=0;l<o.length;l+...
  function L (line 6) | function L(){const t={isMounted:!1,isLeaving:!1,isUnmounting:!1,leavingV...
  function F (line 6) | function F(t,e){const{leavingVNodes:n}=t;let i=n.get(e.type);return i||(...
  function j (line 6) | function j(t,e,n,i,o){const{appear:a,mode:l,persisted:c=!1,onBeforeEnter...
  function H (line 6) | function H(t,e){6&t.shapeFlag&&t.component?(t.transition=e,H(t.component...
  function W (line 6) | function W(t,e=!1,n){let i=[],r=0;for(let o=0;o<t.length;o++){let s=t[o]...
  function $ (line 7) | function $(t,e){return(0,r.mf)(t)?(()=>(0,r.l7)({name:t.name},e,{setup:t...
  function B (line 7) | function B(t){t.ids=[t.ids[0]+t.ids[2]+++"-",0,0]}
  function Y (line 7) | function Y(t,e,n,s,a=!1){if((0,r.kJ)(t))return void t.forEach(((t,i)=>Y(...
  function q (line 8) | function q(t,e){return(0,r.kJ)(t)?t.some((t=>q(t,e))):(0,r.HD)(t)?t.spli...
    method constructor (line 237) | constructor(t){if(t instanceof q)return t;const e=typeof t;let n;"obje...
    method valid (line 237) | get valid(){return this._valid}
    method rgb (line 237) | get rgb(){var t=Y(this._rgb);return t&&(t.a=a(t.a)),t}
    method rgb (line 237) | set rgb(t){this._rgb=V(t)}
    method rgbString (line 237) | rgbString(){return this._valid?j(this._rgb):void 0}
    method hexString (line 237) | hexString(){return this._valid?b(this._rgb):void 0}
    method hslString (line 237) | hslString(){return this._valid?P(this._rgb):void 0}
    method mix (line 237) | mix(t,e){if(t){const n=this.rgb,i=t.rgb;let r;const o=e===r?.5:e,s=2*o...
    method interpolate (line 237) | interpolate(t,e){return t&&(this._rgb=$(this._rgb,t._rgb,e)),this}
    method clone (line 237) | clone(){return new q(this.rgb)}
    method alpha (line 237) | alpha(t){return this._rgb.a=s(t),this}
    method clearer (line 237) | clearer(t){const e=this._rgb;return e.a*=1-t,this}
    method greyscale (line 237) | greyscale(){const t=this._rgb,e=i(.3*t.r+.59*t.g+.11*t.b);return t.r=t...
    method opaquer (line 237) | opaquer(t){const e=this._rgb;return e.a*=1+t,this}
    method negate (line 237) | negate(){const t=this._rgb;return t.r=255-t.r,t.g=255-t.g,t.b=255-t.b,...
    method lighten (line 237) | lighten(t){return B(this._rgb,2,t),this}
    method darken (line 237) | darken(t){return B(this._rgb,2,-t),this}
    method saturate (line 237) | saturate(t){return B(this._rgb,1,t),this}
    method desaturate (line 237) | desaturate(t){return B(this._rgb,1,-t),this}
    method rotate (line 237) | rotate(t){return O(this._rgb,t),this}
  function X (line 8) | function X(t,e){Z(t,"a",e)}
  function G (line 8) | function G(t,e){Z(t,"da",e)}
    method constructor (line 6) | constructor(t=!1,e=!1){this._isReadonly=t,this._isShallow=e}
    method get (line 6) | get(t,e,n){if("__v_skip"===e)return t["__v_skip"];const r=this._isRead...
  function Z (line 8) | function Z(t,e,n=yn){const i=t.__wdc||(t.__wdc=()=>{let e=n;while(e){if(...
    method constructor (line 6) | constructor(t=!1){super(!1,t)}
    method set (line 6) | set(t,e,n,r){let o=t[e];if(!this._isShallow){const e=wt(o);if(kt(n)||w...
    method deleteProperty (line 6) | deleteProperty(t,e){const n=(0,i.RI)(t,e),r=t[e],o=Reflect.deletePrope...
    method has (line 6) | has(t,e){const n=Reflect.has(t,e);return(0,i.yk)(e)&&q.has(e)||L(t,"ha...
    method ownKeys (line 6) | ownKeys(t){return L(t,"iterate",(0,i.kJ)(t)?"length":E),Reflect.ownKey...
  function Q (line 8) | function Q(t,e,n,i){const o=tt(e,t,i,!0);at((()=>{(0,r.Od)(i[e],o)}),n)}
    method constructor (line 6) | constructor(t=!1){super(!0,t)}
    method set (line 6) | set(t,e){return!0}
    method deleteProperty (line 6) | deleteProperty(t,e){return!0}
  function J (line 8) | function J(t){t.shapeFlag&=-257,t.shapeFlag&=-513}
  function K (line 8) | function K(t){return 128&t.shapeFlag?t.ssContent:t}
  function tt (line 8) | function tt(t,e,n=yn,r=!1){if(n){const o=n[t]||(n[t]=[]),a=e.__weh||(e._...
  function ht (line 8) | function ht(t,e=yn){tt("ec",t,e)}
    method acquireContext (line 230) | acquireContext(t){return t&&t.getContext&&t.getContext("2d")||null}
    method updateConfig (line 230) | updateConfig(t){t.options.animation=!1}
    method validate (line 244) | validate(t,e){return!0}
  function ft (line 8) | function ft(t,e){return mt(dt,t,!0,e)||t}
    method constructor (line 244) | constructor(t,e){super(),this.context=t||(t=>u(e,t))}
    method set (line 244) | set(t,e){return e.timestampIsSet?t:u(t,lt(t,this.context))}
  function gt (line 8) | function gt(t){return(0,r.HD)(t)?mt(dt,t,!1)||t:t||pt}
    method parse (line 244) | parse(t,e,n){switch(e){case"G":case"GG":case"GGG":return n.era(t,{widt...
    method set (line 244) | set(t,e,n){return e.era=n,t.setFullYear(n,0,1),t.setHours(0,0,0,0),t}
  function mt (line 8) | function mt(t,e,n=!0,i=!1){const o=S||yn;if(o){const n=o.type;if(t===dt)...
  function bt (line 8) | function bt(t,e){return t&&(t[e]||t[(0,r._A)(e)]||t[(0,r.kC)((0,r._A)(e)...
  function xt (line 8) | function xt(t,e,n,o){let s;const a=n&&n[o],l=(0,r.kJ)(t);if(l||(0,r.HD)(...
  function yt (line 8) | function yt(t,e,n={},i,o){if(S.ce||S.parent&&V(S.parent)&&S.parent.ce)re...
  function vt (line 8) | function vt(t){return t.some((t=>!Ke(t)||t.type!==$e&&!(t.type===He&&!vt...
  method get (line 8) | get({_:t},e){if("__v_skip"===e)return!0;const{ctx:n,setupState:o,data:s,...
  method set (line 8) | set({_:t},e,n){const{data:i,setupState:o,ctx:s}=t;return _t(o,e)?(o[e]=n...
  method has (line 8) | has({_:{data:t,setupState:e,accessCache:n,ctx:i,appContext:o,propsOption...
  method defineProperty (line 8) | defineProperty(t,e,n){return null!=n.get?t._.accessCache[e]=0:(0,r.RI)(n...
  function St (line 8) | function St(t){return(0,r.kJ)(t)?t.reduce(((t,e)=>(t[e]=null,t)),{}):t}
  function Dt (line 8) | function Dt(t){const e=Pt(t),n=t.proxy,o=t.ctx;Tt=!1,e.beforeCreate&&At(...
    method parse (line 244) | parse(t,e,n){const i=t=>({year:t,isTwoDigitYear:"yy"===e});switch(e){c...
    method validate (line 244) | validate(t,e){return e.isTwoDigitYear||e.year>0}
    method set (line 244) | set(t,e,n){const i=t.getFullYear();if(n.isTwoDigitYear){const e=St(n.y...
  function Ct (line 8) | function Ct(t,e,n=r.dG){(0,r.kJ)(t)&&(t=zt(t));for(const o in t){const n...
  function At (line 8) | function At(t,e,n){s((0,r.kJ)(t)?t.map((t=>t.bind(e.proxy))):t.bind(e.pr...
  function Ot (line 8) | function Ot(t,e,n,i){let o=i.includes(".")?De(n,i):()=>n[i];if((0,r.HD)(...
    method parse (line 244) | parse(t,e,n){const i=t=>({year:t,isTwoDigitYear:"YY"===e});switch(e){c...
    method validate (line 244) | validate(t,e){return e.isTwoDigitYear||e.year>0}
    method set (line 244) | set(t,e,n,i){const r=At(t,i);if(n.isTwoDigitYear){const e=St(n.year,r)...
  function Pt (line 8) | function Pt(t){const e=t.type,{mixins:n,extends:i}=e,{mixins:o,optionsCa...
    method acquireContext (line 230) | acquireContext(t,e){const n=t&&t.getContext&&t.getContext("2d");return...
    method releaseContext (line 230) | releaseContext(t){const e=t.canvas;if(!e[dt])return!1;const n=e[dt].in...
    method addEventListener (line 230) | addEventListener(t,e,n){this.removeEventListener(t,e);const i=t.$proxi...
    method removeEventListener (line 230) | removeEventListener(t,e){const n=t.$proxies||(t.$proxies={}),i=n[e];if...
    method getDevicePixelRatio (line 230) | getDevicePixelRatio(){return window.devicePixelRatio}
    method getMaximumSize (line 230) | getMaximumSize(t,e,n,r){return(0,i.G)(t,e,n,r)}
    method isAttached (line 230) | isAttached(t){const e=t&&(0,i.I)(t);return!(!e||!e.isConnected)}
  function Et (line 8) | function Et(t,e,n,i=!1){const{mixins:r,extends:o}=e;o&&Et(t,o,n,!0),r&&r...
    method constructor (line 6) | constructor(t,e){this.dep=new A,this["__v_isRef"]=!0,this["__v_isShall...
    method value (line 6) | get value(){return this.dep.track(),this._value}
    method value (line 6) | set value(t){const e=this._rawValue,n=this["__v_isShallow"]||kt(t)||wt...
    method parse (line 244) | parse(t,e){return _t("R"===e?4:e.length,t)}
    method set (line 244) | set(t,e,n){const i=u(t,0);return i.setFullYear(n,0,4),i.setHours(0,0,0...
  function It (line 8) | function It(t,e){return e?t?function(){return(0,r.l7)((0,r.mf)(t)?t.call...
    method parse (line 244) | parse(t,e,n){switch(e){case"Q":case"QQ":return kt(e.length,t);case"Qo"...
    method validate (line 244) | validate(t,e){return e>=1&&e<=4}
    method set (line 244) | set(t,e,n){return t.setMonth(3*(n-1),1),t.setHours(0,0,0,0),t}
  function Lt (line 8) | function Lt(t,e){return Ft(zt(t),zt(e))}
    method parse (line 244) | parse(t,e,n){switch(e){case"q":case"qq":return kt(e.length,t);case"qo"...
    method validate (line 244) | validate(t,e){return e>=1&&e<=4}
    method set (line 244) | set(t,e,n){return t.setMonth(3*(n-1),1),t.setHours(0,0,0,0),t}
  function zt (line 8) | function zt(t){if((0,r.kJ)(t)){const e={};for(let n=0;n<t.length;n++)e[t...
    method constructor (line 6) | constructor(t,e,n){this.fn=t,this.setter=e,this._value=void 0,this.dep...
    method notify (line 6) | notify(){if(this.flags|=16,!(8&this.flags||o===this))return f(this,!0)...
    method value (line 6) | get value(){const t=this.dep.track();return y(this),t&&(t.version=this...
    method value (line 6) | set value(t){this.setter&&this.setter(t)}
    method parse (line 244) | parse(t,e,n){const i=t=>t-1;switch(e){case"M":return xt(yt(mt.month,t)...
    method validate (line 244) | validate(t,e){return e>=0&&e<=11}
    method set (line 244) | set(t,e,n){return t.setMonth(n,1),t.setHours(0,0,0,0),t}
  function Nt (line 8) | function Nt(t,e){return t?[...new Set([].concat(t,e))]:e}
    method parse (line 244) | parse(t,e,n){const i=t=>t-1;switch(e){case"L":return xt(yt(mt.month,t)...
    method validate (line 244) | validate(t,e){return e>=0&&e<=11}
    method set (line 244) | set(t,e,n){return t.setMonth(n,1),t.setHours(0,0,0,0),t}
  function Ft (line 8) | function Ft(t,e){return t?(0,r.l7)(Object.create(null),t,e):e}
  function jt (line 8) | function jt(t,e){return t?(0,r.kJ)(t)&&(0,r.kJ)(e)?[...new Set([...t,......
  function Ht (line 8) | function Ht(t,e){if(!t)return e;if(!e)return t;const n=(0,r.l7)(Object.c...
  function Wt (line 8) | function Wt(){return{app:null,config:{isNativeTag:r.NO,performance:!1,gl...
    method parse (line 244) | parse(t,e,n){switch(e){case"w":return yt(mt.week,t);case"wo":return n....
    method validate (line 244) | validate(t,e){return e>=1&&e<=53}
    method set (line 244) | set(t,e,n,i){return Ct(Ht(t,n,i),i)}
  function Bt (line 8) | function Bt(t,e){return function(n,i=null){(0,r.mf)(n)||(n=(0,r.l7)({},n...
  function Vt (line 8) | function Vt(t,e){if(yn){let n=yn.provides;const i=yn.parent&&yn.parent.p...
  function Ut (line 8) | function Ut(t,e,n=!1){const i=vn();if(i||Yt){let o=Yt?Yt._context.provid...
    method parse (line 244) | parse(t,e,n){switch(e){case"I":return yt(mt.week,t);case"Io":return n....
    method validate (line 244) | validate(t,e){return e>=1&&e<=53}
    method set (line 244) | set(t,e,n){return Pt(Vt(t,n))}
  function Zt (line 8) | function Zt(t,e,n,r=!1){const o={},s=Xt();t.propsDefaults=Object.create(...
    method parse (line 244) | parse(t,e,n){switch(e){case"D":case"DD":return yt(mt.dayOfYear,t);case...
    method validate (line 244) | validate(t,e){const n=t.getFullYear(),i=Tt(n);return i?e>=1&&e<=366:e>...
    method set (line 244) | set(t,e,n){return t.setMonth(0,n),t.setHours(0,0,0,0),t}
  function Qt (line 8) | function Qt(t,e,n,o){const{props:s,attrs:a,vnode:{patchFlag:l}}=t,c=(0,i...
  function Jt (line 8) | function Jt(t,e,n,o){const[s,a]=t.propsOptions;let l,c=!1;if(e)for(let i...
  function Kt (line 8) | function Kt(t,e,n,i,o,s){const a=t[n];if(null!=a){const t=(0,r.RI)(a,"de...
    method constructor (line 230) | constructor(t){super(),this.id=t.id,this.type=t.type,this.options=void...
    method init (line 230) | init(t){this.options=t.setContext(this.getContext()),this.axis=t.axis,...
    method parse (line 230) | parse(t,e){return t}
    method getUserBounds (line 230) | getUserBounds(){let{_userMin:t,_userMax:e,_suggestedMin:n,_suggestedMa...
    method getMinMax (line 230) | getMinMax(t){let e,{min:n,max:r,minDefined:o,maxDefined:s}=this.getUse...
    method getPadding (line 230) | getPadding(){return{left:this.paddingLeft||0,top:this.paddingTop||0,ri...
    method getTicks (line 230) | getTicks(){return this.ticks}
    method getLabels (line 230) | getLabels(){const t=this.chart.data;return this.options.labels||(this....
    method getLabelItems (line 230) | getLabelItems(t=this.chart.chartArea){const e=this._labelItems||(this....
    method beforeLayout (line 230) | beforeLayout(){this._cache={},this._dataLimitsCached=!1}
    method beforeUpdate (line 230) | beforeUpdate(){(0,i.Q)(this.options.beforeUpdate,[this])}
    method update (line 230) | update(t,e,n){const{beginAtZero:r,grace:o,ticks:s}=this.options,a=s.sa...
    method configure (line 230) | configure(){let t,e,n=this.options.reverse;this.isHorizontal()?(t=this...
    method afterUpdate (line 230) | afterUpdate(){(0,i.Q)(this.options.afterUpdate,[this])}
    method beforeSetDimensions (line 230) | beforeSetDimensions(){(0,i.Q)(this.options.beforeSetDimensions,[this])}
    method setDimensions (line 230) | setDimensions(){this.isHorizontal()?(this.width=this.maxWidth,this.lef...
    method afterSetDimensions (line 230) | afterSetDimensions(){(0,i.Q)(this.options.afterSetDimensions,[this])}
    method _callHooks (line 230) | _callHooks(t){this.chart.notifyPlugins(t,this.getContext()),(0,i.Q)(th...
    method beforeDataLimits (line 230) | beforeDataLimits(){this._callHooks("beforeDataLimits")}
    method determineDataLimits (line 230) | determineDataLimits(){}
    method afterDataLimits (line 230) | afterDataLimits(){this._callHooks("afterDataLimits")}
    method beforeBuildTicks (line 230) | beforeBuildTicks(){this._callHooks("beforeBuildTicks")}
    method buildTicks (line 230) | buildTicks(){return[]}
    method afterBuildTicks (line 230) | afterBuildTicks(){this._callHooks("afterBuildTicks")}
    method beforeTickToLabelConversion (line 230) | beforeTickToLabelConversion(){(0,i.Q)(this.options.beforeTickToLabelCo...
    method generateTickLabels (line 230) | generateTickLabels(t){const e=this.options.ticks;let n,r,o;for(n=0,r=t...
    method afterTickToLabelConversion (line 230) | afterTickToLabelConversion(){(0,i.Q)(this.options.afterTickToLabelConv...
    method beforeCalculateLabelRotation (line 230) | beforeCalculateLabelRotation(){(0,i.Q)(this.options.beforeCalculateLab...
    method calculateLabelRotation (line 230) | calculateLabelRotation(){const t=this.options,e=t.ticks,n=Bt(this.tick...
    method afterCalculateLabelRotation (line 230) | afterCalculateLabelRotation(){(0,i.Q)(this.options.afterCalculateLabel...
    method afterAutoSkip (line 230) | afterAutoSkip(){}
    method beforeFit (line 230) | beforeFit(){(0,i.Q)(this.options.beforeFit,[this])}
    method fit (line 230) | fit(){const t={width:0,height:0},{chart:e,options:{ticks:n,title:r,gri...
    method _calculatePadding (line 230) | _calculatePadding(t,e,n,i){const{ticks:{align:r,padding:o},position:s}...
    method _handleMargins (line 230) | _handleMargins(){this._margins&&(this._margins.left=Math.max(this.padd...
    method afterFit (line 230) | afterFit(){(0,i.Q)(this.options.afterFit,[this])}
    method isHorizontal (line 230) | isHorizontal(){const{axis:t,position:e}=this.options;return"top"===e||...
    method isFullSize (line 230) | isFullSize(){return this.options.fullSize}
    method _convertTicksToLabels (line 230) | _convertTicksToLabels(t){let e,n;for(this.beforeTickToLabelConversion(...
    method _getLabelSizes (line 230) | _getLabelSizes(){let t=this._labelSizes;if(!t){const e=this.options.ti...
    method _computeLabelSizes (line 230) | _computeLabelSizes(t,e,n){const{ctx:r,_longestTextCache:o}=this,s=[],a...
    method getLabelForValue (line 230) | getLabelForValue(t){return t}
    method getPixelForValue (line 230) | getPixelForValue(t,e){return NaN}
    method getValueForPixel (line 230) | getValueForPixel(t){}
    method getPixelForTick (line 230) | getPixelForTick(t){const e=this.ticks;return t<0||t>e.length-1?null:th...
    method getPixelForDecimal (line 230) | getPixelForDecimal(t){this._reversePixels&&(t=1-t);const e=this._start...
    method getDecimalForPixel (line 230) | getDecimalForPixel(t){const e=(t-this._startPixel)/this._length;return...
    method getBasePixel (line 230) | getBasePixel(){return this.getPixelForValue(this.getBaseValue())}
    method getBaseValue (line 230) | getBaseValue(){const{min:t,max:e}=this;return t<0&&e<0?e:t>0&&e>0?t:0}
    method getContext (line 230) | getContext(t){const e=this.ticks||[];if(t>=0&&t<e.length){const n=e[t]...
    method _tickSize (line 230) | _tickSize(){const t=this.options.ticks,e=(0,i.t)(this.labelRotation),n...
    method _isVisible (line 230) | _isVisible(){const t=this.options.display;return"auto"!==t?!!t:this.ge...
    method _computeGridLineItems (line 230) | _computeGridLineItems(t){const e=this.axis,n=this.chart,r=this.options...
    method _computeLabelItems (line 230) | _computeLabelItems(t){const e=this.axis,n=this.options,{position:r,tic...
    method _getXAxisLabelAlignment (line 230) | _getXAxisLabelAlignment(){const{position:t,ticks:e}=this.options,n=-(0...
    method _getYAxisLabelAlignment (line 230) | _getYAxisLabelAlignment(t){const{position:e,ticks:{crossAlign:n,mirror...
    method _computeLabelArea (line 230) | _computeLabelArea(){if(this.options.ticks.mirror)return;const t=this.c...
    method drawBackground (line 230) | drawBackground(){const{ctx:t,options:{backgroundColor:e},left:n,top:i,...
    method getLineWidthForValue (line 230) | getLineWidthForValue(t){const e=this.options.grid;if(!this._isVisible(...
    method drawGrid (line 230) | drawGrid(t){const e=this.options.grid,n=this.ctx,i=this._gridLineItems...
    method drawBorder (line 230) | drawBorder(){const{chart:t,ctx:e,options:{border:n,grid:r}}=this,o=n.s...
    method drawLabels (line 230) | drawLabels(t){const e=this.options.ticks;if(!e.display)return;const n=...
    method drawTitle (line 230) | drawTitle(){const{ctx:t,options:{position:e,title:n,reverse:r}}=this;i...
    method draw (line 230) | draw(t){this._isVisible()&&(this.drawBackground(),this.drawGrid(t),thi...
    method _layers (line 230) | _layers(){const t=this.options,e=t.ticks&&t.ticks.z||0,n=(0,i.v)(t.gri...
    method getMatchingVisibleMetas (line 230) | getMatchingVisibleMetas(t){const e=this.chart.getSortedVisibleDatasetM...
    method _resolveTickFontOptions (line 230) | _resolveTickFontOptions(t){const e=this.options.ticks.setContext(this....
    method _maxDigits (line 230) | _maxDigits(){const t=this._resolveTickFontOptions(0).lineHeight;return...
    method parse (line 244) | parse(t,e,n){switch(e){case"E":case"EE":case"EEE":return n.day(t,{widt...
    method validate (line 244) | validate(t,e){return e>=0&&e<=6}
    method set (line 244) | set(t,e,n,i){return t=Jt(t,n,i),t.setHours(0,0,0,0),t}
  function ee (line 8) | function ee(t,e,n=!1){const i=n?te:e.propsCache,o=i.get(t);if(o)return o...
    method parse (line 244) | parse(t,e,n,i){const r=t=>{const e=7*Math.floor((t-1)/7);return(t+i.we...
    method validate (line 244) | validate(t,e){return e>=0&&e<=6}
    method set (line 244) | set(t,e,n,i){return t=Jt(t,n,i),t.setHours(0,0,0,0),t}
  function ne (line 8) | function ne(t){return"$"!==t[0]&&!(0,r.Gg)(t)}
    method inRange (line 258) | inRange(t,e,n,i){return y({x:t,y:e},{rect:this.getProps(["x","y","x2",...
    method getCenterPoint (line 258) | getCenterPoint(t){return v(this,t)}
    method draw (line 258) | draw(t){const e=this.options,n=!(0,r.h)(this._visible)||this._visible;...
    method resolveElementProperties (line 258) | resolveElementProperties(t,e){let n;if(I(e))n=mt(t,e);else{const{cente...
  function he (line 8) | function he(){"boolean"!==typeof __VUE_PROD_HYDRATION_MISMATCH_DETAILS__...
    method parse (line 244) | parse(t,e,n){switch(e){case"k":return yt(mt.hour24h,t);case"ko":return...
    method validate (line 244) | validate(t,e){return e>=1&&e<=24}
    method set (line 244) | set(t,e,n){const i=n<=24?n%24:n;return t.setHours(i,0,0,0),t}
  function fe (line 8) | function fe(t){return pe(t)}
    method parse (line 244) | parse(t,e,n){switch(e){case"s":return yt(mt.second,t);case"so":return ...
    method validate (line 244) | validate(t,e){return e>=0&&e<=59}
    method set (line 244) | set(t,e,n){return t.setSeconds(n,0),t}
    method inRange (line 258) | inRange(t,e,n,i){const r=(this.options.borderWidth+this.options.hitTol...
    method getCenterPoint (line 258) | getCenterPoint(t){return v(this,t)}
    method draw (line 258) | draw(t){const{x:e,y:n,x2:i,y2:r,cp:o,options:s}=this;if(t.save(),!V(t,...
    method label (line 258) | get label(){return this.elements&&this.elements[0]}
    method resolveElementProperties (line 258) | resolveElementProperties(t,e){const n=yt(t,e),{x:i,y:o,x2:s,y2:a}=n,l=...
  function pe (line 8) | function pe(t,e){he();const n=(0,r.E9)();n.__VUE__=!0;const{insert:o,rem...
    method parse (line 244) | parse(t,e){const n=t=>Math.trunc(t*Math.pow(10,3-e.length));return xt(...
    method set (line 244) | set(t,e,n){return t.setMilliseconds(n),t}
  function ge (line 8) | function ge({type:t,props:e},n){return"svg"===n&&"foreignObject"===t||"m...
  function me (line 8) | function me({effect:t,job:e},n){n?(t.flags|=32,e.flags|=4):(t.flags&=-33...
    method parse (line 244) | parse(t,e){switch(e){case"X":return vt(bt.basicOptionalMinutes,t);case...
    method set (line 244) | set(t,e,n){return e.timestampIsSet?t:u(t,t.getTime()-ge(t)-n)}
  function be (line 8) | function be(t,e){return(!t||t&&!t.pendingBranch)&&e&&!e.persisted}
    method parse (line 244) | parse(t,e){switch(e){case"x":return vt(bt.basicOptionalMinutes,t);case...
    method set (line 244) | set(t,e,n){return e.timestampIsSet?t:u(t,t.getTime()-ge(t)-n)}
  function xe (line 8) | function xe(t,e,n=!1){const i=t.children,o=e.children;if((0,r.kJ)(i)&&(0...
    method parse (line 244) | parse(t){return wt(t)}
    method set (line 244) | set(t,e,n){return[u(t,1e3*n),{timestampIsSet:!0}]}
  function ye (line 8) | function ye(t){const e=t.slice(),n=[0];let i,r,o,s,a;const l=t.length;fo...
    method parse (line 244) | parse(t){return wt(t)}
    method set (line 244) | set(t,e,n){return[u(t,n),{timestampIsSet:!0}]}
  function ve (line 8) | function ve(t){const e=t.subTree.component;if(e)return e.asyncDep&&!e.as...
  function we (line 8) | function we(t){if(t)for(let e=0;e<t.length;e++)t[e].flags|=8}
  function Me (line 8) | function Me(t,e,n){return Se(t,e,n)}
  function Se (line 8) | function Se(t,e,n=r.kT){const{immediate:o,deep:a,flush:l,once:c}=n;const...
  function Te (line 8) | function Te(t,e,n){const i=this.proxy,o=(0,r.HD)(t)?t.includes(".")?De(i...
  function De (line 8) | function De(t,e){const n=e.split(".");return()=>{let e=t;for(let t=0;t<n...
    method constructor (line 230) | constructor(t){this._config=ke(t),this._scopeCache=new Map,this._resol...
    method platform (line 230) | get platform(){return this._config.platform}
    method type (line 230) | get type(){return this._config.type}
    method type (line 230) | set type(t){this._config.type=t}
    method data (line 230) | get data(){return this._config.data}
    method data (line 230) | set data(t){this._config.data=we(t)}
    method options (line 230) | get options(){return this._config.options}
    method options (line 230) | set options(t){this._config.options=t}
    method plugins (line 230) | get plugins(){return this._config.plugins}
    method update (line 230) | update(){const t=this._config;this.clearCache(),ve(t)}
    method clearCache (line 230) | clearCache(){this._scopeCache.clear(),this._resolverCache.clear()}
    method datasetScopeKeys (line 230) | datasetScopeKeys(t){return Se(t,(()=>[[`datasets.${t}`,""]]))}
    method datasetAnimationScopeKeys (line 230) | datasetAnimationScopeKeys(t,e){return Se(`${t}.transition.${e}`,(()=>[...
    method datasetElementScopeKeys (line 230) | datasetElementScopeKeys(t,e){return Se(`${t}-${e}`,(()=>[[`datasets.${...
    method pluginScopeKeys (line 230) | pluginScopeKeys(t){const e=t.id,n=this.type;return Se(`${n}-plugin-${e...
    method _cachedScopes (line 230) | _cachedScopes(t,e){const n=this._scopeCache;let i=n.get(t);return i&&!...
    method getOptionScopes (line 230) | getOptionScopes(t,e,n){const{options:r,type:o}=this,s=this._cachedScop...
    method chartOptionScopes (line 230) | chartOptionScopes(){const{options:t,type:e}=this;return[t,i.a3[e]||{},...
    method resolveNamedOptions (line 230) | resolveNamedOptions(t,e,n,r=[""]){const o={$shared:!0},{resolver:s,sub...
    method createResolver (line 230) | createResolver(t,e,n=[""],r){const{resolver:o}=Ce(this._resolverCache,...
  function Ae (line 8) | function Ae(t,e,...n){if(t.isUnmounted)return;const i=t.vnode.props||r.k...
  function Oe (line 8) | function Oe(t,e,n=!1){const i=e.emitsCache,o=i.get(t);if(void 0!==o)retu...
  function Pe (line 8) | function Pe(t,e){return!(!t||!(0,r.F7)(e))&&(e=e.slice(2).replace(/Once$...
  function Ee (line 8) | function Ee(t){const{type:e,vnode:n,proxy:i,withProxy:o,propsOptions:[s]...
  function Le (line 8) | function Le(t,e,n){const{props:i,children:r,component:o}=t,{props:s,chil...
    method inRange (line 258) | inRange(t,e,n,i){const o=this.options.rotation,s=(this.options.borderW...
    method getCenterPoint (line 258) | getCenterPoint(t){return v(this,t)}
    method draw (line 258) | draw(t){const{width:e,height:n,centerX:i,centerY:o,options:s}=this;t.s...
    method label (line 258) | get label(){return this.elements&&this.elements[0]}
    method resolveElementProperties (line 258) | resolveElementProperties(t,e){return vt(t,e)}
  function ze (line 8) | function ze(t,e,n){const i=Object.keys(e);if(i.length!==Object.keys(t).l...
  function Ne (line 8) | function 
Condensed preview — 325 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,925K chars).
[
  {
    "path": ".dockerignore",
    "chars": 61,
    "preview": ".examples\nDockerfile\n.github\n.idea\n.git\nweb/app\n*.db\ntestdata"
  },
  {
    "path": ".examples/docker-compose/compose.yaml",
    "chars": 125,
    "preview": "services:\n  gatus:\n    image: twinproduction/gatus:latest\n    ports:\n      - 8080:8080\n    volumes:\n      - ./config:/co"
  },
  {
    "path": ".examples/docker-compose-grafana-prometheus/README.md",
    "chars": 1156,
    "preview": "## Usage\nGatus exposes Prometheus metrics at `/metrics` if the `metrics` configuration option is set to `true`.\n\nTo run "
  },
  {
    "path": ".examples/docker-compose-grafana-prometheus/compose.yaml",
    "chars": 883,
    "preview": "services:\n  gatus:\n    container_name: gatus\n    image: twinproduction/gatus\n    restart: always\n    ports:\n      - \"808"
  },
  {
    "path": ".examples/docker-compose-grafana-prometheus/grafana/grafana.ini",
    "chars": 774,
    "preview": "[paths]\n\n[server]\n\n[database]\n\n[session]\n\n[dataproxy]\n\n[analytics]\nreporting_enabled = false\n\n[security]\n\n[snapshots]\n\n["
  },
  {
    "path": ".examples/docker-compose-grafana-prometheus/grafana/provisioning/dashboards/dashboard.yml",
    "chars": 199,
    "preview": "apiVersion: 1\n\nproviders:\n  - name: 'Prometheus'\n    orgId: 1\n    folder: ''\n    type: file\n    disableDeletion: false\n "
  },
  {
    "path": ".examples/docker-compose-grafana-prometheus/grafana/provisioning/dashboards/gatus.json",
    "chars": 24900,
    "preview": "{\n  \"annotations\": {\n    \"list\": [\n      {\n        \"builtIn\": 1,\n        \"datasource\": {\n          \"type\": \"grafana\",\n  "
  },
  {
    "path": ".examples/docker-compose-grafana-prometheus/grafana/provisioning/datasources/prometheus.yml",
    "chars": 175,
    "preview": "apiVersion: 1\n\ndatasources:\n  - name: Prometheus\n    type: prometheus\n    access: proxy\n    url: http://prometheus:9090\n"
  },
  {
    "path": ".examples/docker-compose-grafana-prometheus/prometheus/prometheus.yml",
    "chars": 121,
    "preview": "scrape_configs:\n  - job_name: gatus\n    scrape_interval: 10s\n    static_configs:\n      - targets:\n          - gatus:8080"
  },
  {
    "path": ".examples/docker-compose-mattermost/compose.yaml",
    "chars": 379,
    "preview": "services:\n  gatus:\n    container_name: gatus\n    image: twinproduction/gatus:latest\n    ports:\n      - \"8080:8080\"\n    v"
  },
  {
    "path": ".examples/docker-compose-mtls/certs/client/client.crt",
    "chars": 1801,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIFBjCCAu6gAwIBAgIUHJXHAqywj2v25AgX7pDSZ+LX4iAwDQYJKoZIhvcNAQEL\nBQAwEjEQMA4GA1UEAwwHZXhhbXB"
  },
  {
    "path": ".examples/docker-compose-mtls/certs/client/client.key",
    "chars": 3243,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIJKQIBAAKCAgEA1OZGVLkE0bzaY52uG1E8gEPlTjScRbgJUWbzlkngGB7hPLzq\nO59qgKcjoAR/ClWcmFc/ONS"
  },
  {
    "path": ".examples/docker-compose-mtls/certs/server/ca.crt",
    "chars": 1777,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIE9DCCAtygAwIBAgIUCXgA3IbeA2mn8DQ0E5IxaKBLtf8wDQYJKoZIhvcNAQEL\nBQAwEjEQMA4GA1UEAwwHZXhhbXB"
  },
  {
    "path": ".examples/docker-compose-mtls/certs/server/server.crt",
    "chars": 1814,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIFDjCCAvagAwIBAgITc5Ejz7RzBJ2/PcUMsVhj41RtQDANBgkqhkiG9w0BAQsF\nADASMRAwDgYDVQQDDAdleGFtcGx"
  },
  {
    "path": ".examples/docker-compose-mtls/certs/server/server.key",
    "chars": 3243,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIJKQIBAAKCAgEAoGywZ1awXUZARdl5iYA04kalhT0JXncwq5BxeHolL7RMrSYZ\nKaEb9vkokT7IwqN148HIECI"
  },
  {
    "path": ".examples/docker-compose-mtls/compose.yaml",
    "chars": 452,
    "preview": "services:\n  nginx:\n    image: nginx:stable\n    volumes:\n      - ./certs/server:/etc/nginx/certs\n      - ./nginx:/etc/ngi"
  },
  {
    "path": ".examples/docker-compose-mtls/nginx/default.conf",
    "chars": 413,
    "preview": "server {\n    listen                  443 ssl;\n\n    ssl_certificate         /etc/nginx/certs/server.crt;\n    ssl_certific"
  },
  {
    "path": ".examples/docker-compose-multiple-config-files/compose.yaml",
    "chars": 177,
    "preview": "services:\n  gatus:\n    image: twinproduction/gatus:latest\n    ports:\n      - \"8080:8080\"\n    environment:\n      - GATUS_"
  },
  {
    "path": ".examples/docker-compose-multiple-config-files/config/backend.yaml",
    "chars": 489,
    "preview": "endpoints:\n  - name: check-if-api-is-healthy\n    group: backend\n    url: \"https://twin.sh/health\"\n    interval: 5m\n    c"
  },
  {
    "path": ".examples/docker-compose-multiple-config-files/config/frontend.yaml",
    "chars": 245,
    "preview": "endpoints:\n  - name: make-sure-html-rendering-works\n    group: frontend\n    url: \"https://example.org\"\n    interval: 5m\n"
  },
  {
    "path": ".examples/docker-compose-multiple-config-files/config/global.yaml",
    "chars": 136,
    "preview": "metrics: true\nui:\n  header: Example Company\n  link: https://example.org\n  buttons:\n    - name: \"Home\"\n      link: \"https"
  },
  {
    "path": ".examples/docker-compose-postgres-storage/compose.yaml",
    "chars": 588,
    "preview": "services:\n  postgres:\n    image: postgres\n    volumes:\n      - ./data/db:/var/lib/postgresql/data\n    ports:\n      - \"54"
  },
  {
    "path": ".examples/docker-compose-sqlite-storage/compose.yaml",
    "chars": 149,
    "preview": "services:\n  gatus:\n    image: twinproduction/gatus:latest\n    ports:\n      - \"8080:8080\"\n    volumes:\n      - ./config:/"
  },
  {
    "path": ".examples/docker-compose-sqlite-storage/data/.gitkeep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": ".examples/docker-minimal/Dockerfile",
    "chars": 62,
    "preview": "FROM twinproduction/gatus\nADD config.yaml ./config/config.yaml"
  },
  {
    "path": ".examples/kubernetes/gatus.yaml",
    "chars": 2532,
    "preview": "apiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: gatus\n  namespace: kube-system\ndata:\n  config.yaml: |\n    metrics: true"
  },
  {
    "path": ".examples/nixos/README.md",
    "chars": 97,
    "preview": "# NixOS\n\nGatus is implemented as a NixOS module. See [gatus.nix](./gatus.nix) for example\nusage.\n"
  },
  {
    "path": ".examples/nixos/gatus.nix",
    "chars": 383,
    "preview": "{\n  services.gatus = {\n    enable = true;\n\n    settings = {\n      web.port = 8080;\n\n      endpoints = [\n        {\n      "
  },
  {
    "path": ".gitattributes",
    "chars": 18,
    "preview": "* text=auto eol=lf"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 15,
    "preview": "github: [TwiN]\n"
  },
  {
    "path": ".github/assets/gatus-diagram.drawio",
    "chars": 2405,
    "preview": "<mxfile host=\"app.diagrams.net\" modified=\"2022-12-07T04:00:31.242Z\" agent=\"5.0 (Windows)\" etag=\"4-CttOJPoGYGt_6RMEMf\" ve"
  },
  {
    "path": ".github/codecov.yml",
    "chars": 316,
    "preview": "ignore:\n  - \"storage/store/sql/specific_postgres.go\" # Can't test for postgres\n  - \"watchdog/endpoint.go\"\n  - \"watchdog/"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 295,
    "preview": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    labels: [\"dependencies\"]\n    schedule"
  },
  {
    "path": ".github/workflows/benchmark.yml",
    "chars": 866,
    "preview": "name: benchmark\non:\n  workflow_run:\n    workflows: [publish-latest]\n    branches: [master]\n    types: [completed]\n  work"
  },
  {
    "path": ".github/workflows/labeler.yml",
    "chars": 1719,
    "preview": "name: labeler\non:\n  pull_request_target:\n    types:\n      - opened\n  issues:\n    types:\n      - opened\njobs:\n  labeler:\n"
  },
  {
    "path": ".github/workflows/publish-custom.yml",
    "chars": 1536,
    "preview": "name: publish-custom\nrun-name: \"${{ inputs.tag }}\"\non:\n  workflow_dispatch:\n    inputs:\n      tag:\n        description: "
  },
  {
    "path": ".github/workflows/publish-experimental.yml",
    "chars": 1177,
    "preview": "name: publish-experimental\non: [workflow_dispatch]\njobs:\n  publish-experimental:\n    runs-on: ubuntu-latest\n    timeout-"
  },
  {
    "path": ".github/workflows/publish-latest.yml",
    "chars": 2026,
    "preview": "name: publish-latest\non:\n  workflow_run:\n    workflows: [test]\n    branches: [master]\n    types: [completed]\nconcurrency"
  },
  {
    "path": ".github/workflows/publish-release.yml",
    "chars": 1860,
    "preview": "name: publish-release\non:\n  release:\n    types: [published]\njobs:\n  publish-release:\n    name: publish-release\n    runs-"
  },
  {
    "path": ".github/workflows/regenerate-static-assets.yml",
    "chars": 3953,
    "preview": "name: regenerate-static-assets\non:\n  issue_comment:\n    types: [created]\n\njobs:\n  check-command:\n    runs-on: ubuntu-lat"
  },
  {
    "path": ".github/workflows/test-ui.yml",
    "chars": 320,
    "preview": "name: test-ui\non:\n  pull_request:\n    paths:\n      - 'web/**'\n  push:\n    branches:\n      - master\n    paths:\n      - 'w"
  },
  {
    "path": ".github/workflows/test.yml",
    "chars": 1061,
    "preview": "name: test\non:\n  pull_request:\n    paths-ignore:\n      - '*.md'\n      - '.examples/**'\n  push:\n    branches:\n      - mas"
  },
  {
    "path": ".gitignore",
    "chars": 141,
    "preview": "# IDE\n*.iml\n.idea\n.vscode\n\n# OS\n.DS_Store\n\n# JS\nnode_modules\n\n# Go\n/vendor\n\n# Misc\n*.db\n*.db-shm\n*.db-wal\ngatus\nconfig/c"
  },
  {
    "path": "Dockerfile",
    "chars": 744,
    "preview": "# Build the go application into a binary\nFROM golang:alpine AS builder\nRUN apk --update add ca-certificates\nWORKDIR /app"
  },
  {
    "path": "LICENSE",
    "chars": 11358,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "Makefile",
    "chars": 745,
    "preview": "BINARY=gatus\n\n.PHONY: install\ninstall:\n\tgo build -v -o $(BINARY) .\n\n.PHONY: run\nrun:\n\tENVIRONMENT=dev GATUS_CONFIG_PATH="
  },
  {
    "path": "README.md",
    "chars": 211678,
    "preview": "[![Gatus](.github/assets/logo-with-dark-text.png)](https://gatus.io)\n\n![test](https://github.com/TwiN/gatus/actions/work"
  },
  {
    "path": "alerting/alert/alert.go",
    "chars": 5150,
    "preview": "package alert\n\nimport (\n\t\"crypto/sha256\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/TwiN/logr"
  },
  {
    "path": "alerting/alert/alert_test.go",
    "chars": 7474,
    "preview": "package alert\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestAlert_ValidateAndSetDefaults(t *testing.T) {\n\tinvalidDe"
  },
  {
    "path": "alerting/alert/type.go",
    "chars": 3948,
    "preview": "package alert\n\n// Type is the type of the alert.\n// The value will generally be the name of the alert provider\ntype Type"
  },
  {
    "path": "alerting/config.go",
    "chars": 8898,
    "preview": "package alerting\n\nimport (\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"github.com/TwiN/gatus/v5/"
  },
  {
    "path": "alerting/provider/awsses/awsses.go",
    "chars": 6898,
    "preview": "package awsses\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"github.com"
  },
  {
    "path": "alerting/provider/awsses/awsses_test.go",
    "chars": 8231,
    "preview": "package awsses\n\nimport (\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"github.com/TwiN/gatus/v5/config/endpoi"
  },
  {
    "path": "alerting/provider/clickup/clickup.go",
    "chars": 8314,
    "preview": "package clickup\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/TwiN/gatus/v5/"
  },
  {
    "path": "alerting/provider/clickup/clickup_test.go",
    "chars": 12030,
    "preview": "package clickup\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/al"
  },
  {
    "path": "alerting/provider/custom/custom.go",
    "chars": 6737,
    "preview": "package custom\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/ale"
  },
  {
    "path": "alerting/provider/custom/custom_test.go",
    "chars": 17273,
    "preview": "package custom\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"github.com/Tw"
  },
  {
    "path": "alerting/provider/datadog/datadog.go",
    "chars": 6338,
    "preview": "package datadog\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/TwiN/gatus/"
  },
  {
    "path": "alerting/provider/datadog/datadog_test.go",
    "chars": 6396,
    "preview": "package datadog\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\""
  },
  {
    "path": "alerting/provider/discord/discord.go",
    "chars": 6058,
    "preview": "package discord\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/TwiN/gatus/v5/alert"
  },
  {
    "path": "alerting/provider/discord/discord_test.go",
    "chars": 16565,
    "preview": "package discord\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"github.c"
  },
  {
    "path": "alerting/provider/email/email.go",
    "chars": 6668,
    "preview": "package email\n\nimport (\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"strings\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\""
  },
  {
    "path": "alerting/provider/email/email_test.go",
    "chars": 10449,
    "preview": "package email\n\nimport (\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"github.com/TwiN/gatus/v5/config/endpoin"
  },
  {
    "path": "alerting/provider/gitea/gitea.go",
    "chars": 6638,
    "preview": "package gitea\n\nimport (\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\n\t\"code.gitea.io/sdk/gitea\"\n\t\"g"
  },
  {
    "path": "alerting/provider/gitea/gitea_test.go",
    "chars": 8780,
    "preview": "package gitea\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"code.gitea.io/sdk/gitea\"\n\t\"github.com/TwiN/gatus/v5/alertin"
  },
  {
    "path": "alerting/provider/github/github.go",
    "chars": 6511,
    "preview": "package github\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/"
  },
  {
    "path": "alerting/provider/github/github_test.go",
    "chars": 8200,
    "preview": "package github\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"github.com/TwiN"
  },
  {
    "path": "alerting/provider/gitlab/gitlab.go",
    "chars": 8118,
    "preview": "package gitlab\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/TwiN/gatus/"
  },
  {
    "path": "alerting/provider/gitlab/gitlab_test.go",
    "chars": 9429,
    "preview": "package gitlab\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"github.com/TwiN"
  },
  {
    "path": "alerting/provider/googlechat/googlechat.go",
    "chars": 7120,
    "preview": "package googlechat\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/TwiN/gatus/v5/al"
  },
  {
    "path": "alerting/provider/googlechat/googlechat_test.go",
    "chars": 12103,
    "preview": "package googlechat\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"githu"
  },
  {
    "path": "alerting/provider/gotify/gotify.go",
    "chars": 4846,
    "preview": "package gotify\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/TwiN/gatus/v5/alerti"
  },
  {
    "path": "alerting/provider/gotify/gotify_test.go",
    "chars": 6307,
    "preview": "package gotify\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"github.com/Twi"
  },
  {
    "path": "alerting/provider/homeassistant/homeassistant.go",
    "chars": 5761,
    "preview": "package homeassistant\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/TwiN/gatus/v5"
  },
  {
    "path": "alerting/provider/homeassistant/homeassistant_test.go",
    "chars": 5523,
    "preview": "package homeassistant\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"gi"
  },
  {
    "path": "alerting/provider/ifttt/ifttt.go",
    "chars": 5678,
    "preview": "package ifttt\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/TwiN/gatus/v5/alertin"
  },
  {
    "path": "alerting/provider/ifttt/ifttt_test.go",
    "chars": 5365,
    "preview": "package ifttt\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t"
  },
  {
    "path": "alerting/provider/ilert/ilert.go",
    "chars": 4749,
    "preview": "package ilert\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/TwiN/gatus/v5/alertin"
  },
  {
    "path": "alerting/provider/ilert/ilert_test.go",
    "chars": 11718,
    "preview": "package ilert\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/aler"
  },
  {
    "path": "alerting/provider/incidentio/dedup.go",
    "chars": 511,
    "preview": "package incidentio\n\nimport (\n\t\"crypto/sha256\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\""
  },
  {
    "path": "alerting/provider/incidentio/incidentio.go",
    "chars": 7211,
    "preview": "package incidentio\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"maps\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n"
  },
  {
    "path": "alerting/provider/incidentio/incidentio_test.go",
    "chars": 16399,
    "preview": "package incidentio\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting"
  },
  {
    "path": "alerting/provider/line/line.go",
    "chars": 5751,
    "preview": "package line\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/TwiN/gatus/v5/alerting"
  },
  {
    "path": "alerting/provider/line/line_test.go",
    "chars": 4939,
    "preview": "package line\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"github.com/"
  },
  {
    "path": "alerting/provider/matrix/matrix.go",
    "chars": 8166,
    "preview": "package matrix\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"math/rand\"\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github."
  },
  {
    "path": "alerting/provider/matrix/matrix_test.go",
    "chars": 13155,
    "preview": "package matrix\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"github.co"
  },
  {
    "path": "alerting/provider/mattermost/mattermost.go",
    "chars": 6411,
    "preview": "package mattermost\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/TwiN/gatus/v5/al"
  },
  {
    "path": "alerting/provider/mattermost/mattermost_test.go",
    "chars": 10398,
    "preview": "package mattermost\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"githu"
  },
  {
    "path": "alerting/provider/messagebird/messagebird.go",
    "chars": 4244,
    "preview": "package messagebird\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/TwiN/gatus/v5/a"
  },
  {
    "path": "alerting/provider/messagebird/messagebird_test.go",
    "chars": 7485,
    "preview": "package messagebird\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"gith"
  },
  {
    "path": "alerting/provider/n8n/n8n.go",
    "chars": 5659,
    "preview": "package n8n\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/"
  },
  {
    "path": "alerting/provider/n8n/n8n_test.go",
    "chars": 12593,
    "preview": "package n8n\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"github.com/T"
  },
  {
    "path": "alerting/provider/newrelic/newrelic.go",
    "chars": 6613,
    "preview": "package newrelic\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/TwiN/gatus"
  },
  {
    "path": "alerting/provider/newrelic/newrelic_test.go",
    "chars": 6716,
    "preview": "package newrelic\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert"
  },
  {
    "path": "alerting/provider/ntfy/ntfy.go",
    "chars": 7207,
    "preview": "package ntfy\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/"
  },
  {
    "path": "alerting/provider/ntfy/ntfy_test.go",
    "chars": 18876,
    "preview": "package ntfy\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/al"
  },
  {
    "path": "alerting/provider/opsgenie/opsgenie.go",
    "chars": 8239,
    "preview": "package opsgenie\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github."
  },
  {
    "path": "alerting/provider/opsgenie/opsgenie_test.go",
    "chars": 11483,
    "preview": "package opsgenie\n\nimport (\n\t\"net/http\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"github.com/Tw"
  },
  {
    "path": "alerting/provider/pagerduty/pagerduty.go",
    "chars": 5919,
    "preview": "package pagerduty\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/TwiN/gatus/v5/ale"
  },
  {
    "path": "alerting/provider/pagerduty/pagerduty_test.go",
    "chars": 9530,
    "preview": "package pagerduty\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"github"
  },
  {
    "path": "alerting/provider/plivo/plivo.go",
    "chars": 5381,
    "preview": "package plivo\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/Tw"
  },
  {
    "path": "alerting/provider/plivo/plivo_test.go",
    "chars": 16666,
    "preview": "package plivo\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"gith"
  },
  {
    "path": "alerting/provider/provider.go",
    "chars": 8291,
    "preview": "package provider\n\nimport (\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"github.com/TwiN/gatus/v5/alerting/provider/awsse"
  },
  {
    "path": "alerting/provider/provider_test.go",
    "chars": 5342,
    "preview": "package provider\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n)\n\nfunc TestParseWithDefaultAl"
  },
  {
    "path": "alerting/provider/pushover/pushover.go",
    "chars": 6971,
    "preview": "package pushover\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/TwiN/gatus/v5/aler"
  },
  {
    "path": "alerting/provider/pushover/pushover_test.go",
    "chars": 13321,
    "preview": "package pushover\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"github."
  },
  {
    "path": "alerting/provider/rocketchat/rocketchat.go",
    "chars": 6260,
    "preview": "package rocketchat\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/TwiN/gatus/v5/al"
  },
  {
    "path": "alerting/provider/rocketchat/rocketchat_test.go",
    "chars": 5678,
    "preview": "package rocketchat\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/ale"
  },
  {
    "path": "alerting/provider/sendgrid/sendgrid.go",
    "chars": 7237,
    "preview": "package sendgrid\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/TwiN/ga"
  },
  {
    "path": "alerting/provider/sendgrid/sendgrid_test.go",
    "chars": 19180,
    "preview": "package sendgrid\n\nimport (\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"github."
  },
  {
    "path": "alerting/provider/signal/signal.go",
    "chars": 5872,
    "preview": "package signal\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/TwiN/gatu"
  },
  {
    "path": "alerting/provider/signal/signal_test.go",
    "chars": 5255,
    "preview": "package signal\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n"
  },
  {
    "path": "alerting/provider/signl4/signl4.go",
    "chars": 5610,
    "preview": "package signl4\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/TwiN/gatus/v5/alerti"
  },
  {
    "path": "alerting/provider/signl4/signl4_test.go",
    "chars": 14181,
    "preview": "package signl4\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"github.co"
  },
  {
    "path": "alerting/provider/slack/slack.go",
    "chars": 5888,
    "preview": "package slack\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/TwiN/gatus/v5/alertin"
  },
  {
    "path": "alerting/provider/slack/slack_test.go",
    "chars": 13280,
    "preview": "package slack\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"github.com"
  },
  {
    "path": "alerting/provider/splunk/splunk.go",
    "chars": 6640,
    "preview": "package splunk\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/TwiN/gatus/v"
  },
  {
    "path": "alerting/provider/splunk/splunk_test.go",
    "chars": 5318,
    "preview": "package splunk\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"github.co"
  },
  {
    "path": "alerting/provider/squadcast/squadcast.go",
    "chars": 5649,
    "preview": "package squadcast\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/TwiN/gatus/v5/ale"
  },
  {
    "path": "alerting/provider/squadcast/squadcast_test.go",
    "chars": 4706,
    "preview": "package squadcast\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/aler"
  },
  {
    "path": "alerting/provider/teams/teams.go",
    "chars": 6003,
    "preview": "package teams\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/TwiN/gatus/v5/alertin"
  },
  {
    "path": "alerting/provider/teams/teams_test.go",
    "chars": 10642,
    "preview": "package teams\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"github.com"
  },
  {
    "path": "alerting/provider/teamsworkflows/teamsworkflows.go",
    "chars": 7365,
    "preview": "package teamsworkflows\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/TwiN/gatus/v"
  },
  {
    "path": "alerting/provider/teamsworkflows/teamsworkflows_test.go",
    "chars": 11945,
    "preview": "package teamsworkflows\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"g"
  },
  {
    "path": "alerting/provider/telegram/telegram.go",
    "chars": 5962,
    "preview": "package telegram\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/TwiN/gatus/v5/aler"
  },
  {
    "path": "alerting/provider/telegram/telegram_test.go",
    "chars": 11946,
    "preview": "package telegram\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"github."
  },
  {
    "path": "alerting/provider/twilio/twilio.go",
    "chars": 5629,
    "preview": "package twilio\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\n\t\"github."
  },
  {
    "path": "alerting/provider/twilio/twilio_test.go",
    "chars": 8506,
    "preview": "package twilio\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"github.com/TwiN/gatus/v5/c"
  },
  {
    "path": "alerting/provider/vonage/vonage.go",
    "chars": 6154,
    "preview": "package vonage\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\n\t\"github.com/TwiN/ga"
  },
  {
    "path": "alerting/provider/vonage/vonage_test.go",
    "chars": 15397,
    "preview": "package vonage\n\nimport (\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"github.co"
  },
  {
    "path": "alerting/provider/webex/webex.go",
    "chars": 5078,
    "preview": "package webex\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/TwiN/gatus/v5/alertin"
  },
  {
    "path": "alerting/provider/webex/webex_test.go",
    "chars": 4403,
    "preview": "package webex\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t"
  },
  {
    "path": "alerting/provider/zapier/zapier.go",
    "chars": 6224,
    "preview": "package zapier\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/TwiN/gatus/v"
  },
  {
    "path": "alerting/provider/zapier/zapier_test.go",
    "chars": 5669,
    "preview": "package zapier\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n"
  },
  {
    "path": "alerting/provider/zulip/zulip.go",
    "chars": 5763,
    "preview": "package zulip\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/aler"
  },
  {
    "path": "alerting/provider/zulip/zulip_test.go",
    "chars": 13062,
    "preview": "package zulip\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t"
  },
  {
    "path": "api/api.go",
    "chars": 5091,
    "preview": "package api\n\nimport (\n\t\"io/fs\"\n\t\"net/http\"\n\t\"os\"\n\n\t\"github.com/TwiN/gatus/v5/config\"\n\t\"github.com/TwiN/gatus/v5/config/u"
  },
  {
    "path": "api/api_test.go",
    "chars": 3263,
    "preview": "package api\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/config\"\n\t\"github.com/TwiN/"
  },
  {
    "path": "api/badge.go",
    "chars": 12145,
    "preview": "package api\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/TwiN/gatu"
  },
  {
    "path": "api/badge_test.go",
    "chars": 10992,
    "preview": "package api\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strconv\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/TwiN/gatus/v5/config\""
  },
  {
    "path": "api/cache.go",
    "chars": 201,
    "preview": "package api\n\nimport (\n\t\"time\"\n\n\t\"github.com/TwiN/gocache/v2\"\n)\n\nconst (\n\tcacheTTL = 10 * time.Second\n)\n\nvar (\n\tcache = g"
  },
  {
    "path": "api/chart.go",
    "chars": 5874,
    "preview": "package api\n\nimport (\n\t\"errors\"\n\t\"math\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"sort\"\n\t\"time\"\n\n\t\"github.com/TwiN/gatus/v5/storage/store"
  },
  {
    "path": "api/chart_test.go",
    "chars": 4356,
    "preview": "package api\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/TwiN/gatus/v5/config\"\n\t\"github.c"
  },
  {
    "path": "api/config.go",
    "chars": 1271,
    "preview": "package api\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\n\t\"github.com/TwiN/gatus/v5/config\"\n\t\"github.com/TwiN/gatus/v5/security\"\n\t"
  },
  {
    "path": "api/config_test.go",
    "chars": 1444,
    "preview": "package api\n\nimport (\n\t\"io\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/security\"\n\t\"github.com/gofiber/fiber/v2\"\n"
  },
  {
    "path": "api/custom_css.go",
    "chars": 264,
    "preview": "package api\n\nimport (\n\t\"github.com/gofiber/fiber/v2\"\n)\n\ntype CustomCSSHandler struct {\n\tcustomCSS string\n}\n\nfunc (handle"
  },
  {
    "path": "api/endpoint_status.go",
    "chars": 5001,
    "preview": "package api\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/url\"\n\n\t\"github.com/TwiN/gatus/v5/client\"\n\t\"github.com/TwiN"
  },
  {
    "path": "api/endpoint_status_test.go",
    "chars": 8894,
    "preview": "package api\n\nimport (\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/TwiN/gatus/v5/config\"\n\t\"gi"
  },
  {
    "path": "api/external_endpoint.go",
    "chars": 3840,
    "preview": "package api\n\nimport (\n\t\"errors\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/TwiN/gatus/v5/config\"\n\t\"github.com/TwiN/gatus/v5/config"
  },
  {
    "path": "api/external_endpoint_test.go",
    "chars": 6388,
    "preview": "package api\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting\"\n\t\"github.com/Twi"
  },
  {
    "path": "api/raw.go",
    "chars": 2508,
    "preview": "package api\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/TwiN/gatus/v5/storage/store\"\n\t\"github.com/TwiN/g"
  },
  {
    "path": "api/raw_test.go",
    "chars": 3496,
    "preview": "package api\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/TwiN/gatus/v5/config\"\n\t\"github.c"
  },
  {
    "path": "api/spa.go",
    "chars": 1619,
    "preview": "package api\n\nimport (\n\t_ \"embed\"\n\t\"html/template\"\n\n\t\"github.com/TwiN/gatus/v5/config/ui\"\n\tstatic \"github.com/TwiN/gatus/"
  },
  {
    "path": "api/spa_test.go",
    "chars": 3039,
    "preview": "package api\n\nimport (\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/TwiN/gatus/v5/c"
  },
  {
    "path": "api/suite_status.go",
    "chars": 1807,
    "preview": "package api\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/TwiN/gatus/v5/config\"\n\t\"github.com/TwiN/gatus/v5/config/suite\"\n\t\"github.com/T"
  },
  {
    "path": "api/suite_status_test.go",
    "chars": 18720,
    "preview": "package api\n\nimport (\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/TwiN/gatus/v5/config\"\n\t\"gi"
  },
  {
    "path": "api/util.go",
    "chars": 1184,
    "preview": "package api\n\nimport (\n\t\"strconv\"\n\n\t\"github.com/gofiber/fiber/v2\"\n)\n\nconst (\n\t// DefaultPage is the default page to use i"
  },
  {
    "path": "api/util_test.go",
    "chars": 2381,
    "preview": "package api\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/storage\"\n\t\"github.com/gofiber/fiber/v2\"\n\t\"github.com"
  },
  {
    "path": "client/client.go",
    "chars": 17231,
    "preview": "package client\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"f"
  },
  {
    "path": "client/client_test.go",
    "chars": 17504,
    "preview": "package client\n\nimport (\n\t\"bytes\"\n\t\"crypto/tls\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/netip\"\n\t\"os\"\n\t\"runtime\"\n\t\"testing\"\n\t\"time\"\n\n\t\"gi"
  },
  {
    "path": "client/config.go",
    "chars": 12075,
    "preview": "package client\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"time\"\n\n"
  },
  {
    "path": "client/config_test.go",
    "chars": 5238,
    "preview": "package client\n\nimport (\n\t\"net/http\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestConfig_getHTTPClient(t *testing.T) {\n\tins"
  },
  {
    "path": "client/grpc.go",
    "chars": 2385,
    "preview": "package client\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/TwiN/logr\"\n\t\"google.golang.org/grpc\"\n\t\"go"
  },
  {
    "path": "config/announcement/announcement.go",
    "chars": 2871,
    "preview": "package announcement\n\nimport (\n\t\"errors\"\n\t\"sort\"\n\t\"time\"\n)\n\nconst (\n\t// TypeOutage represents a service outage\n\tTypeOuta"
  },
  {
    "path": "config/announcement/announcement_test.go",
    "chars": 7064,
    "preview": "package announcement\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestAnnouncement_ValidateAndSetDefaults(t *testing.T"
  },
  {
    "path": "config/config.go",
    "chars": 25331,
    "preview": "package config\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"slices\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github"
  },
  {
    "path": "config/config_test.go",
    "chars": 89003,
    "preview": "package config\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"slices\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/TwiN/gatus/v"
  },
  {
    "path": "config/connectivity/connectivity.go",
    "chars": 1351,
    "preview": "package connectivity\n\nimport (\n\t\"errors\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/TwiN/gatus/v5/client\"\n)\n\nvar (\n\tErrInvalidInte"
  },
  {
    "path": "config/connectivity/connectivity_test.go",
    "chars": 1934,
    "preview": "package connectivity\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestConfig(t *testing.T) {\n\tscenarios := []struct {\n\t\tn"
  },
  {
    "path": "config/endpoint/common.go",
    "chars": 1031,
    "preview": "package endpoint\n\nimport (\n\t\"errors\"\n\t\"strings\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n)\n\nvar (\n\t// ErrEndpointWith"
  },
  {
    "path": "config/endpoint/common_test.go",
    "chars": 1147,
    "preview": "package endpoint\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n)\n\nfunc TestValidateEndpoint"
  },
  {
    "path": "config/endpoint/condition.go",
    "chars": 12302,
    "preview": "package endpoint\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/TwiN/gatus/v5/config/gontext\"\n\t\""
  },
  {
    "path": "config/endpoint/condition_bench_test.go",
    "chars": 2534,
    "preview": "package endpoint\n\nimport (\n\t\"testing\"\n)\n\nfunc BenchmarkCondition_evaluateWithBodyStringAny(b *testing.B) {\n\tcondition :="
  },
  {
    "path": "config/endpoint/condition_result.go",
    "chars": 264,
    "preview": "package endpoint\n\n// ConditionResult result of a Condition\ntype ConditionResult struct {\n\t// Condition that was evaluate"
  },
  {
    "path": "config/endpoint/condition_test.go",
    "chars": 33993,
    "preview": "package endpoint\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/TwiN/gatus/v5/config/gontext\"\n)\n"
  },
  {
    "path": "config/endpoint/dns/dns.go",
    "chars": 1015,
    "preview": "package dns\n\nimport (\n\t\"errors\"\n\t\"strings\"\n\n\t\"github.com/miekg/dns\"\n)\n\nvar (\n\t// ErrDNSWithNoQueryName is the error with"
  },
  {
    "path": "config/endpoint/dns/dns_test.go",
    "chars": 675,
    "preview": "package dns\n\nimport (\n\t\"testing\"\n)\n\nfunc TestConfig_ValidateAndSetDefault(t *testing.T) {\n\tdns := &Config{\n\t\tQueryType: "
  },
  {
    "path": "config/endpoint/endpoint.go",
    "chars": 21803,
    "preview": "package endpoint\n\nimport (\n\t\"bytes\"\n\t\"crypto/x509\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"maps\"\n\t\"math/rand\"\n\t\"net\"\n\t"
  },
  {
    "path": "config/endpoint/endpoint_test.go",
    "chars": 56775,
    "preview": "package endpoint\n\nimport (\n\t\"bytes\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"errors\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\t\"testing\"\n\t\"tim"
  },
  {
    "path": "config/endpoint/event.go",
    "chars": 1000,
    "preview": "package endpoint\n\nimport (\n\t\"time\"\n)\n\n// Event is something that happens at a specific time\ntype Event struct {\n\t// Type"
  },
  {
    "path": "config/endpoint/event_test.go",
    "chars": 374,
    "preview": "package endpoint\n\nimport (\n\t\"testing\"\n)\n\nfunc TestNewEventFromResult(t *testing.T) {\n\tif event := NewEventFromResult(&Re"
  },
  {
    "path": "config/endpoint/external_endpoint.go",
    "chars": 4014,
    "preview": "package endpoint\n\nimport (\n\t\"errors\"\n\t\"time\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"github.com/TwiN/gatus/v5/conf"
  },
  {
    "path": "config/endpoint/external_endpoint_test.go",
    "chars": 8973,
    "preview": "package endpoint\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/TwiN/gatus/v5/alerting/alert\"\n\t\"github.com/TwiN/gatus/v5/con"
  },
  {
    "path": "config/endpoint/heartbeat/heartbeat.go",
    "chars": 541,
    "preview": "package heartbeat\n\nimport \"time\"\n\n// Config used to check if the external endpoint has received new results when it shou"
  },
  {
    "path": "config/endpoint/placeholder.go",
    "chars": 9996,
    "preview": "package endpoint\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/TwiN/gatus/v5/config/gontext\"\n\t\"github.com/TwiN/ga"
  },
  {
    "path": "config/endpoint/placeholder_test.go",
    "chars": 3865,
    "preview": "package endpoint\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/TwiN/gatus/v5/config/gontext\"\n)\n\nfunc TestResolvePlaceholder"
  },
  {
    "path": "config/endpoint/result.go",
    "chars": 2344,
    "preview": "package endpoint\n\nimport (\n\t\"slices\"\n\t\"time\"\n)\n\n// Result of the evaluation of an Endpoint\ntype Result struct {\n\t// HTTP"
  },
  {
    "path": "config/endpoint/result_test.go",
    "chars": 433,
    "preview": "package endpoint\n\nimport (\n\t\"testing\"\n)\n\nfunc TestResult_AddError(t *testing.T) {\n\tresult := &Result{}\n\tresult.AddError("
  },
  {
    "path": "config/endpoint/ssh/ssh.go",
    "chars": 1366,
    "preview": "package ssh\n\nimport (\n\t\"errors\"\n)\n\nvar (\n\t// ErrEndpointWithoutSSHUsername is the error with which Gatus will panic if a"
  },
  {
    "path": "config/endpoint/ssh/ssh_test.go",
    "chars": 1108,
    "preview": "package ssh\n\nimport (\n\t\"errors\"\n\t\"testing\"\n)\n\nfunc TestSSH_validatePasswordCfg(t *testing.T) {\n\tcfg := &Config{}\n\tif err"
  },
  {
    "path": "config/endpoint/status.go",
    "chars": 1054,
    "preview": "package endpoint\n\nimport \"github.com/TwiN/gatus/v5/config/key\"\n\n// Status contains the evaluation Results of an Endpoint"
  },
  {
    "path": "config/endpoint/status_test.go",
    "chars": 449,
    "preview": "package endpoint\n\nimport (\n\t\"testing\"\n)\n\nfunc TestNewEndpointStatus(t *testing.T) {\n\tep := &Endpoint{Name: \"name\", Group"
  },
  {
    "path": "config/endpoint/ui/ui.go",
    "chars": 2412,
    "preview": "package ui\n\nimport \"errors\"\n\n// Config is the UI configuration for endpoint.Endpoint\ntype Config struct {\n\t// HideCondit"
  },
  {
    "path": "config/endpoint/ui/ui_test.go",
    "chars": 1136,
    "preview": "package ui\n\nimport (\n\t\"errors\"\n\t\"testing\"\n)\n\nfunc TestValidateAndSetDefaults(t *testing.T) {\n\ttests := []struct {\n\t\tname"
  },
  {
    "path": "config/endpoint/uptime.go",
    "chars": 909,
    "preview": "package endpoint\n\n// Uptime is the struct that contains the relevant data for calculating the uptime as well as the upti"
  },
  {
    "path": "config/gontext/gontext.go",
    "chars": 2920,
    "preview": "package gontext\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\t\"sync\"\n)\n\nvar (\n\t// ErrGontextPathNotFound is returned when a gon"
  },
  {
    "path": "config/gontext/gontext_test.go",
    "chars": 9321,
    "preview": "package gontext\n\nimport (\n\t\"errors\"\n\t\"testing\"\n)\n\nfunc TestNew(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\t"
  },
  {
    "path": "config/key/key.go",
    "chars": 597,
    "preview": "package key\n\nimport \"strings\"\n\n// ConvertGroupAndNameToKey converts a group and a name to a key\nfunc ConvertGroupAndName"
  },
  {
    "path": "config/key/key_bench_test.go",
    "chars": 167,
    "preview": "package key\n\nimport (\n\t\"testing\"\n)\n\nfunc BenchmarkConvertGroupAndNameToKey(b *testing.B) {\n\tfor n := 0; n < b.N; n++ {\n\t"
  },
  {
    "path": "config/key/key_test.go",
    "chars": 1261,
    "preview": "package key\n\nimport \"testing\"\n\nfunc TestConvertGroupAndNameToKey(t *testing.T) {\n\ttype Scenario struct {\n\t\tGroupName    "
  }
]

// ... and 125 more files (download for full content)

About this extraction

This page contains the full source code of the TwiN/gatus GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 325 files (2.6 MB), approximately 690.7k tokens, and a symbol index with 3373 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!