Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d626f2934 | ||
|
|
75c1b290f6 | ||
|
|
fe7b74f555 | ||
|
|
ed4c270a25 |
10
README.md
10
README.md
@@ -110,7 +110,6 @@ Have any feedback or questions? [Create a discussion](https://github.com/TwiN/ga
|
||||
- [Helm Chart](#helm-chart)
|
||||
- [Terraform](#terraform)
|
||||
- [Kubernetes](#kubernetes)
|
||||
- [ECS Fargate](#ecs-fargate)
|
||||
- [Running the tests](#running-the-tests)
|
||||
- [Using in Production](#using-in-production)
|
||||
- [FAQ](#faq)
|
||||
@@ -264,6 +263,8 @@ If you want to test it locally, see [Docker](#docker).
|
||||
| `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. | `""` |
|
||||
@@ -2809,10 +2810,6 @@ To get more details, please check [chart's configuration](https://github.com/Twi
|
||||
|
||||
Gatus can be deployed on Kubernetes using Terraform by using the following module: [terraform-kubernetes-gatus](https://github.com/TwiN/terraform-kubernetes-gatus).
|
||||
|
||||
#### ECS Fargate
|
||||
|
||||
Gatus can be deployed on ECS Fargate using Terraform by using the following module: [terraform-aws-gatus-ecs](https://github.com/GiamPy5/terraform-aws-gatus-ecs).
|
||||
|
||||
## Running the tests
|
||||
```console
|
||||
go test -v ./...
|
||||
@@ -3023,6 +3020,9 @@ You can specify a domain prefixed by `icmp://`, or an IP address prefixed by `ic
|
||||
If you run Gatus on Linux, please read the Linux section on [https://github.com/prometheus-community/pro-bing#linux]
|
||||
if you encounter any problems.
|
||||
|
||||
Prior to `v5.31.0`, some environment setups required adding `CAP_NET_RAW` capabilities to allow pings to work.
|
||||
As of `v5.31.0`, this is no longer necessary, and ICMP checks will work with unprivileged pings unless running as root. See #1346 for details.
|
||||
|
||||
|
||||
### Monitoring an endpoint using DNS queries
|
||||
Defining a `dns` configuration in an endpoint will automatically mark said endpoint as an endpoint of type DNS:
|
||||
|
||||
@@ -10,14 +10,16 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
defaultTitle = "Health Dashboard | Gatus"
|
||||
defaultDescription = "Gatus is an advanced automated status page that lets you monitor your applications and configure alerts to notify you if there's an issue"
|
||||
defaultHeader = "Gatus"
|
||||
defaultLogo = ""
|
||||
defaultLink = ""
|
||||
defaultCustomCSS = ""
|
||||
defaultSortBy = "name"
|
||||
defaultFilterBy = "none"
|
||||
defaultTitle = "Health Dashboard | Gatus"
|
||||
defaultDescription = "Gatus is an advanced automated status page that lets you monitor your applications and configure alerts to notify you if there's an issue"
|
||||
defaultHeader = "Gatus"
|
||||
defaultDashboardHeading = "Health Dashboard"
|
||||
defaultDashboardSubheading = "Monitor the health of your endpoints in real-time"
|
||||
defaultLogo = ""
|
||||
defaultLink = ""
|
||||
defaultCustomCSS = ""
|
||||
defaultSortBy = "name"
|
||||
defaultFilterBy = "none"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -30,17 +32,18 @@ var (
|
||||
|
||||
// Config is the configuration for the UI of Gatus
|
||||
type Config struct {
|
||||
Title string `yaml:"title,omitempty"` // Title of the page
|
||||
Description string `yaml:"description,omitempty"` // Meta description of the page
|
||||
Header string `yaml:"header,omitempty"` // Header is the text at the top of the page
|
||||
Logo string `yaml:"logo,omitempty"` // Logo to display on the page
|
||||
Link string `yaml:"link,omitempty"` // Link to open when clicking on the logo
|
||||
Buttons []Button `yaml:"buttons,omitempty"` // Buttons to display below the header
|
||||
CustomCSS string `yaml:"custom-css,omitempty"` // Custom CSS to include in the page
|
||||
DarkMode *bool `yaml:"dark-mode,omitempty"` // DarkMode is a flag to enable dark mode by default
|
||||
DefaultSortBy string `yaml:"default-sort-by,omitempty"` // DefaultSortBy is the default sort option ('name', 'group', 'health')
|
||||
DefaultFilterBy string `yaml:"default-filter-by,omitempty"` // DefaultFilterBy is the default filter option ('none', 'failing', 'unstable')
|
||||
|
||||
Title string `yaml:"title,omitempty"` // Title of the page
|
||||
Description string `yaml:"description,omitempty"` // Meta description of the page
|
||||
DashboardHeading string `yaml:"dashboard-heading,omitempty"` // Dashboard Title between header and endpoints
|
||||
DashboardSubheading string `yaml:"dashboard-subheading,omitempty"` // Dashboard Description between header and endpoints
|
||||
Header string `yaml:"header,omitempty"` // Header is the text at the top of the page
|
||||
Logo string `yaml:"logo,omitempty"` // Logo to display on the page
|
||||
Link string `yaml:"link,omitempty"` // Link to open when clicking on the logo
|
||||
Buttons []Button `yaml:"buttons,omitempty"` // Buttons to display below the header
|
||||
CustomCSS string `yaml:"custom-css,omitempty"` // Custom CSS to include in the page
|
||||
DarkMode *bool `yaml:"dark-mode,omitempty"` // DarkMode is a flag to enable dark mode by default
|
||||
DefaultSortBy string `yaml:"default-sort-by,omitempty"` // DefaultSortBy is the default sort option ('name', 'group', 'health')
|
||||
DefaultFilterBy string `yaml:"default-filter-by,omitempty"` // DefaultFilterBy is the default filter option ('none', 'failing', 'unstable')
|
||||
//////////////////////////////////////////////
|
||||
// Non-configurable - used for UI rendering //
|
||||
//////////////////////////////////////////////
|
||||
@@ -73,6 +76,8 @@ func GetDefaultConfig() *Config {
|
||||
return &Config{
|
||||
Title: defaultTitle,
|
||||
Description: defaultDescription,
|
||||
DashboardHeading: defaultDashboardHeading,
|
||||
DashboardSubheading: defaultDashboardSubheading,
|
||||
Header: defaultHeader,
|
||||
Logo: defaultLogo,
|
||||
Link: defaultLink,
|
||||
@@ -92,6 +97,12 @@ func (cfg *Config) ValidateAndSetDefaults() error {
|
||||
if len(cfg.Description) == 0 {
|
||||
cfg.Description = defaultDescription
|
||||
}
|
||||
if len(cfg.DashboardHeading) == 0 {
|
||||
cfg.DashboardHeading = defaultDashboardHeading
|
||||
}
|
||||
if len(cfg.DashboardSubheading) == 0 {
|
||||
cfg.DashboardSubheading = defaultDashboardSubheading
|
||||
}
|
||||
if len(cfg.Header) == 0 {
|
||||
cfg.Header = defaultHeader
|
||||
}
|
||||
|
||||
@@ -7,31 +7,110 @@ import (
|
||||
)
|
||||
|
||||
func TestConfig_ValidateAndSetDefaults(t *testing.T) {
|
||||
cfg := &Config{
|
||||
Title: "",
|
||||
Description: "",
|
||||
Header: "",
|
||||
Logo: "",
|
||||
Link: "",
|
||||
}
|
||||
if err := cfg.ValidateAndSetDefaults(); err != nil {
|
||||
t.Error("expected no error, got", err.Error())
|
||||
}
|
||||
if cfg.Title != defaultTitle {
|
||||
t.Errorf("expected title to be %s, got %s", defaultTitle, cfg.Title)
|
||||
}
|
||||
if cfg.Description != defaultDescription {
|
||||
t.Errorf("expected description to be %s, got %s", defaultDescription, cfg.Description)
|
||||
}
|
||||
if cfg.Header != defaultHeader {
|
||||
t.Errorf("expected header to be %s, got %s", defaultHeader, cfg.Header)
|
||||
}
|
||||
if cfg.DefaultSortBy != defaultSortBy {
|
||||
t.Errorf("expected defaultSortBy to be %s, got %s", defaultSortBy, cfg.DefaultSortBy)
|
||||
}
|
||||
if cfg.DefaultFilterBy != defaultFilterBy {
|
||||
t.Errorf("expected defaultFilterBy to be %s, got %s", defaultFilterBy, cfg.DefaultFilterBy)
|
||||
}
|
||||
t.Run("empty-config", func(t *testing.T) {
|
||||
cfg := &Config{
|
||||
Title: "",
|
||||
Description: "",
|
||||
DashboardHeading: "",
|
||||
DashboardSubheading: "",
|
||||
Header: "",
|
||||
Logo: "",
|
||||
Link: "",
|
||||
}
|
||||
if err := cfg.ValidateAndSetDefaults(); err != nil {
|
||||
t.Error("expected no error, got", err.Error())
|
||||
}
|
||||
if cfg.Title != defaultTitle {
|
||||
t.Errorf("expected title to be %s, got %s", defaultTitle, cfg.Title)
|
||||
}
|
||||
if cfg.Description != defaultDescription {
|
||||
t.Errorf("expected description to be %s, got %s", defaultDescription, cfg.Description)
|
||||
}
|
||||
if cfg.DashboardHeading != defaultDashboardHeading {
|
||||
t.Errorf("expected DashboardHeading to be %s, got %s", defaultDashboardHeading, cfg.DashboardHeading)
|
||||
}
|
||||
if cfg.DashboardSubheading != defaultDashboardSubheading {
|
||||
t.Errorf("expected DashboardSubheading to be %s, got %s", defaultDashboardSubheading, cfg.DashboardSubheading)
|
||||
}
|
||||
if cfg.Header != defaultHeader {
|
||||
t.Errorf("expected header to be %s, got %s", defaultHeader, cfg.Header)
|
||||
}
|
||||
if cfg.DefaultSortBy != defaultSortBy {
|
||||
t.Errorf("expected defaultSortBy to be %s, got %s", defaultSortBy, cfg.DefaultSortBy)
|
||||
}
|
||||
if cfg.DefaultFilterBy != defaultFilterBy {
|
||||
t.Errorf("expected defaultFilterBy to be %s, got %s", defaultFilterBy, cfg.DefaultFilterBy)
|
||||
}
|
||||
})
|
||||
t.Run("custom-values", func(t *testing.T) {
|
||||
cfg := &Config{
|
||||
Title: "Custom Title",
|
||||
Description: "Custom Description",
|
||||
DashboardHeading: "Production Status",
|
||||
DashboardSubheading: "Monitor all production endpoints",
|
||||
Header: "My Company",
|
||||
Logo: "https://example.com/logo.png",
|
||||
Link: "https://example.com",
|
||||
DefaultSortBy: "health",
|
||||
DefaultFilterBy: "failing",
|
||||
}
|
||||
if err := cfg.ValidateAndSetDefaults(); err != nil {
|
||||
t.Error("expected no error, got", err.Error())
|
||||
}
|
||||
if cfg.Title != "Custom Title" {
|
||||
t.Errorf("expected title to be preserved, got %s", cfg.Title)
|
||||
}
|
||||
if cfg.Description != "Custom Description" {
|
||||
t.Errorf("expected description to be preserved, got %s", cfg.Description)
|
||||
}
|
||||
if cfg.DashboardHeading != "Production Status" {
|
||||
t.Errorf("expected DashboardHeading to be preserved, got %s", cfg.DashboardHeading)
|
||||
}
|
||||
if cfg.DashboardSubheading != "Monitor all production endpoints" {
|
||||
t.Errorf("expected DashboardSubheading to be preserved, got %s", cfg.DashboardSubheading)
|
||||
}
|
||||
if cfg.Header != "My Company" {
|
||||
t.Errorf("expected header to be preserved, got %s", cfg.Header)
|
||||
}
|
||||
if cfg.Logo != "https://example.com/logo.png" {
|
||||
t.Errorf("expected logo to be preserved, got %s", cfg.Logo)
|
||||
}
|
||||
if cfg.Link != "https://example.com" {
|
||||
t.Errorf("expected link to be preserved, got %s", cfg.Link)
|
||||
}
|
||||
if cfg.DefaultSortBy != "health" {
|
||||
t.Errorf("expected defaultSortBy to be preserved, got %s", cfg.DefaultSortBy)
|
||||
}
|
||||
if cfg.DefaultFilterBy != "failing" {
|
||||
t.Errorf("expected defaultFilterBy to be preserved, got %s", cfg.DefaultFilterBy)
|
||||
}
|
||||
})
|
||||
t.Run("partial-custom-values", func(t *testing.T) {
|
||||
cfg := &Config{
|
||||
Title: "Custom Title",
|
||||
DashboardHeading: "My Dashboard",
|
||||
Header: "",
|
||||
DashboardSubheading: "",
|
||||
}
|
||||
if err := cfg.ValidateAndSetDefaults(); err != nil {
|
||||
t.Error("expected no error, got", err.Error())
|
||||
}
|
||||
if cfg.Title != "Custom Title" {
|
||||
t.Errorf("expected custom title to be preserved, got %s", cfg.Title)
|
||||
}
|
||||
if cfg.DashboardHeading != "My Dashboard" {
|
||||
t.Errorf("expected custom DashboardHeading to be preserved, got %s", cfg.DashboardHeading)
|
||||
}
|
||||
if cfg.DashboardSubheading != defaultDashboardSubheading {
|
||||
t.Errorf("expected DashboardSubheading to use default, got %s", cfg.DashboardSubheading)
|
||||
}
|
||||
if cfg.Header != defaultHeader {
|
||||
t.Errorf("expected header to use default, got %s", cfg.Header)
|
||||
}
|
||||
if cfg.Description != defaultDescription {
|
||||
t.Errorf("expected description to use default, got %s", cfg.Description)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestButton_Validate(t *testing.T) {
|
||||
@@ -78,6 +157,12 @@ func TestGetDefaultConfig(t *testing.T) {
|
||||
if defaultConfig.Title != defaultTitle {
|
||||
t.Error("expected GetDefaultConfig() to return defaultTitle, got", defaultConfig.Title)
|
||||
}
|
||||
if defaultConfig.DashboardHeading != defaultDashboardHeading {
|
||||
t.Error("expected GetDefaultConfig() to return defaultDashboardHeading, got", defaultConfig.DashboardHeading)
|
||||
}
|
||||
if defaultConfig.DashboardSubheading != defaultDashboardSubheading {
|
||||
t.Error("expected GetDefaultConfig() to return defaultDashboardSubheading, got", defaultConfig.DashboardSubheading)
|
||||
}
|
||||
if defaultConfig.Logo != defaultLogo {
|
||||
t.Error("expected GetDefaultConfig() to return defaultLogo, got", defaultConfig.Logo)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<script type="text/javascript">
|
||||
window.config = {logo: "{{ .UI.Logo }}", header: "{{ .UI.Header }}", link: "{{ .UI.Link }}", buttons: [], maximumNumberOfResults: "{{ .UI.MaximumNumberOfResults }}", defaultSortBy: "{{ .UI.DefaultSortBy }}", defaultFilterBy: "{{ .UI.DefaultFilterBy }}"};{{- range .UI.Buttons}}window.config.buttons.push({name:"{{ .Name }}",link:"{{ .Link }}"});{{end}}
|
||||
window.config = {logo: "{{ .UI.Logo }}", header: "{{ .UI.Header }}", dashboardHeading: "{{ .UI.DashboardHeading }}", dashboardSubheading: "{{ .UI.DashboardSubheading }}", link: "{{ .UI.Link }}", buttons: [], maximumNumberOfResults: "{{ .UI.MaximumNumberOfResults }}", defaultSortBy: "{{ .UI.DefaultSortBy }}", defaultFilterBy: "{{ .UI.DefaultFilterBy }}"};{{- range .UI.Buttons}}window.config.buttons.push({name:"{{ .Name }}",link:"{{ .Link }}"});{{end}}
|
||||
// Initialize theme immediately to prevent flash
|
||||
(function() {
|
||||
const themeFromCookie = document.cookie.match(/theme=(dark|light);?/)?.[1];
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
<div class="mb-6">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<div>
|
||||
<h1 class="text-4xl font-bold tracking-tight">Health Dashboard</h1>
|
||||
<p class="text-muted-foreground mt-2">Monitor the health of your endpoints in real-time</p>
|
||||
<h1 class="text-4xl font-bold tracking-tight">{{ dashboardHeading }}</h1>
|
||||
<p class="text-muted-foreground mt-2">{{ dashboardSubheading }}</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<Button
|
||||
@@ -532,6 +532,14 @@ const initializeCollapsedGroups = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const dashboardHeading = computed(() => {
|
||||
return window.config && window.config.dashboardHeading && window.config.dashboardHeading !== '{{ .UI.DashboardHeading }}' ? window.config.dashboardHeading : "Health Dashboard"
|
||||
})
|
||||
|
||||
const dashboardSubheading = computed(() => {
|
||||
return window.config && window.config.dashboardSubheading && window.config.dashboardSubheading !== '{{ .UI.dashboardSubheading }}' ? window.config.dashboardSubheading : "Monitor the health of your endpoints in real-time"
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<!doctype html><html lang="en" class="{{ .Theme }}"><head><meta charset="utf-8"/><script>window.config = {logo: "{{ .UI.Logo }}", header: "{{ .UI.Header }}", link: "{{ .UI.Link }}", buttons: [], maximumNumberOfResults: "{{ .UI.MaximumNumberOfResults }}", defaultSortBy: "{{ .UI.DefaultSortBy }}", defaultFilterBy: "{{ .UI.DefaultFilterBy }}"};{{- range .UI.Buttons}}window.config.buttons.push({name:"{{ .Name }}",link:"{{ .Link }}"});{{end}}
|
||||
<!doctype html><html lang="en" class="{{ .Theme }}"><head><meta charset="utf-8"/><script>window.config = {logo: "{{ .UI.Logo }}", header: "{{ .UI.Header }}", dashboardHeading: "{{ .UI.DashboardHeading }}", dashboardSubheading: "{{ .UI.DashboardSubheading }}", link: "{{ .UI.Link }}", buttons: [], maximumNumberOfResults: "{{ .UI.MaximumNumberOfResults }}", defaultSortBy: "{{ .UI.DefaultSortBy }}", defaultFilterBy: "{{ .UI.DefaultFilterBy }}"};{{- range .UI.Buttons}}window.config.buttons.push({name:"{{ .Name }}",link:"{{ .Link }}"});{{end}}
|
||||
// Initialize theme immediately to prevent flash
|
||||
(function() {
|
||||
const themeFromCookie = document.cookie.match(/theme=(dark|light);?/)?.[1];
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user