mirror of
https://github.com/mentos1386/zdravko.git
synced 2024-11-21 23:33:34 +00:00
feat: ui, logger and other general improvements
This commit is contained in:
parent
4afb709b87
commit
b68c348f2a
10 changed files with 143 additions and 132 deletions
|
@ -2,7 +2,7 @@ package main
|
|||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
|
@ -20,7 +20,19 @@ type StartableAndStoppable interface {
|
|||
Stop() error
|
||||
}
|
||||
|
||||
func setupLogger() {
|
||||
opts := &slog.HandlerOptions{
|
||||
Level: slog.LevelDebug,
|
||||
}
|
||||
|
||||
logger := slog.New(slog.NewTextHandler(os.Stdout, opts))
|
||||
|
||||
slog.SetDefault(logger)
|
||||
}
|
||||
|
||||
func main() {
|
||||
setupLogger()
|
||||
|
||||
var startServer bool
|
||||
var startWorker bool
|
||||
var startTemporal bool
|
||||
|
@ -28,47 +40,47 @@ func main() {
|
|||
flag.BoolVar(&startServer, "server", false, "Start the server")
|
||||
flag.BoolVar(&startWorker, "worker", false, "Start the worker")
|
||||
flag.BoolVar(&startTemporal, "temporal", false, "Start the temporal")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
println("Starting zdravko...")
|
||||
println("Server: ", startServer)
|
||||
println("Worker: ", startWorker)
|
||||
println("Temporal: ", startTemporal)
|
||||
slog.Info("Starting zdravko...", "server", startServer, "worker", startWorker, "temporal", startTemporal)
|
||||
|
||||
if !startServer && !startWorker && !startTemporal {
|
||||
log.Fatal("At least one of the following must be set: --server, --worker, --temporal")
|
||||
slog.Error("At least one of the following must be set: --server, --worker, --temporal")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var servers [3]StartableAndStoppable
|
||||
var wg sync.WaitGroup
|
||||
|
||||
if startTemporal {
|
||||
log.Println("Setting up Temporal")
|
||||
slog.Info("Setting up Temporal")
|
||||
cfg := config.NewTemporalConfig()
|
||||
temporal, err := temporal.NewTemporal(cfg)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to create temporal: %v", err)
|
||||
slog.Error("Unable to create temporal", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
servers[0] = temporal
|
||||
}
|
||||
|
||||
if startServer {
|
||||
log.Println("Setting up Server")
|
||||
slog.Info("Setting up Server")
|
||||
cfg := config.NewServerConfig()
|
||||
server, err := server.NewServer(cfg)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to create server: %v", err)
|
||||
slog.Error("Unable to create server", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
servers[1] = server
|
||||
}
|
||||
|
||||
if startWorker {
|
||||
log.Println("Setting up Worker")
|
||||
slog.Info("Setting up Worker")
|
||||
cfg := config.NewWorkerConfig()
|
||||
worker, err := worker.NewWorker(cfg)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to create worker: %v", err)
|
||||
slog.Error("Unable to create worker", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
servers[2] = worker
|
||||
}
|
||||
|
@ -79,13 +91,14 @@ func main() {
|
|||
continue
|
||||
}
|
||||
|
||||
println("Starting", srv.Name())
|
||||
slog.Info("Starting", "name", srv.Name())
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
err := srv.Start()
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to start %s: %v", srv.Name(), err)
|
||||
slog.Error("Unable to start", "name", srv.Name(), "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
@ -94,16 +107,16 @@ func main() {
|
|||
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
|
||||
go func() {
|
||||
for sig := range c {
|
||||
log.Printf("Received signal: %v", sig)
|
||||
slog.Info("Received signal", "signal", sig)
|
||||
for _, srv := range servers {
|
||||
if srv == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
println("Stopping", srv.Name())
|
||||
slog.Info("Stopping", "name", srv.Name())
|
||||
err := srv.Stop()
|
||||
if err != nil {
|
||||
log.Printf("Unable to stop server %s: %v", srv.Name(), err)
|
||||
slog.Error("Unable to stop server", "name", srv.Name(), "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ func NewServer(cfg *config.ServerConfig) (*Server, error) {
|
|||
return &Server{
|
||||
cfg: cfg,
|
||||
echo: echo.New(),
|
||||
logger: slog.Default().WithGroup("server"),
|
||||
logger: slog.Default(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,12 @@ func (s *Server) Start() error {
|
|||
|
||||
s.worker = NewWorker(temporalClient, s.cfg, s.logger, sqliteDb, kvStore)
|
||||
|
||||
s.echo.Renderer = templates.NewTemplates()
|
||||
templates, err := templates.NewTemplates(s.logger)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create templates")
|
||||
}
|
||||
s.echo.Renderer = templates
|
||||
|
||||
s.echo.Use(middleware.Logger())
|
||||
s.echo.Use(middleware.Recover())
|
||||
s.echo.Use(middleware.Secure())
|
||||
|
|
|
@ -66,7 +66,7 @@ type Worker struct {
|
|||
func NewWorker(cfg *config.WorkerConfig) (*Worker, error) {
|
||||
return &Worker{
|
||||
cfg: cfg,
|
||||
logger: slog.Default().WithGroup("worker"),
|
||||
logger: slog.Default(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -82,18 +82,18 @@ code {
|
|||
background: repeating-linear-gradient(
|
||||
0deg,
|
||||
var(--color-orange-300),
|
||||
var(--color-orange-300) 10%,
|
||||
var(--color-orange-400) 10%,
|
||||
var(--color-orange-400) 20%
|
||||
var(--color-orange-300) 33%,
|
||||
var(--color-orange-400) 33%,
|
||||
var(--color-orange-400) 66%
|
||||
);
|
||||
}
|
||||
#page-index .history.degraded:hover > .bar {
|
||||
background: repeating-linear-gradient(
|
||||
0deg,
|
||||
var(--color-orange-400),
|
||||
var(--color-orange-400) 10%,
|
||||
var(--color-orange-500) 10%,
|
||||
var(--color-orange-500) 20%
|
||||
var(--color-orange-400) 33%,
|
||||
var(--color-orange-500) 33%,
|
||||
var(--color-orange-500) 66%
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1020,10 +1020,6 @@ video {
|
|||
height: min-content;
|
||||
}
|
||||
|
||||
.w-12 {
|
||||
width: 3rem;
|
||||
}
|
||||
|
||||
.w-20 {
|
||||
width: 5rem;
|
||||
}
|
||||
|
@ -1386,16 +1382,16 @@ video {
|
|||
line-height: 1.5rem;
|
||||
}
|
||||
|
||||
.text-lg {
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.75rem;
|
||||
}
|
||||
|
||||
.text-sm {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
|
||||
.text-xl {
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.75rem;
|
||||
}
|
||||
|
||||
.text-xs {
|
||||
font-size: 0.75rem;
|
||||
line-height: 1rem;
|
||||
|
@ -1514,6 +1510,11 @@ video {
|
|||
color: rgb(107 33 168 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-red-500 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(239 68 68 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-red-600 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(220 38 38 / var(--tw-text-opacity));
|
||||
|
@ -1743,9 +1744,9 @@ code {
|
|||
background: repeating-linear-gradient(
|
||||
0deg,
|
||||
var(--color-orange-300),
|
||||
var(--color-orange-300) 10%,
|
||||
var(--color-orange-400) 10%,
|
||||
var(--color-orange-400) 20%
|
||||
var(--color-orange-300) 33%,
|
||||
var(--color-orange-400) 33%,
|
||||
var(--color-orange-400) 66%
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1753,9 +1754,9 @@ code {
|
|||
background: repeating-linear-gradient(
|
||||
0deg,
|
||||
var(--color-orange-400),
|
||||
var(--color-orange-400) 10%,
|
||||
var(--color-orange-500) 10%,
|
||||
var(--color-orange-500) 20%
|
||||
var(--color-orange-400) 33%,
|
||||
var(--color-orange-500) 33%,
|
||||
var(--color-orange-500) 66%
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2133,8 +2134,8 @@ code {
|
|||
justify-content: center;
|
||||
}
|
||||
|
||||
.sm\:justify-evenly {
|
||||
justify-content: space-evenly;
|
||||
.sm\:justify-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.sm\:px-8 {
|
||||
|
|
|
@ -17,8 +17,12 @@
|
|||
<link rel="stylesheet" href="/static/css/tailwind.css" />
|
||||
</head>
|
||||
<body class="bg-gray-100 flex flex-col">
|
||||
<header class="flex flex-col sm:flex-row items-center sm:justify-evenly p-4 gap-2">
|
||||
<a href="/" class="text-2xl font-bold">zdravko.mnts.dev</a>
|
||||
<header
|
||||
class="container max-w-screen-md flex flex-col sm:flex-row items-center sm:justify-between p-4 gap-2"
|
||||
>
|
||||
<a href="/" class="hover:underline text-2xl font-bold"
|
||||
>zdravko.mnts.dev</a
|
||||
>
|
||||
<nav class="navbar flex sm:flex-row flex-col flex-wrap space-x-2 gap-1">
|
||||
{{ range .Navbar }}
|
||||
<a
|
||||
|
@ -31,21 +35,28 @@
|
|||
</a>
|
||||
{{ end }}
|
||||
</nav>
|
||||
<img
|
||||
src="https://avatars.githubusercontent.com/u/1910649?v=4"
|
||||
alt="Profile Image"
|
||||
class="rounded-full w-12 h-12"
|
||||
></img>
|
||||
</header>
|
||||
{{ template "main" . }}
|
||||
<div class="container mx-auto">
|
||||
<footer class="text-center text-gray-600 text-xs mt-8 mb-4">
|
||||
© {{ Now.UTC.Year }} Zdravko -
|
||||
Powered by
|
||||
<a class="hover:underline" href="https://zdravko.mnts.dev">Zdravko</a>
|
||||
-
|
||||
<a
|
||||
class="hover:underline"
|
||||
href="https://github.com/mentos1386/zdravko"
|
||||
>Open Source</a
|
||||
>
|
||||
- Made with <span class="text-red-500">❤</span> by
|
||||
<a class="hover:underline" href="https://mnts.dev/about"
|
||||
>Mentos1386</a
|
||||
>
|
||||
and
|
||||
<a
|
||||
class="hover:underline"
|
||||
href="https://github.com/mentos1386/zdravko/graphs/contributors"
|
||||
>others</a
|
||||
>.
|
||||
</footer>
|
||||
</div>
|
||||
<script src="/static/js/htmx.min.js"></script>
|
||||
|
|
0
web/templates/components/footer.tmpl
Normal file
0
web/templates/components/footer.tmpl
Normal file
0
web/templates/components/head.tmpl
Normal file
0
web/templates/components/head.tmpl
Normal file
|
@ -1,4 +1,22 @@
|
|||
{{ define "main" }}
|
||||
{{ $outcomeText := "All services are online." }}
|
||||
{{ $outcomeIcon := "check" }}
|
||||
{{ $outcomeColor := "bg-green-300" }}
|
||||
{{ if eq .Outcome "DOWN" }}
|
||||
{{ $outcomeText = "Some services are down." }}
|
||||
{{ $outcomeIcon = "alert-circle" }}
|
||||
{{ $outcomeColor = "bg-red-300" }}
|
||||
{{ else if eq .Outcome "DEGRADED" }}
|
||||
{{ $outcomeText = "Some services are degraded." }}
|
||||
{{ $outcomeIcon = "alert-triangle" }}
|
||||
{{ $outcomeColor = "bg-orange-300" }}
|
||||
{{ else }}
|
||||
{{ $outcomeText = "We are unable to determine current status." }}
|
||||
{{ $outcomeIcon = "help-circle" }}
|
||||
{{ $outcomeColor = "bg-gray-300" }}
|
||||
{{ end }}
|
||||
|
||||
|
||||
<div
|
||||
id="page-index"
|
||||
class="container max-w-screen-md flex flex-col mt-20 gap-20"
|
||||
|
@ -32,61 +50,21 @@
|
|||
</div>
|
||||
</section>
|
||||
{{ else }}
|
||||
{{ if eq .Outcome "HEALTHY" }}
|
||||
<div class="flex flex-col items-center">
|
||||
<svg
|
||||
class="feather h-20 w-20 rounded-full bg-green-300 p-4 overflow-visible"
|
||||
>
|
||||
<use href="/static/icons/feather-sprite.svg#check" />
|
||||
</svg>
|
||||
<h1 class="text-gray-700 mt-4 text-lg">All services are online</h1>
|
||||
<p class="text-gray-700 text-sm">
|
||||
Last updated on
|
||||
{{ Now.UTC.Format "Jan 02 at 15:04 MST" }}
|
||||
</p>
|
||||
</div>
|
||||
{{ else if eq .Outcome "UNKNOWN" }}
|
||||
<div class="flex flex-col items-center">
|
||||
<svg
|
||||
class="feather h-20 w-20 rounded-full bg-gray-300 p-4 overflow-visible"
|
||||
>
|
||||
<use href="/static/icons/feather-sprite.svg#help-circle" />
|
||||
</svg>
|
||||
<h1 class="text-gray-700 mt-4 text-lg">
|
||||
We are unable to determine current status
|
||||
</h1>
|
||||
<p class="text-gray-700 text-sm">
|
||||
Last updated on
|
||||
{{ Now.UTC.Format "Jan 02 at 15:04 MST" }}
|
||||
</p>
|
||||
</div>
|
||||
{{ else if eq .Outcome "DOWN" }}
|
||||
<div class="flex flex-col items-center">
|
||||
<svg
|
||||
class="feather h-20 w-20 rounded-full bg-red-300 p-4 overflow-visible"
|
||||
>
|
||||
<use href="/static/icons/feather-sprite.svg#alert-circle" />
|
||||
</svg>
|
||||
<h1 class="text-gray-700 mt-4 text-lg">Some services are down</h1>
|
||||
<p class="text-gray-700 text-sm">
|
||||
Last updated on
|
||||
{{ Now.UTC.Format "Jan 02 at 15:04 MST" }}
|
||||
</p>
|
||||
</div>
|
||||
{{ else if eq .Outcome "DEGRADED" }}
|
||||
<div class="flex flex-col items-center">
|
||||
<svg
|
||||
class="feather h-20 w-20 rounded-full bg-orange-300 p-4 overflow-visible"
|
||||
>
|
||||
<use href="/static/icons/feather-sprite.svg#alert-triangle" />
|
||||
</svg>
|
||||
<h1 class="text-gray-700 mt-4 text-lg">Degraded services</h1>
|
||||
<p class="text-gray-700 text-sm">
|
||||
Last updated on
|
||||
{{ Now.UTC.Format "Jan 02 at 15:04 MST" }}
|
||||
</p>
|
||||
</div>
|
||||
{{ end }}
|
||||
<div class="flex flex-col items-center">
|
||||
<svg
|
||||
class="feather h-20 w-20 rounded-full {{ $outcomeColor }} p-4 overflow-visible"
|
||||
>
|
||||
<use href="/static/icons/feather-sprite.svg#{{ $outcomeIcon }}" />
|
||||
</svg>
|
||||
<h1 class="text-gray-800 mt-4 text-xl font-bold">
|
||||
{{ $outcomeText }}
|
||||
</h1>
|
||||
<p class="text-gray-700 text-sm">
|
||||
Last updated on
|
||||
{{ Now.UTC.Format "Jan 02 at 15:04 MST" }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="targets flex flex-col gap-4">
|
||||
<div
|
||||
class="inline-flex gap-1 justify-center md:justify-end time-range"
|
||||
|
|
|
@ -3,7 +3,8 @@ package templates
|
|||
import (
|
||||
"embed"
|
||||
"io"
|
||||
"log"
|
||||
"io/fs"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
@ -18,6 +19,7 @@ var templates embed.FS
|
|||
const base = "components/base.tmpl"
|
||||
|
||||
type Templates struct {
|
||||
logger *slog.Logger
|
||||
templates map[string]*template.Template
|
||||
}
|
||||
|
||||
|
@ -52,40 +54,41 @@ func loadSettings(files ...string) *template.Template {
|
|||
return load(files...)
|
||||
}
|
||||
|
||||
func NewTemplates() *Templates {
|
||||
return &Templates{
|
||||
templates: map[string]*template.Template{
|
||||
"404.tmpl": load("pages/404.tmpl"),
|
||||
"index.tmpl": load("pages/index.tmpl"),
|
||||
"incidents.tmpl": load("pages/incidents.tmpl"),
|
||||
"settings_home.tmpl": loadSettings("pages/settings_home.tmpl"),
|
||||
"settings_triggers.tmpl": loadSettings("pages/settings_triggers.tmpl"),
|
||||
"settings_triggers_create.tmpl": loadSettings("pages/settings_triggers_create.tmpl"),
|
||||
"settings_triggers_describe.tmpl": loadSettings("pages/settings_triggers_describe.tmpl"),
|
||||
"settings_targets.tmpl": loadSettings("pages/settings_targets.tmpl"),
|
||||
"settings_targets_create.tmpl": loadSettings("pages/settings_targets_create.tmpl"),
|
||||
"settings_targets_describe.tmpl": loadSettings("pages/settings_targets_describe.tmpl"),
|
||||
"settings_incidents.tmpl": loadSettings("pages/settings_incidents.tmpl"),
|
||||
"settings_notifications.tmpl": loadSettings("pages/settings_notifications.tmpl"),
|
||||
"settings_worker_groups.tmpl": loadSettings("pages/settings_worker_groups.tmpl"),
|
||||
"settings_worker_groups_create.tmpl": loadSettings("pages/settings_worker_groups_create.tmpl"),
|
||||
"settings_worker_groups_describe.tmpl": loadSettings("pages/settings_worker_groups_describe.tmpl"),
|
||||
"settings_checks.tmpl": loadSettings("pages/settings_checks.tmpl"),
|
||||
"settings_checks_create.tmpl": loadSettings("pages/settings_checks_create.tmpl"),
|
||||
"settings_checks_describe.tmpl": loadSettings("pages/settings_checks_describe.tmpl"),
|
||||
},
|
||||
func NewTemplates(logger *slog.Logger) (*Templates, error) {
|
||||
t := Templates{
|
||||
logger: logger,
|
||||
templates: map[string]*template.Template{},
|
||||
}
|
||||
|
||||
err := fs.WalkDir(templates, "pages", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.Contains(path, ".tmpl") {
|
||||
t.logger.Debug("Loading template", "path", path)
|
||||
pathWithoutPrefix := strings.TrimPrefix(path, "pages/")
|
||||
|
||||
if strings.Contains(path, "settings") {
|
||||
t.templates[pathWithoutPrefix] = loadSettings(path)
|
||||
} else {
|
||||
t.templates[pathWithoutPrefix] = load(path)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return &t, err
|
||||
}
|
||||
|
||||
func (t *Templates) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
|
||||
if t.templates[name] == nil {
|
||||
log.Printf("template not found: %s", name)
|
||||
t.logger.Error("template not found", "template", name)
|
||||
return echo.ErrNotFound
|
||||
}
|
||||
|
||||
err := t.templates[name].ExecuteTemplate(w, "base", data)
|
||||
if err != nil {
|
||||
log.Printf("error rendering template: %s", err)
|
||||
t.logger.Error("error rendering template", "template", err)
|
||||
}
|
||||
|
||||
return err
|
||||
|
|
Loading…
Reference in a new issue