Skip to content

Commit

Permalink
feat(ui): external links
Browse files Browse the repository at this point in the history
  • Loading branch information
Level8Broccoli committed Jan 13, 2025
1 parent c259364 commit ac116fd
Show file tree
Hide file tree
Showing 10 changed files with 64 additions and 21 deletions.
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>

0 comments on commit ac116fd

Please sign in to comment.