mirror of
https://github.com/TwiN/gatus.git
synced 2026-08-24 08:06:03 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31bf2aeb80 | ||
|
|
787f6f0d74 | ||
|
|
17a431321c | ||
|
|
05e9add16d | ||
|
|
c4ef56511d | ||
|
|
cfa2c8ef6f | ||
|
|
f36b6863ce | ||
|
|
24482cf7a0 | ||
|
|
d661a0ea6d | ||
|
|
a0ec6941ab | ||
|
|
5e711fb3b9 | ||
|
|
ab66e7ec8a |
Binary file not shown.
|
After Width: | Height: | Size: 51 KiB |
@@ -25,6 +25,7 @@ docker run -p 8080:8080 --name gatus twinproduction/gatus
|
||||
For more details, see [Usage](#usage)
|
||||
</details>
|
||||
|
||||
Have any feedback or want to share your good/bad experience with Gatus? Feel free to email me at [feedback@gatus.io](mailto:feedback@gatus.io)
|
||||
|
||||
## Table of Contents
|
||||
- [Why Gatus?](#why-gatus)
|
||||
@@ -103,7 +104,7 @@ The main features of Gatus are:
|
||||
- **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 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)**:  
|
||||
- **[Badges](#badges)**:  
|
||||
|
||||
|
||||
## Usage
|
||||
@@ -1129,9 +1130,9 @@ web:
|
||||
|
||||
### Badges
|
||||
### Uptime
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
Gatus can automatically generate a SVG badge for one of your monitored endpoints.
|
||||
This allows you to put badges in your individual applications' README or even create your own status page, if you
|
||||
@@ -1156,15 +1157,15 @@ https://example.com/api/v1/endpoints/_frontend/uptimes/7d/badge.svg
|
||||
```
|
||||
Example:
|
||||
```
|
||||

|
||||

|
||||
```
|
||||
If you'd like to see a visual example of each badges available, you can simply navigate to the endpoint's detail page.
|
||||
|
||||
|
||||
### Response time
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
The endpoint to generate a badge is the following:
|
||||
```
|
||||
@@ -1188,7 +1189,7 @@ Specific endpoints can also be queried by using the following pattern:
|
||||
```
|
||||
/api/v1/endpoints/{group}_{endpoint}/statuses
|
||||
```
|
||||
Example: https://status.twin.sh/api/v1/endpoints/core_website-home/statuses
|
||||
Example: https://status.twin.sh/api/v1/endpoints/core_blog-home/statuses
|
||||
|
||||
Gzip compression will be used if the `Accept-Encoding` HTTP header contains `gzip`.
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ func (provider *AlertProvider) ToCustomAlertProvider(endpoint *core.Endpoint, al
|
||||
Body: fmt.Sprintf(`{
|
||||
"text": "",
|
||||
"username": "gatus",
|
||||
"icon_url": "https://raw.githubusercontent.com/TwiN/gatus/master/static/logo.png",
|
||||
"icon_url": "https://raw.githubusercontent.com/TwiN/gatus/master/.github/assets/logo.png",
|
||||
"attachments": [
|
||||
{
|
||||
"title": ":rescue_worker_helmet: Gatus",
|
||||
|
||||
@@ -34,7 +34,7 @@ func TestHandle(t *testing.T) {
|
||||
defer os.Clearenv()
|
||||
Handle(cfg.Security, cfg.Web, cfg.UI, cfg.Metrics)
|
||||
defer Shutdown()
|
||||
request, _ := http.NewRequest("GET", "/health", nil)
|
||||
request, _ := http.NewRequest("GET", "/health", http.NoBody)
|
||||
responseRecorder := httptest.NewRecorder()
|
||||
server.Handler.ServeHTTP(responseRecorder, request)
|
||||
if responseRecorder.Code != http.StatusOK {
|
||||
|
||||
@@ -43,13 +43,12 @@ func UptimeBadge(writer http.ResponseWriter, request *http.Request) {
|
||||
uptime, err := store.Get().GetUptimeByKey(key, from, time.Now())
|
||||
if err != nil {
|
||||
if err == common.ErrEndpointNotFound {
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
http.Error(writer, err.Error(), http.StatusNotFound)
|
||||
} else if err == common.ErrInvalidTimeRange {
|
||||
writer.WriteHeader(http.StatusBadRequest)
|
||||
http.Error(writer, err.Error(), http.StatusBadRequest)
|
||||
} else {
|
||||
writer.WriteHeader(http.StatusInternalServerError)
|
||||
http.Error(writer, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
_, _ = writer.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
formattedDate := time.Now().Format(http.TimeFormat)
|
||||
@@ -82,13 +81,12 @@ func ResponseTimeBadge(writer http.ResponseWriter, request *http.Request) {
|
||||
averageResponseTime, err := store.Get().GetAverageResponseTimeByKey(key, from, time.Now())
|
||||
if err != nil {
|
||||
if err == common.ErrEndpointNotFound {
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
http.Error(writer, err.Error(), http.StatusNotFound)
|
||||
} else if err == common.ErrInvalidTimeRange {
|
||||
writer.WriteHeader(http.StatusBadRequest)
|
||||
http.Error(writer, err.Error(), http.StatusBadRequest)
|
||||
} else {
|
||||
writer.WriteHeader(http.StatusInternalServerError)
|
||||
http.Error(writer, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
_, _ = writer.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
formattedDate := time.Now().Format(http.TimeFormat)
|
||||
|
||||
@@ -107,7 +107,7 @@ func TestUptimeBadge(t *testing.T) {
|
||||
}
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.Name, func(t *testing.T) {
|
||||
request, _ := http.NewRequest("GET", scenario.Path, nil)
|
||||
request, _ := http.NewRequest("GET", scenario.Path, http.NoBody)
|
||||
if scenario.Gzip {
|
||||
request.Header.Set("Accept-Encoding", "gzip")
|
||||
}
|
||||
|
||||
@@ -45,18 +45,16 @@ func ResponseTimeChart(writer http.ResponseWriter, r *http.Request) {
|
||||
hourlyAverageResponseTime, err := store.Get().GetHourlyAverageResponseTimeByKey(vars["key"], from, time.Now())
|
||||
if err != nil {
|
||||
if err == common.ErrEndpointNotFound {
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
http.Error(writer, err.Error(), http.StatusNotFound)
|
||||
} else if err == common.ErrInvalidTimeRange {
|
||||
writer.WriteHeader(http.StatusBadRequest)
|
||||
http.Error(writer, err.Error(), http.StatusBadRequest)
|
||||
} else {
|
||||
writer.WriteHeader(http.StatusInternalServerError)
|
||||
http.Error(writer, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
_, _ = writer.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
if len(hourlyAverageResponseTime) == 0 {
|
||||
writer.WriteHeader(http.StatusNoContent)
|
||||
_, _ = writer.Write(nil)
|
||||
http.Error(writer, "", http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
series := chart.TimeSeries{
|
||||
|
||||
@@ -66,7 +66,7 @@ func TestResponseTimeChart(t *testing.T) {
|
||||
}
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.Name, func(t *testing.T) {
|
||||
request, _ := http.NewRequest("GET", scenario.Path, nil)
|
||||
request, _ := http.NewRequest("GET", scenario.Path, http.NoBody)
|
||||
if scenario.Gzip {
|
||||
request.Header.Set("Accept-Encoding", "gzip")
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ func TestEndpointStatus(t *testing.T) {
|
||||
}
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.Name, func(t *testing.T) {
|
||||
request, _ := http.NewRequest("GET", scenario.Path, nil)
|
||||
request, _ := http.NewRequest("GET", scenario.Path, http.NoBody)
|
||||
if scenario.Gzip {
|
||||
request.Header.Set("Accept-Encoding", "gzip")
|
||||
}
|
||||
@@ -211,7 +211,7 @@ func TestEndpointStatuses(t *testing.T) {
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.Name, func(t *testing.T) {
|
||||
request, _ := http.NewRequest("GET", scenario.Path, nil)
|
||||
request, _ := http.NewRequest("GET", scenario.Path, http.NoBody)
|
||||
responseRecorder := httptest.NewRecorder()
|
||||
router.ServeHTTP(responseRecorder, request)
|
||||
if responseRecorder.Code != scenario.ExpectedCode {
|
||||
|
||||
@@ -22,7 +22,7 @@ func TestFavIcon(t *testing.T) {
|
||||
}
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.Name, func(t *testing.T) {
|
||||
request, _ := http.NewRequest("GET", scenario.Path, nil)
|
||||
request, _ := http.NewRequest("GET", scenario.Path, http.NoBody)
|
||||
responseRecorder := httptest.NewRecorder()
|
||||
router.ServeHTTP(responseRecorder, request)
|
||||
if responseRecorder.Code != scenario.ExpectedCode {
|
||||
|
||||
@@ -44,7 +44,7 @@ func TestCreateRouter(t *testing.T) {
|
||||
}
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.Name, func(t *testing.T) {
|
||||
request, _ := http.NewRequest("GET", scenario.Path, nil)
|
||||
request, _ := http.NewRequest("GET", scenario.Path, http.NoBody)
|
||||
if scenario.Gzip {
|
||||
request.Header.Set("Accept-Encoding", "gzip")
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ func TestSinglePageApplication(t *testing.T) {
|
||||
}
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.Name, func(t *testing.T) {
|
||||
request, _ := http.NewRequest("GET", scenario.Path, nil)
|
||||
request, _ := http.NewRequest("GET", scenario.Path, http.NoBody)
|
||||
if scenario.Gzip {
|
||||
request.Header.Set("Accept-Encoding", "gzip")
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ func TestExtractPageAndPageSizeFromRequest(t *testing.T) {
|
||||
}
|
||||
for _, scenario := range scenarios {
|
||||
t.Run("page-"+scenario.Page+"-pageSize-"+scenario.PageSize, func(t *testing.T) {
|
||||
request, _ := http.NewRequest("GET", fmt.Sprintf("/api/v1/statuses?page=%s&pageSize=%s", scenario.Page, scenario.PageSize), nil)
|
||||
request, _ := http.NewRequest("GET", fmt.Sprintf("/api/v1/statuses?page=%s&pageSize=%s", scenario.Page, scenario.PageSize), http.NoBody)
|
||||
actualPage, actualPageSize := extractPageAndPageSizeFromRequest(request)
|
||||
if actualPage != scenario.ExpectedPage {
|
||||
t.Errorf("expected %d, got %d", scenario.ExpectedPage, actualPage)
|
||||
|
||||
@@ -4,7 +4,7 @@ go 1.17
|
||||
|
||||
require (
|
||||
github.com/TwiN/gocache v1.2.4
|
||||
github.com/TwiN/health v1.0.1
|
||||
github.com/TwiN/health v1.1.0
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/go-ping/ping v0.0.0-20210911151512-381826476871
|
||||
|
||||
@@ -35,8 +35,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/TwiN/gocache v1.2.4 h1:AfJ1YRcxtQ/zZEN61URDwk/dwFG7LSRenU5qIm9dQzo=
|
||||
github.com/TwiN/gocache v1.2.4/go.mod h1:BjabsQQy6z5uHDorHa4LJVPEzFeitLIDbCtdv3gc1gA=
|
||||
github.com/TwiN/health v1.0.1 h1:Q8lE6mTMPG4A5nHXq5Xa+NY4Y8LkQdRBWh1ReUkuc6Y=
|
||||
github.com/TwiN/health v1.0.1/go.mod h1:Bt+lEvSi6C/9NWb7OoGmUmgtS4dfPeMM9EINnURv5dE=
|
||||
github.com/TwiN/health v1.1.0 h1:IbXV4b5VPxzfIqOPiP/19JdBNFYM0oEDReLbUazhb2k=
|
||||
github.com/TwiN/health v1.1.0/go.mod h1:Bt+lEvSi6C/9NWb7OoGmUmgtS4dfPeMM9EINnURv5dE=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
|
||||
+4
-4
@@ -6,8 +6,8 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrSQLStorageRequiresFile = errors.New("sql storage requires a non-empty file to be defined")
|
||||
ErrMemoryStorageDoesNotSupportFile = errors.New("memory storage does not support persistence, use sqlite if you want persistence on file")
|
||||
ErrSQLStorageRequiresPath = errors.New("sql storage requires a non-empty path to be defined")
|
||||
ErrMemoryStorageDoesNotSupportPath = errors.New("memory storage does not support persistence, use sqlite if you want persistence on file")
|
||||
ErrCannotSetBothFileAndPath = errors.New("file has been deprecated in favor of path: you cannot set both of them")
|
||||
)
|
||||
|
||||
@@ -45,7 +45,7 @@ func (c *Config) ValidateAndSetDefaults() error {
|
||||
c.Type = TypeMemory
|
||||
}
|
||||
if (c.Type == TypePostgres || c.Type == TypeSQLite) && len(c.Path) == 0 {
|
||||
return ErrSQLStorageRequiresFile
|
||||
return ErrSQLStorageRequiresPath
|
||||
}
|
||||
if c.Type == TypeMemory && len(c.Path) > 0 {
|
||||
log.Println("WARNING: Your configuration is using a storage of type memory with persistence, which has been deprecated")
|
||||
@@ -53,7 +53,7 @@ func (c *Config) ValidateAndSetDefaults() error {
|
||||
log.Println("WARNING: If you want persistence, use 'storage.type: sqlite' instead of 'storage.type: memory'")
|
||||
log.Println("WARNING: See https://github.com/TwiN/gatus/issues/198")
|
||||
// XXX: Uncomment the following line for v4.0.0
|
||||
//return ErrMemoryStorageDoesNotSupportFile
|
||||
//return ErrMemoryStorageDoesNotSupportPath
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -493,12 +493,6 @@ func (s *Store) getEndpointStatusByKey(tx *sql.Tx, key string, parameters *pagin
|
||||
log.Printf("[sql][getEndpointStatusByKey] Failed to retrieve results for key=%s: %s", key, err.Error())
|
||||
}
|
||||
}
|
||||
//if parameters.IncludeUptime {
|
||||
// now := time.Now()
|
||||
// endpointStatus.Uptime.LastHour, _, err = s.getEndpointUptime(tx, endpointID, now.Add(-time.Hour), now)
|
||||
// endpointStatus.Uptime.LastTwentyFourHours, _, err = s.getEndpointUptime(tx, endpointID, now.Add(-24*time.Hour), now)
|
||||
// endpointStatus.Uptime.LastSevenDays, _, err = s.getEndpointUptime(tx, endpointID, now.Add(-7*24*time.Hour), now)
|
||||
//}
|
||||
return endpointStatus, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -74,6 +74,7 @@ var (
|
||||
|
||||
func Get() Store {
|
||||
if !initialized {
|
||||
// This only happens in tests
|
||||
log.Println("[store][Get] Provider requested before it was initialized, automatically initializing")
|
||||
err := Initialize(nil)
|
||||
if err != nil {
|
||||
@@ -92,12 +93,12 @@ func Initialize(cfg *storage.Config) error {
|
||||
cancelFunc()
|
||||
}
|
||||
if cfg == nil {
|
||||
// This only happens in tests
|
||||
log.Println("[store][Initialize] nil storage config passed as parameter. This should only happen in tests. Defaulting to an empty config.")
|
||||
cfg = &storage.Config{}
|
||||
}
|
||||
if len(cfg.Path) == 0 && cfg.Type != storage.TypePostgres {
|
||||
log.Printf("[store][Initialize] Creating storage provider with type=%s and file=%s", cfg.Type, cfg.Path)
|
||||
} else {
|
||||
log.Printf("[store][Initialize] Creating storage provider with type=%s", cfg.Type)
|
||||
log.Printf("[store][Initialize] Creating storage provider of type=%s", cfg.Type)
|
||||
}
|
||||
ctx, cancelFunc = context.WithCancel(context.Background())
|
||||
switch cfg.Type {
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
bench:
|
||||
go test -bench . -race
|
||||
+34
-11
@@ -1,6 +1,9 @@
|
||||
package health
|
||||
|
||||
import "net/http"
|
||||
import (
|
||||
"net/http"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
handler = &healthHandler{
|
||||
@@ -13,6 +16,8 @@ var (
|
||||
type healthHandler struct {
|
||||
useJSON bool
|
||||
status Status
|
||||
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
// WithJSON configures whether the handler should output a response in JSON or in raw text
|
||||
@@ -24,30 +29,48 @@ func (h *healthHandler) WithJSON(v bool) *healthHandler {
|
||||
}
|
||||
|
||||
// ServeHTTP serves the HTTP request for the health handler
|
||||
func (h healthHandler) ServeHTTP(writer http.ResponseWriter, _ *http.Request) {
|
||||
var status int
|
||||
func (h *healthHandler) ServeHTTP(writer http.ResponseWriter, _ *http.Request) {
|
||||
var statusCode int
|
||||
var body []byte
|
||||
if h.status == Up {
|
||||
status = http.StatusOK
|
||||
handlerStatus := h.getStatus()
|
||||
if handlerStatus == Up {
|
||||
statusCode = http.StatusOK
|
||||
} else {
|
||||
status = http.StatusInternalServerError
|
||||
statusCode = http.StatusInternalServerError
|
||||
}
|
||||
if h.useJSON {
|
||||
writer.Header().Set("Content-Type", "application/json")
|
||||
body = []byte(`{"status":"` + h.status + `"}`)
|
||||
body = []byte(`{"status":"` + handlerStatus + `"}`)
|
||||
} else {
|
||||
body = []byte(h.status)
|
||||
body = []byte(handlerStatus)
|
||||
}
|
||||
writer.WriteHeader(status)
|
||||
writer.WriteHeader(statusCode)
|
||||
_, _ = writer.Write(body)
|
||||
}
|
||||
|
||||
func (h *healthHandler) getStatus() Status {
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
return h.status
|
||||
}
|
||||
|
||||
func (h *healthHandler) setStatus(status Status) {
|
||||
h.Lock()
|
||||
h.status = status
|
||||
h.Unlock()
|
||||
}
|
||||
|
||||
// Handler retrieves the health handler
|
||||
func Handler() *healthHandler {
|
||||
return handler
|
||||
}
|
||||
|
||||
// SetStatus sets the status to be reflected by the health handler
|
||||
// GetStatus retrieves the current status returned by the health handler
|
||||
func GetStatus() Status {
|
||||
return handler.getStatus()
|
||||
}
|
||||
|
||||
// SetStatus sets the status to be returned by the health handler
|
||||
func SetStatus(status Status) {
|
||||
handler.status = status
|
||||
handler.setStatus(status)
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -1,7 +1,7 @@
|
||||
# github.com/TwiN/gocache v1.2.4
|
||||
## explicit; go 1.16
|
||||
github.com/TwiN/gocache
|
||||
# github.com/TwiN/health v1.0.1
|
||||
# github.com/TwiN/health v1.1.0
|
||||
## explicit; go 1.17
|
||||
github.com/TwiN/health
|
||||
# github.com/beorn7/perks v1.0.1
|
||||
|
||||
Generated
+1991
-3315
File diff suppressed because it is too large
Load Diff
+10
-10
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gatus",
|
||||
"version": "3.2.2",
|
||||
"version": "3.3.3",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve --mode development",
|
||||
@@ -8,22 +8,22 @@
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": "^3.17.3",
|
||||
"vue": "^3.2.11",
|
||||
"core-js": "^3.19.1",
|
||||
"vue": "3.2.21",
|
||||
"vue-router": "^4.0.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^5.0.0-beta.3",
|
||||
"@vue/cli-plugin-eslint": "^5.0.0-beta.3",
|
||||
"@vue/cli-plugin-router": "^5.0.0-beta.3",
|
||||
"@vue/cli-service": "^5.0.0-beta.3",
|
||||
"@vue/compiler-sfc": "^3.2.11",
|
||||
"autoprefixer": "^10.3.4",
|
||||
"@vue/cli-plugin-babel": "5.0.0-beta.6",
|
||||
"@vue/cli-plugin-eslint": "5.0.0-beta.6",
|
||||
"@vue/cli-plugin-router": "5.0.0-beta.6",
|
||||
"@vue/cli-service": "5.0.0-beta.6",
|
||||
"@vue/compiler-sfc": "3.2.21",
|
||||
"autoprefixer": "10.4.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-vue": "^7.17.0",
|
||||
"postcss": "^8.3.6",
|
||||
"tailwindcss": "^2.2.15"
|
||||
"tailwindcss": "^2.2.19"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<script type="text/javascript">
|
||||
window.config = {
|
||||
logo: "{{ .Logo }}"
|
||||
};
|
||||
window.config = {logo: "{{ .Logo }}"};
|
||||
</script>
|
||||
<title>{{ .Title }}</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
</div>
|
||||
<div class="w-1/4 flex justify-end">
|
||||
<img v-if="getLogo" :src="getLogo" alt="Gatus" class="object-scale-down" style="max-width: 100px; min-width: 50px; min-height:50px;"/>
|
||||
<img v-if="!getLogo" src="./assets/logo.png" alt="Gatus" class="object-scale-down" style="max-width: 100px; min-width: 50px; min-height:50px;"/>
|
||||
<img v-else src="./assets/logo.png" alt="Gatus" class="object-scale-down" style="max-width: 100px; min-width: 50px; min-height:50px;"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,11 @@ export const helper = {
|
||||
methods: {
|
||||
generatePrettyTimeAgo(t) {
|
||||
let differenceInMs = new Date().getTime() - new Date(t).getTime();
|
||||
if (differenceInMs > 3600000) {
|
||||
if (differenceInMs > 3*86400000) { // If it was more than 3 days ago, we'll display the number of days ago
|
||||
let days = (differenceInMs / 86400000).toFixed(0);
|
||||
return days + " day" + (days !== "1" ? "s" : "") + " ago";
|
||||
}
|
||||
if (differenceInMs > 3600000) { // If it was more than 1h ago, display the number of hours ago
|
||||
let hours = (differenceInMs / 3600000).toFixed(0);
|
||||
return hours + " hour" + (hours !== "1" ? "s" : "") + " ago";
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,3 +1 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><script>window.config = {
|
||||
logo: "{{ .Logo }}"
|
||||
};</script><title>{{ .Title }}</title><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><script defer="defer" src="/js/chunk-vendors.js" type="module"></script><script defer="defer" src="/js/app.js" type="module"></script><link href="/css/app.css" rel="stylesheet"><script defer="defer" src="/js/chunk-vendors-legacy.js" nomodule></script><script defer="defer" src="/js/app-legacy.js" nomodule></script></head><body class="dark:bg-gray-900"><noscript><strong>Enable JavaScript to view this page.</strong></noscript><div id="app"></div></body></html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><script>window.config = {logo: "{{ .Logo }}"};</script><title>{{ .Title }}</title><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><script defer="defer" type="module" src="/js/chunk-vendors.js"></script><script defer="defer" type="module" src="/js/app.js"></script><link href="/css/app.css" rel="stylesheet"><script defer="defer" src="/js/chunk-vendors-legacy.js" nomodule></script><script defer="defer" src="/js/app-legacy.js" nomodule></script></head><body class="dark:bg-gray-900"><noscript><strong>Enable JavaScript to view this page.</strong></noscript><div id="app"></div></body></html>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user