Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add root path and UI #308

Merged
merged 7 commits into from
Nov 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
51 changes: 37 additions & 14 deletions pkg/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ import (
"github.com/wundergraph/wundergraph/pkg/httpidletimeout"
"github.com/wundergraph/wundergraph/pkg/loadvariable"
"github.com/wundergraph/wundergraph/pkg/logging"
"github.com/wundergraph/wundergraph/pkg/node/nodetemplates"
"github.com/wundergraph/wundergraph/pkg/pool"
"github.com/wundergraph/wundergraph/pkg/validate"
"github.com/wundergraph/wundergraph/pkg/wgpb"
)

const (
rootEndpoint = "/"
healthCheckEndpoint = "/health"
)

Expand Down Expand Up @@ -319,6 +321,24 @@ func (n *Node) HandleGracefulShutdown(gracefulTimeoutInSeconds int) {
n.log.Info("WunderNode shutdown complete")
}

func (n *Node) GetHealth(w http.ResponseWriter, hooksClient *hooks.Client) HealthCheck {
serverStatus := "SKIP"
if n.options.hooksServerHealthCheck {
serverStatus = "OK"
ok := hooksClient.DoHealthCheckRequest(n.options.healthCheckTimeout)
if !ok {
w.WriteHeader(http.StatusServiceUnavailable)
serverStatus = "DEAD"
}
}

return HealthCheck{
ServerStatus: serverStatus,
NodeStatus: "OK",
BuildInfo: n.info,
}
}

func (n *Node) startServer(nodeConfig WunderNodeConfig) error {
logLevel := nodeConfig.Api.Options.Logging.Level
if n.options.enableDebugMode {
Expand Down Expand Up @@ -423,22 +443,25 @@ func (n *Node) startServer(nodeConfig WunderNodeConfig) error {
}
}()

router.Handle(healthCheckEndpoint, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
serverStatus := "SKIP"
if n.options.hooksServerHealthCheck {
serverStatus = "OK"
ok := hooksClient.DoHealthCheckRequest(n.options.healthCheckTimeout)
if !ok {
w.WriteHeader(http.StatusServiceUnavailable)
serverStatus = "DEAD"
}
router.Handle(rootEndpoint, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
template, err := nodetemplates.GetTemplateByPath(rootEndpoint)
health := n.GetHealth(w, hooksClient)
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
if err != nil {
n.log.Error("GetTemplateByPath", abstractlogger.Error(err))
w.WriteHeader(http.StatusInternalServerError)
return
}

health := HealthCheck{
ServerStatus: serverStatus,
NodeStatus: "OK",
BuildInfo: n.info,
w.Header().Set("Content-Type", "text/html")
w.WriteHeader(http.StatusOK)
if err := template.Execute(w, health); err != nil {
return
}
}))

router.Handle(healthCheckEndpoint, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
health := n.GetHealth(w, hooksClient)
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
_ = json.NewEncoder(w).Encode(health)
}))

Expand Down
156 changes: 156 additions & 0 deletions pkg/node/nodetemplates/resources/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
<html lang="en">
<head>
<title>WunderNode</title>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=block" rel="stylesheet">
<link rel="stylesheet" href="https://unpkg.com/flowbite@1.5.3/dist/flowbite.min.css"/>
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
theme: {
extend: {
fontFamily: {
sans: ["Inter"],
},
},
},
};
</script>
<style>
body {
background-size: 40px 40px;
background-image: radial-gradient(
circle,
#eedfdf 1px,
rgba(0, 0, 0, 0) 1px
);
}
</style>
</head>

<body
class="min-h-screen overflow-auto flex flex-col items-center justify-center container max-w-screen-md mx-auto px-4 py-12"
>
<h2 class="font-medium text-gray-600 text-xl">Powered by</h2>
<a
href="https://wundergraph.com"
class="mt-4 bg-gradient-to-r from-purple-500 to-pink-500 bg-clip-text text-5xl lg:text-6xl font-bold text-transparent"
>
WunderGraph
</a>
<div class="border w-full mt-12 lg:mt-24 rounded-md bg-white relative">
<span
class="absolute text-sm bg-white px-2 text-xs -top-2 ml-3 text-gray-500 rounded-sm"
>Status</span
>
<table class="w-full text-sm text-left table-fixed">
<tr class="border-b hover:bg-sky-50/50 transition-all">
<th class="pb-4 pt-5 px-6 text-sky-800 ">
<div class="flex gap-x-2 items-center">
<span>Server Status</span>
<div id="tooltip-light" role="tooltip"
class="flex flex-col absolute invisible z-10 py-2 px-3 text-xs text-gray-600 bg-white rounded-lg border border-gray-200 shadow-sm opacity-0 tooltip">
<div class="flex flex-col font-normal">
<span>OK : Server is reachable and healthy</span>
<span>SKIP : Node run without server</span>
<span>DEAD : Server is not reachable</span>
</div>
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<svg data-tooltip-target="tooltip-light" data-tooltip-style="light" xmlns="http://www.w3.org/2000/svg"
fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="stroke-gray-300 w-5 h-5">
<path stroke-linecap="round" stroke-linejoin="round"
d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z"/>
</svg>
</div>
</th>
<td class="text-gray-800 font-semibold">
<div class="flex gap-x-3 items-center">
<span>{{.ServerStatus}}</span>
{{if eq .ServerStatus "DEAD"}}
<span class="relative flex h-3 w-3">
<span class="relative inline-flex h-3 w-3 rounded-full bg-red-500"></span>
</span>
{{ else }}
<span class="relative flex h-3 w-3">
<span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-green-500 opacity-75"></span>
<span class="relative inline-flex h-3 w-3 rounded-full bg-green-500"></span>
</span>
{{ end }}
</div>
</td>
</tr>
<tr class="hover:bg-sky-50/50 transition-all">
<th class="py-4 px-6 text-sky-800">Node Status</th>
<td class="text-gray-800 font-semibold">{{.NodeStatus}}</td>
</tr>
</table>
</div>
<div class="border w-full mt-12 rounded-md bg-white relative">
<span
class="absolute text-sm bg-white px-2 text-xs -top-2 ml-3 text-gray-500 rounded-sm"
>Build Info</span
>
<table class="w-full text-sm text-left table-fixed">
<tr class="border-b hover:bg-sky-50/50 transition-all">
<th class="pb-4 pt-5 px-6 text-sky-800">Version</th>
<td class="text-gray-800 font-semibold">{{.BuildInfo.Version}}</td>
</tr>
<tr class="border-b hover:bg-sky-50/50 transition-all">
<th class="py-4 px-6 text-sky-800">Commit</th>
<td class="text-gray-800 font-semibold">
<a class="flex items-center gap-x-2 hover:text-blue-500 group hover:underline"
href="https://github.com/wundergraph/wundergraph/commit/{{.BuildInfo.Commit}}" target="_blank"><span>{{.BuildInfo.Commit}}</span>
<svg class="stroke-gray-300 group-hover:stroke-blue-500 w-4 h-4" xmlns="http://www.w3.org/2000/svg"
fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round"
d="M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25"/>
</svg>
</a>
</td>
</tr>
<tr class="border-b hover:bg-sky-50/50 transition-all">
<th class="py-4 px-6 text-sky-800">Date</th>
<td class="text-gray-800 font-semibold">{{.BuildInfo.Date}}</td>
</tr>
<tr class="hover:bg-sky-50/50 transition-all">
<th class="py-4 px-6 text-sky-800">Built By</th>
<td class="text-gray-800 font-semibold">{{.BuildInfo.BuiltBy}}</td>
</tr>
</table>
</div>
<a
href="https://docs.wundergraph.com"
target="_blank"
class="flex flex-col border w-full rounded-md py-2 px-4 group hover:border-sky-500 bg-white mt-12"
>
<p
class="flex items-center gap-x-2 font-medium group-hover:gap-x-3 transition-all"
>
<span>Docs</span
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-4 h-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M13.5 4.5L21 12m0 0l-7.5 7.5M21 12H3"
/>
</svg>
</p>
<p class="text-gray-600 text-sm mt-2">
Learn how to use and further empower your WunderNode.
</p>
</a>
<script src="https://unpkg.com/flowbite@1.5.3/dist/flowbite.js"></script>
</body>
</html>
28 changes: 28 additions & 0 deletions pkg/node/nodetemplates/template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package nodetemplates

import (
"embed"
"fmt"
"html/template"
)

var (
//go:embed resources
res embed.FS
pages = map[string]string{
"/": "resources/index.html",
}
)

func GetTemplateByPath(path string) (*template.Template, error) {
page, ok := pages[path]
if !ok {
return nil, fmt.Errorf("page not found: %s", path)
}
tpl, err := template.ParseFS(res, page)
if err != nil {
return nil, err
}

return tpl, nil
}