Skip to content
This repository has been archived by the owner on Jul 19, 2023. It is now read-only.

Commit

Permalink
Add index page (#547)
Browse files Browse the repository at this point in the history
* Add index page

* Add some indexes

* Add swagger json link

* Remove new line
  • Loading branch information
Rustin170506 authored Mar 6, 2023
1 parent 79dcf1c commit 0898778
Show file tree
Hide file tree
Showing 12 changed files with 1,986 additions and 1 deletion.
98 changes: 98 additions & 0 deletions pkg/api/index.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// SPDX-License-Identifier: AGPL-3.0-only
// Provenance-includes-location: https://github.com/cortexproject/cortex/blob/master/pkg/api/handlers.go
// Provenance-includes-license: Apache-2.0
// Provenance-includes-copyright: The Cortex Authors.

package api

import (
"embed"
"html/template"
"net/http"
"path"
"sort"
"sync"
)

// List of weights to order link groups in the same order as weights are ordered here.
const (
serviceStatusWeight = iota
configWeight
RuntimeConfigWeight
DefaultWeight
memberlistWeight
dangerousWeight
OpenAPIDefinitionWeight
)

func NewIndexPageContent() *IndexPageContent {
return &IndexPageContent{}
}

// IndexPageContent is a map of sections to path -> description.
type IndexPageContent struct {
mu sync.Mutex

elements []IndexPageLinkGroup
}

type IndexPageLinkGroup struct {
weight int
Desc string
Links []IndexPageLink
}

type IndexPageLink struct {
Desc string
Path string
Dangerous bool
}

func (pc *IndexPageContent) AddLinks(weight int, groupDesc string, links []IndexPageLink) {
pc.mu.Lock()
defer pc.mu.Unlock()

pc.elements = append(pc.elements, IndexPageLinkGroup{weight: weight, Desc: groupDesc, Links: links})
}

func (pc *IndexPageContent) GetContent() []IndexPageLinkGroup {
pc.mu.Lock()
els := append([]IndexPageLinkGroup(nil), pc.elements...)
pc.mu.Unlock()

sort.Slice(els, func(i, j int) bool {
if els[i].weight != els[j].weight {
return els[i].weight < els[j].weight
}
return els[i].Desc < els[j].Desc
})

return els
}

//go:embed index.gohtml
var indexPageHTML string

type indexPageContents struct {
LinkGroups []IndexPageLinkGroup
}

//go:embed static
var StaticFiles embed.FS

func IndexHandler(httpPathPrefix string, content *IndexPageContent) http.HandlerFunc {
templ := template.New("main")
templ.Funcs(map[string]interface{}{
"AddPathPrefix": func(link string) string {
return path.Join(httpPathPrefix, link)
},
})
template.Must(templ.Parse(indexPageHTML))

return func(w http.ResponseWriter, r *http.Request) {
err := templ.Execute(w, indexPageContents{LinkGroups: content.GetContent()})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
}
41 changes: 41 additions & 0 deletions pkg/api/index.gohtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{{- /*gotype: github.com/grafana/phlare/pkg/api.indexPageContents */ -}}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">

<title>Grafana Phlare</title>

<link rel="stylesheet" href="{{ AddPathPrefix "/static/bootstrap-5.1.3.min.css" }}">
<link rel="stylesheet" href="{{ AddPathPrefix "/static/phlare-styles.css" }}">
<script src="{{ AddPathPrefix "/static/bootstrap-5.1.3.bundle.min.js" }}"></script>
</head>
<body>
<div class="d-flex flex-column container py-3">
<div class="header row border-bottom py-3 flex-column-reverse flex-sm-row">
<div class="col-12 col-sm-9 text-center text-sm-start">
<h1>Grafana Phlare Admin</h1>
</div>
<div class="col-12 col-sm-3 text-center text-sm-end mb-3 mb-sm-0">
<img alt="Phlare logo" class="phlare-brand" src="{{ AddPathPrefix "/static/phlare-logo.png" }}">
</div>
</div>
{{ range $i, $ := .LinkGroups }}
<div class="row service-row border-bottom py-3">
<div class="col-sm-3 text-start text-sm-end"><h2>{{ $.Desc }}</h2></div>
<div class="col-sm-9">
<ul class="my-0 list-unstyled">
{{ range $.Links }}
<li><a href="{{ AddPathPrefix .Path }}">{{ .Desc }}</a>
{{ if .Dangerous }}<span
class="badge bg-danger rounded-pill align-middle">Dangerous</span>{{ end }}</li>
{{ end }}
</ul>
</div>
</div>
{{ end }}
</div>
</body>
</html>
64 changes: 64 additions & 0 deletions pkg/api/index_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: AGPL-3.0-only
// Provenance-includes-location: https://github.com/cortexproject/cortex/blob/master/pkg/api/handlers_test.go
// Provenance-includes-license: Apache-2.0
// Provenance-includes-copyright: The Cortex Authors.

package api

import (
"net/http/httptest"
"strings"
"testing"

"github.com/stretchr/testify/require"
)

func TestIndexHandlerPrefix(t *testing.T) {
c := NewIndexPageContent()
c.AddLinks(DefaultWeight, "Store Gateway", []IndexPageLink{{Desc: "Ring status", Path: "/store-gateway/ring"}})

for _, tc := range []struct {
prefix string
toBeFound string
}{
{prefix: "", toBeFound: "<a href=\"/store-gateway/ring\">"},
{prefix: "/test", toBeFound: "<a href=\"/test/store-gateway/ring\">"},
// All the extra slashed are cleaned up in the result.
{prefix: "///test///", toBeFound: "<a href=\"/test/store-gateway/ring\">"},
} {
h := IndexHandler(tc.prefix, c)

req := httptest.NewRequest("GET", "/", nil)
resp := httptest.NewRecorder()

h.ServeHTTP(resp, req)

require.Equal(t, 200, resp.Code)
require.True(t, strings.Contains(resp.Body.String(), tc.toBeFound))
}
}

func TestIndexPageContent(t *testing.T) {
c := NewIndexPageContent()
c.AddLinks(DefaultWeight, "Some group", []IndexPageLink{
{Desc: "Some link", Path: "/store-gateway/ring"},
{Dangerous: true, Desc: "Boom!", Path: "/store-gateway/boom"},
})

h := IndexHandler("", c)

req := httptest.NewRequest("GET", "/", nil)
resp := httptest.NewRecorder()

h.ServeHTTP(resp, req)

require.Equal(t, 200, resp.Code)
require.True(t, strings.Contains(resp.Body.String(), "Some group"))
require.True(t, strings.Contains(resp.Body.String(), "Some link"))
require.True(t, strings.Contains(resp.Body.String(), "Dangerous"))
require.True(t, strings.Contains(resp.Body.String(), "Boom!"))
require.True(t, strings.Contains(resp.Body.String(), "Dangerous"))
require.True(t, strings.Contains(resp.Body.String(), "/store-gateway/ring"))
require.True(t, strings.Contains(resp.Body.String(), "/store-gateway/boom"))
require.False(t, strings.Contains(resp.Body.String(), "/compactor/ring"))
}
7 changes: 7 additions & 0 deletions pkg/api/static/bootstrap-5.1.3.bundle.min.js

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions pkg/api/static/bootstrap-5.1.3.min.css

Large diffs are not rendered by default.

Loading

0 comments on commit 0898778

Please sign in to comment.