Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions config/endpoint/status.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package endpoint

import "github.com/TwiN/gatus/v5/config/endpoint/ui"

// Status contains the evaluation Results of an Endpoint
type Status struct {
// Name of the endpoint
Expand All @@ -23,16 +25,19 @@ type Status struct {
//
// To retrieve the uptime between two time, use store.GetUptimeByKey.
Uptime *Uptime `json:"-"`

UiConfig *ui.Config `json:"uiConfig,omitempty"`
}

// NewStatus creates a new Status
func NewStatus(group, name string) *Status {
func NewStatus(group, name string, uiConfig ui.Config) *Status {
return &Status{
Name: name,
Group: group,
Key: ConvertGroupAndEndpointNameToKey(group, name),
Results: make([]*Result, 0),
Events: make([]*Event, 0),
Uptime: NewUptime(),
Name: name,
Group: group,
Key: ConvertGroupAndEndpointNameToKey(group, name),
Results: make([]*Result, 0),
Events: make([]*Event, 0),
Uptime: NewUptime(),
UiConfig: &uiConfig,
}
}
2 changes: 1 addition & 1 deletion config/endpoint/status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (

func TestNewEndpointStatus(t *testing.T) {
ep := &Endpoint{Name: "name", Group: "group"}
status := NewStatus(ep.Group, ep.Name)
status := NewStatus(ep.Group, ep.Name, *ep.UIConfig)
if status.Name != ep.Name {
t.Errorf("expected %s, got %s", ep.Name, status.Name)
}
Expand Down
9 changes: 9 additions & 0 deletions config/endpoint/ui/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ type Config struct {

// Badge is the configuration for the badges generated
Badge *Badge `yaml:"badge"`

Menu []MenuItem `yaml:"menu"`
}

type MenuItem struct {
Name string `yaml:"name"`
Type string `yaml:"type"`
Value string `yaml:"value"`
}

type Badge struct {
Expand Down Expand Up @@ -61,5 +69,6 @@ func GetDefaultConfig() *Config {
Thresholds: []int{50, 200, 300, 500, 750},
},
},
Menu: []MenuItem{},
}
}
2 changes: 1 addition & 1 deletion storage/store/memory/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ func (s *Store) Insert(ep *endpoint.Endpoint, result *endpoint.Result) error {
s.Lock()
status, exists := s.cache.Get(key)
if !exists {
status = endpoint.NewStatus(ep.Group, ep.Name)
status = endpoint.NewStatus(ep.Group, ep.Name, *ep.UIConfig)
status.(*endpoint.Status).Events = append(status.(*endpoint.Status).Events, &endpoint.Event{
Type: endpoint.EventStart,
Timestamp: time.Now(),
Expand Down
4 changes: 2 additions & 2 deletions storage/store/memory/uptime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (

func TestProcessUptimeAfterResult(t *testing.T) {
ep := &endpoint.Endpoint{Name: "name", Group: "group"}
status := endpoint.NewStatus(ep.Group, ep.Name)
status := endpoint.NewStatus(ep.Group, ep.Name, *ep.UIConfig)
uptime := status.Uptime

now := time.Now()
Expand Down Expand Up @@ -44,7 +44,7 @@ func TestProcessUptimeAfterResult(t *testing.T) {

func TestAddResultUptimeIsCleaningUpAfterItself(t *testing.T) {
ep := &endpoint.Endpoint{Name: "name", Group: "group"}
status := endpoint.NewStatus(ep.Group, ep.Name)
status := endpoint.NewStatus(ep.Group, ep.Name, *ep.UIConfig)
now := time.Now()
now = time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, now.Location())
// Start 12 days ago
Expand Down
9 changes: 5 additions & 4 deletions storage/store/memory/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import (
// within the range defined by the page and pageSize parameters
func ShallowCopyEndpointStatus(ss *endpoint.Status, params *paging.EndpointStatusParams) *endpoint.Status {
shallowCopy := &endpoint.Status{
Name: ss.Name,
Group: ss.Group,
Key: ss.Key,
Uptime: endpoint.NewUptime(),
Name: ss.Name,
Group: ss.Group,
Key: ss.Key,
Uptime: endpoint.NewUptime(),
UiConfig: ss.UiConfig,
}
numberOfResults := len(ss.Results)
resultsStart, resultsEnd := getStartAndEndIndex(numberOfResults, params.ResultsPage, params.ResultsPageSize)
Expand Down
2 changes: 1 addition & 1 deletion storage/store/memory/util_bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (

func BenchmarkShallowCopyEndpointStatus(b *testing.B) {
ep := &testEndpoint
status := endpoint.NewStatus(ep.Group, ep.Name)
status := endpoint.NewStatus(ep.Group, ep.Name, *ep.UIConfig)
for i := 0; i < common.MaximumNumberOfResults; i++ {
AddResult(status, &testSuccessfulResult)
}
Expand Down
4 changes: 2 additions & 2 deletions storage/store/memory/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (

func TestAddResult(t *testing.T) {
ep := &endpoint.Endpoint{Name: "name", Group: "group"}
endpointStatus := endpoint.NewStatus(ep.Group, ep.Name)
endpointStatus := endpoint.NewStatus(ep.Group, ep.Name, *ep.UIConfig)
for i := 0; i < (common.MaximumNumberOfResults+common.MaximumNumberOfEvents)*2; i++ {
AddResult(endpointStatus, &endpoint.Result{Success: i%2 == 0, Timestamp: time.Now()})
}
Expand All @@ -27,7 +27,7 @@ func TestAddResult(t *testing.T) {

func TestShallowCopyEndpointStatus(t *testing.T) {
ep := &endpoint.Endpoint{Name: "name", Group: "group"}
endpointStatus := endpoint.NewStatus(ep.Group, ep.Name)
endpointStatus := endpoint.NewStatus(ep.Group, ep.Name, *ep.UIConfig)
ts := time.Now().Add(-25 * time.Hour)
for i := 0; i < 25; i++ {
AddResult(endpointStatus, &endpoint.Result{Success: i%2 == 0, Timestamp: ts})
Expand Down
3 changes: 2 additions & 1 deletion storage/store/sql/sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/TwiN/gatus/v5/alerting/alert"
"github.com/TwiN/gatus/v5/config/endpoint"
"github.com/TwiN/gatus/v5/config/endpoint/ui"
"github.com/TwiN/gatus/v5/storage/store/common"
"github.com/TwiN/gatus/v5/storage/store/common/paging"
"github.com/TwiN/gocache/v2"
Expand Down Expand Up @@ -652,7 +653,7 @@ func (s *Store) getEndpointStatusByKey(tx *sql.Tx, key string, parameters *pagin
if err != nil {
return nil, err
}
endpointStatus := endpoint.NewStatus(group, endpointName)
endpointStatus := endpoint.NewStatus(group, endpointName, *ui.GetDefaultConfig())
if parameters.EventsPageSize > 0 {
if endpointStatus.Events, err = s.getEndpointEventsByEndpointID(tx, endpointID, parameters.EventsPage, parameters.EventsPageSize); err != nil {
logr.Errorf("[sql.getEndpointStatusByKey] Failed to retrieve events for key=%s: %s", key, err.Error())
Expand Down
31 changes: 29 additions & 2 deletions web/app/src/components/Endpoint.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,23 @@
{{ data.name }}
</router-link>
<span v-if="data.results && data.results.length && data.results[data.results.length - 1].hostname" class='text-gray-500 font-light'> | {{ data.results[data.results.length - 1].hostname }}</span>
<div v-if="data.uiConfig.Menu.length > 0" class="relative inline-block ml-2">
<div>
<button type="button" class="relative top-1 inline-flex w-full justify-center gap-x-1.5 rounded-md bg-white pr-1 py-0 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50" id="menu-button" aria-expanded="true" aria-haspopup="true" @click="toggleMenu">
<svg class="-mr-1 size-5 text-gray-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" data-slot="icon">
<path fill-rule="evenodd" d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" />
</svg>
</button>
</div>
<div v-if="menuOpen" class="absolute left-0 z-10 mt-2 w-56 origin-top-left rounded-md bg-white shadow-lg ring-1 ring-black/5 focus:outline-none" role="menu" aria-orientation="vertical" aria-labelledby="menu-button" tabindex="-1">
<div class="py-1" role="none">
<template v-for="item in data.uiConfig.Menu" :key="item.Name">
<a :href="item.Value" v-if="item.Type === 'link'" class="block px-4 py-2 text-sm text-gray-700" role="menuitem" tabindex="-1">{{item.Name}}</a>
<span v-else class="block px-4 py-2 text-sm text-gray-700">{{item.Name}}</span>
</template>
</div>
</div>
</div>
</div>
<div class='w-1/4 text-right'>
<span class='font-light overflow-x-hidden cursor-pointer select-none hover:text-gray-500' v-if="data.results && data.results.length" @click="toggleShowAverageResponseTime" :title="showAverageResponseTime ? 'Average response time' : 'Minimum and maximum response time'">
Expand Down Expand Up @@ -104,6 +121,9 @@ export default {
},
toggleShowAverageResponseTime() {
this.$emit('toggleShowAverageResponseTime');
},
toggleMenu() {
this.menuOpen = !this.menuOpen;
}
},
watch: {
Expand All @@ -118,7 +138,8 @@ export default {
return {
minResponseTime: 0,
maxResponseTime: 0,
averageResponseTime: 0
averageResponseTime: 0,
menuOpen: false,
}
}
}
Expand Down Expand Up @@ -183,4 +204,10 @@ export default {
white-space: pre;
}
}
</style>

.size-5 {
--tw-spacing: 0.25rem;
width: calc(var(--tw-spacing) * 5);
height: calc(var(--tw-spacing) * 5);
}
</style>