Merge branch 'master' into dependabot/go_modules/code.gitea.io/sdk/gitea-0.24.1

This commit is contained in:
TwiN
2026-04-12 23:10:57 -04:00
committed by GitHub
15 changed files with 173 additions and 55 deletions
+1
View File
@@ -0,0 +1 @@
See AGENTS.md for agent instructions
@@ -35,7 +35,7 @@ jobs:
steps:
- name: Get PR branch
id: pr
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
script: |
const pr = await github.rest.pulls.get({
@@ -52,7 +52,7 @@ jobs:
ref: ${{ steps.pr.outputs.ref }}
- name: Regenerate static assets
run: |
make frontend-install-dependencies
make frontend-install
make frontend-build
- name: Commit and push changes
id: commit
@@ -79,7 +79,7 @@ jobs:
pull-requests: write
steps:
- name: Create response comment
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
script: |
const status = '${{ needs.regenerate-static-assets.outputs.status }}';
@@ -88,6 +88,8 @@ jobs:
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 if (status === 'success') {
// Assets regenerated successfully - no comment needed, just add reaction
} else {
reaction = '-1';
message = `@${context.actor} There was an issue regenerating static assets. Please check the [workflow run logs](${workflowUrl}) for more details.`;
+1 -1
View File
@@ -14,5 +14,5 @@ jobs:
timeout-minutes: 30
steps:
- uses: actions/checkout@v5
- run: make frontend-install-dependencies
- run: make frontend-install
- run: make frontend-build
+84
View File
@@ -0,0 +1,84 @@
# AGENTS.md
Guidance for AI coding agents working in this repository.
## Commands
- `make install` — Build the `gatus` binary
- `make run` — Run in dev mode (uses `./config.yaml`)
- `make test` — Run all Go tests with coverage
- `make clean` — Remove built binary
- `make frontend-install` — Install frontend npm dependencies
- `make frontend-build` — Build the Vue.js frontend (**must run after any `web/` changes**)
- `make frontend-dev` — Start the Vue dev server
## Key Rules
- **Vendored dependencies**: After adding/updating a Go dependency, run `go mod tidy && go mod vendor`
- **Frontend build artifacts**: `web/static/` is generated by `make frontend-build` and embedded into the Go binary at compile time via `//go:embed`. Never edit files in `web/static/` directly
- **Config validation order**: In `config/config.go` `parseAndValidateConfigBytes`, `ValidateAlertingConfig` **must** run before `ValidateEndpointsConfig` because provider default alerts must be parsed before endpoint defaults are set
- **Invalid configs panic at startup** — validation errors are fatal, not graceful
- **Hot-reload**: `main.go` watches the config file and re-runs the full stop → save → load → init → start cycle. New startup logic must account for this
## Environment Variables
- `GATUS_CONFIG_PATH` — Config file path (default: `config/config.yaml`); can be a directory of YAML files (deep-merged)
- `GATUS_LOG_LEVEL``DEBUG`, `INFO`, `WARN`, `ERROR`
- `ENVIRONMENT=dev` — Enables CORS for frontend dev on `localhost:8081`
## Project Structure
- `main.go` — Entry point; loads config, initializes storage, starts watchdog + API server
- `config/` — Central config struct (`config.go`) and sub-packages per config section (each with `ValidateAndSetDefaults()`)
- `alerting/` — Alerting config (`config.go`), alert types (`alert/type.go`), and one sub-package per provider under `provider/`
- `api/` — Fiber-based HTTP API routes and SPA serving
- `watchdog/` — Monitoring loop; spawns a goroutine per endpoint with semaphore-based concurrency control
- `client/` — HTTP/gRPC client used for health checks
- `storage/store/` — Storage abstraction with `memory/`, `sql/` (SQLite + PostgreSQL) implementations
- `security/` — Authentication (basic + OIDC) and security middleware
- `web/app/` — Vue 3 frontend source; builds to `web/static/` (embedded into the Go binary)
- `vendor/` — Vendored Go dependencies (committed to repo)
## Common Changes
### Adding an Alerting Provider
Use `alerting/provider/slack/` as the reference implementation. Every new provider touches **6 locations**:
1. **Create provider package**`alerting/provider/{name}/{name}.go` + `{name}_test.go`
- `Config` struct with `Validate()` and `Merge(*Config)` methods
- `AlertProvider` struct with `DefaultConfig` (yaml inline), `DefaultAlert *alert.Alert`, `Overrides []Override`
- Use `client.GetHTTPClient(nil)` (not `http.DefaultClient`), always `defer response.Body.Close()`
2. **Register type** — Add `Type{Name} Type = "{name}"` in `alerting/alert/type.go`
3. **Add to config struct** — Add field to `alerting.Config` in `alerting/config.go`; the **YAML tag must exactly match** the `alert.Type` string (reflection-based lookup via `GetAlertingProviderByAlertType`)
4. **Add compile-time checks** — Both `_ AlertProvider = (*pkg.AlertProvider)(nil)` **and** `_ Config[pkg.Config] = (*pkg.Config)(nil)` in `alerting/provider/provider.go`
5. **Register in validation** — Add the type to the `alertTypes` slice in `config/config.go` `ValidateAlertingConfig`
6. **Document** — Add to README.md in alphabetical order
### Adding a Config Section
1. Create package in `config/` with struct + `ValidateAndSetDefaults()` method
2. Add field to `Config` struct in `config/config.go`
3. Add `Validate*` call in `parseAndValidateConfigBytes`**respect ordering constraints** (see comments in that function)
## Frontend
- Vue 3 Composition API (`<script setup>`) + Tailwind CSS
- Props-based data flow — **do not use provide/inject**
- All components must include `dark:` prefixed Tailwind classes for dark mode
- `window.config` contains server-injected UI settings (Go template placeholders)
## API Routes (`api/api.go`)
- Unprotected routes are registered **before** `ApplySecurityMiddleware`
- Protected routes are registered **after** — placement order matters
## Testing
- Config structs should have `*_test.go` files
- Use `t.Parallel()` where appropriate
- API tests spin up test servers (see existing `*_test.go` in `api/`)
## Commits & PRs
When creating a commit or PR as an agent, state that it was made by an agent and include your model name and version.
+8 -2
View File
@@ -25,12 +25,15 @@ test:
# Docker #
##########
.PHONY: docker-build
docker-build:
docker build -t twinproduction/gatus:latest .
.PHONY: docker-run
docker-run:
docker run -p 8080:8080 --name gatus twinproduction/gatus:latest
.PHONY: docker-build-and-run
docker-build-and-run: docker-build docker-run
@@ -38,11 +41,14 @@ docker-build-and-run: docker-build docker-run
# Front end #
#############
frontend-install-dependencies:
.PHONY: frontend-install
frontend-install:
npm --prefix web/app install
.PHONY: frontend-build
frontend-build:
npm --prefix web/app run build
frontend-run:
.PHONY: frontend-dev
frontend-dev:
npm --prefix web/app run serve
+3 -2
View File
@@ -536,8 +536,8 @@ Allows you to configure the application wide defaults for the dashboard's UI. So
| `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.header` | Header at the top of the dashboard. Also used as the title on the OIDC login page. | `Gatus` |
| `ui.logo` | URL to the logo to display. When set, shown alongside the Gatus logo on the OIDC login page. | `""` |
| `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` |
@@ -549,6 +549,7 @@ Allows you to configure the application wide defaults for the dashboard's UI. So
| `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` |
| `ui.login-subtitle` | Subtitle displayed on the OIDC login page. | `System Monitoring Dashboard` |
### 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.
+6
View File
@@ -23,6 +23,7 @@ const (
defaultCustomCSS = ""
defaultSortBy = "name"
defaultFilterBy = "none"
defaultLoginSubtitle = "System Monitoring Dashboard"
)
var (
@@ -48,6 +49,7 @@ type Config struct {
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')
LoginSubtitle string `yaml:"login-subtitle,omitempty"` // LoginSubtitle is the subtitle displayed on the OIDC login page
//////////////////////////////////////////////
// Non-configurable - used for UI rendering //
//////////////////////////////////////////////
@@ -95,6 +97,7 @@ func GetDefaultConfig() *Config {
DarkMode: &defaultDarkMode,
DefaultSortBy: defaultSortBy,
DefaultFilterBy: defaultFilterBy,
LoginSubtitle: defaultLoginSubtitle,
MaximumNumberOfResults: storage.DefaultMaximumNumberOfResults,
Favicon: Favicon{
Default: defaultFavicon,
@@ -143,6 +146,9 @@ func (cfg *Config) ValidateAndSetDefaults() error {
} else if cfg.DefaultFilterBy != "none" && cfg.DefaultFilterBy != "failing" && cfg.DefaultFilterBy != "unstable" {
return ErrInvalidDefaultFilterBy
}
if len(cfg.LoginSubtitle) == 0 {
cfg.LoginSubtitle = defaultLoginSubtitle
}
if len(cfg.Favicon.Default) == 0 {
cfg.Favicon.Default = defaultFavicon
}
+13
View File
@@ -50,6 +50,9 @@ func TestConfig_ValidateAndSetDefaults(t *testing.T) {
if cfg.Favicon.Size32x32 != defaultFavicon32 {
t.Errorf("expected favicon to be %s, got %s", defaultFavicon32, cfg.Favicon.Size32x32)
}
if cfg.LoginSubtitle != defaultLoginSubtitle {
t.Errorf("expected LoginSubtitle to be %s, got %s", defaultLoginSubtitle, cfg.LoginSubtitle)
}
})
t.Run("custom-values", func(t *testing.T) {
cfg := &Config{
@@ -62,6 +65,7 @@ func TestConfig_ValidateAndSetDefaults(t *testing.T) {
Link: "https://example.com",
DefaultSortBy: "health",
DefaultFilterBy: "failing",
LoginSubtitle: "Welcome",
}
if err := cfg.ValidateAndSetDefaults(); err != nil {
t.Error("expected no error, got", err.Error())
@@ -93,6 +97,9 @@ func TestConfig_ValidateAndSetDefaults(t *testing.T) {
if cfg.DefaultFilterBy != "failing" {
t.Errorf("expected defaultFilterBy to be preserved, got %s", cfg.DefaultFilterBy)
}
if cfg.LoginSubtitle != "Welcome" {
t.Errorf("expected LoginSubtitle to be preserved, got %s", cfg.LoginSubtitle)
}
})
t.Run("partial-custom-values", func(t *testing.T) {
cfg := &Config{
@@ -119,6 +126,9 @@ func TestConfig_ValidateAndSetDefaults(t *testing.T) {
if cfg.Description != defaultDescription {
t.Errorf("expected description to use default, got %s", cfg.Description)
}
if cfg.LoginSubtitle != defaultLoginSubtitle {
t.Errorf("expected LoginSubtitle to use default, got %s", cfg.LoginSubtitle)
}
})
}
@@ -181,6 +191,9 @@ func TestGetDefaultConfig(t *testing.T) {
if defaultConfig.DefaultFilterBy != defaultFilterBy {
t.Error("expected GetDefaultConfig() to return defaultFilterBy, got", defaultConfig.DefaultFilterBy)
}
if defaultConfig.LoginSubtitle != defaultLoginSubtitle {
t.Error("expected GetDefaultConfig() to return defaultLoginSubtitle, got", defaultConfig.LoginSubtitle)
}
}
func TestConfig_ValidateAndSetDefaults_DefaultSortBy(t *testing.T) {
+10 -10
View File
@@ -30,15 +30,15 @@ require (
golang.org/x/crypto v0.49.0
golang.org/x/oauth2 v0.36.0
golang.org/x/sync v0.20.0
google.golang.org/api v0.265.0
google.golang.org/grpc v1.78.0
google.golang.org/api v0.273.1
google.golang.org/grpc v1.79.3
gopkg.in/mail.v2 v2.3.1
gopkg.in/yaml.v3 v3.0.1
modernc.org/sqlite v1.47.0
)
require (
cloud.google.com/go/auth v0.18.1 // indirect
cloud.google.com/go/auth v0.18.2 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
cloud.google.com/go/compute/metadata v0.9.0 // indirect
github.com/42wim/httpsig v1.2.4 // indirect
@@ -68,8 +68,8 @@ require (
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.11 // indirect
github.com/googleapis/gax-go/v2 v2.16.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.14 // indirect
github.com/googleapis/gax-go/v2 v2.19.0 // indirect
github.com/hashicorp/go-version v1.8.0 // indirect
github.com/klauspost/compress v1.18.3 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
@@ -85,17 +85,17 @@ require (
github.com/valyala/bytebufferpool v1.0.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
go.opentelemetry.io/otel v1.39.0 // indirect
go.opentelemetry.io/otel/metric v1.39.0 // indirect
go.opentelemetry.io/otel/trace v1.39.0 // indirect
go.opentelemetry.io/otel v1.42.0 // indirect
go.opentelemetry.io/otel/metric v1.42.0 // indirect
go.opentelemetry.io/otel/trace v1.42.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
golang.org/x/image v0.35.0 // indirect
golang.org/x/mod v0.33.0 // indirect
golang.org/x/net v0.51.0 // indirect
golang.org/x/net v0.52.0 // indirect
golang.org/x/sys v0.42.0 // indirect
golang.org/x/text v0.35.0 // indirect
golang.org/x/tools v0.42.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
modernc.org/libc v1.70.0 // indirect
+26 -26
View File
@@ -1,5 +1,5 @@
cloud.google.com/go/auth v0.18.1 h1:IwTEx92GFUo2pJ6Qea0EU3zYvKnTAeRCODxfA/G5UWs=
cloud.google.com/go/auth v0.18.1/go.mod h1:GfTYoS9G3CWpRA3Va9doKN9mjPGRS+v41jmZAhBzbrA=
cloud.google.com/go/auth v0.18.2 h1:+Nbt5Ev0xEqxlNjd6c+yYUeosQ5TtEUaNcN/3FozlaM=
cloud.google.com/go/auth v0.18.2/go.mod h1:xD+oY7gcahcu7G2SG2DsBerfFxgPAJz17zz2joOFF3M=
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
@@ -101,10 +101,10 @@ github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.11 h1:vAe81Msw+8tKUxi2Dqh/NZMz7475yUvmRIkXr4oN2ao=
github.com/googleapis/enterprise-certificate-proxy v0.3.11/go.mod h1:RFV7MUdlb7AgEq2v7FmMCfeSMCllAzWxFgRdusoGks8=
github.com/googleapis/gax-go/v2 v2.16.0 h1:iHbQmKLLZrexmb0OSsNGTeSTS0HO4YvFOG8g5E4Zd0Y=
github.com/googleapis/gax-go/v2 v2.16.0/go.mod h1:o1vfQjjNZn4+dPnRdl/4ZD7S9414Y4xA+a/6Icj6l14=
github.com/googleapis/enterprise-certificate-proxy v0.3.14 h1:yh8ncqsbUY4shRD5dA6RlzjJaT4hi3kII+zYw8wmLb8=
github.com/googleapis/enterprise-certificate-proxy v0.3.14/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg=
github.com/googleapis/gax-go/v2 v2.19.0 h1:fYQaUOiGwll0cGj7jmHT/0nPlcrZDFPrZRhTsoCr8hE=
github.com/googleapis/gax-go/v2 v2.19.0/go.mod h1:w2ROXVdfGEVFXzmlciUU4EdjHgWvB5h2n6x/8XSTTJA=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4=
@@ -170,16 +170,16 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho=
go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc=
go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4=
go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI=
go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo=
go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts=
go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA=
go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc=
go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY=
go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
@@ -212,8 +212,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -259,8 +259,8 @@ golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
@@ -273,12 +273,12 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/api v0.265.0 h1:FZvfUdI8nfmuNrE34aOWFPmLC+qRBEiNm3JdivTvAAU=
google.golang.org/api v0.265.0/go.mod h1:uAvfEl3SLUj/7n6k+lJutcswVojHPp2Sp08jWCu8hLY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
google.golang.org/api v0.273.1 h1:L7G/TmpAMz0nKx/ciAVssVmWQiOF6+pOuXeKrWVsquY=
google.golang.org/api v0.273.1/go.mod h1:JbAt7mF+XVmWu6xNP8/+CTiGH30ofmCmk9nM8d8fHew=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7 h1:ndE4FoJqsIceKP2oYSnUZqhTdYufCYYkqwtFzfrhI7w=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
+1 -1
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<script type="text/javascript">
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}}
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 }}", loginSubtitle: "{{ .UI.LoginSubtitle }}"};{{- 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];
+12 -7
View File
@@ -112,13 +112,14 @@
<div v-else id="login-container" class="flex items-center justify-center min-h-screen p-4">
<Card class="w-full max-w-md">
<CardHeader class="text-center">
<img
src="./assets/logo.svg"
alt="Gatus"
class="w-20 h-20 mx-auto mb-4"
/>
<CardTitle class="text-3xl">Gatus</CardTitle>
<p class="text-muted-foreground mt-2">System Monitoring Dashboard</p>
<div v-if="logo" class="flex items-center justify-center gap-4 mb-4">
<img :src="logo" alt="" class="w-20 h-20 object-contain" />
<div class="w-px h-12 bg-border"></div>
<img src="./assets/logo.svg" alt="Gatus" class="w-20 h-20" />
</div>
<img v-else src="./assets/logo.svg" alt="Gatus" class="w-20 h-20 mx-auto mb-4" />
<CardTitle class="text-3xl">{{ header }}</CardTitle>
<p class="text-muted-foreground mt-2">{{ loginSubtitle }}</p>
</CardHeader>
<CardContent>
<div v-if="route && route.query.error" class="mb-6">
@@ -191,6 +192,10 @@ const buttons = computed(() => {
return window.config && window.config.buttons ? window.config.buttons : []
})
const loginSubtitle = computed(() => {
return window.config && window.config.loginSubtitle && window.config.loginSubtitle !== '{{ .UI.LoginSubtitle }}' ? window.config.loginSubtitle : "System Monitoring Dashboard"
})
// Methods
const fetchConfig = async () => {
try {
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -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 }}", 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}}
<!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 }}", loginSubtitle: "{{ .UI.LoginSubtitle }}"};{{- 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];
+1 -1
View File
File diff suppressed because one or more lines are too long