Skip to content

Commit 6fb8f10

Browse files
committed
Merge pull request 'Built #25 new template builder' (#26) from dev/25-template-builder into v0.3.0
Reviewed-on: https://git.cer.sh/Axodouble/QUptime/pulls/26
2 parents 77f31e7 + 5c3a005 commit 6fb8f10

3 files changed

Lines changed: 853 additions & 0 deletions

File tree

internal/cli/builder.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package cli
2+
3+
import (
4+
_ "embed"
5+
"encoding/json"
6+
"fmt"
7+
"os"
8+
"path/filepath"
9+
"strings"
10+
11+
"github.com/spf13/cobra"
12+
13+
"git.cer.sh/axodouble/quptime/internal/alerts"
14+
)
15+
16+
//go:embed builder.html
17+
var builderHTML string
18+
19+
// addBuilderCmd registers `qu builder`, which materialises a standalone
20+
// HTML page that operators can use to compose alert message templates
21+
// interactively. The page is self-contained — no network calls, no
22+
// external assets — so it can be opened straight from disk or copied
23+
// to a workstation that has no qu binary installed.
24+
//
25+
// The default templates baked into the page are injected from
26+
// internal/alerts at build (actually at command-run) time, so the
27+
// builder always offers the same starting points the daemon would
28+
// render itself.
29+
func addBuilderCmd(root *cobra.Command) {
30+
var output string
31+
cmd := &cobra.Command{
32+
Use: "builder",
33+
Short: "Generate a standalone HTML alert-template builder",
34+
Long: `Write a self-contained HTML page that helps you compose subject and
35+
body templates for ` + "`qu alert`" + ` channels. The page bundles a
36+
drag-and-drop variable palette, a live preview rendered against
37+
editable sample data, and the per-check-type defaults you can use as a
38+
starting point. It runs entirely offline once written to disk.
39+
40+
Pass ` + "`-o -`" + ` to write the HTML to stdout instead of a file.`,
41+
RunE: func(cmd *cobra.Command, args []string) error {
42+
rendered, err := renderBuilderHTML()
43+
if err != nil {
44+
return err
45+
}
46+
if output == "-" {
47+
_, err := cmd.OutOrStdout().Write([]byte(rendered))
48+
return err
49+
}
50+
if err := os.WriteFile(output, []byte(rendered), 0o644); err != nil {
51+
return fmt.Errorf("write %s: %w", output, err)
52+
}
53+
abs, _ := filepath.Abs(output)
54+
fmt.Fprintf(cmd.OutOrStdout(), "wrote %s\n", abs)
55+
fmt.Fprintf(cmd.OutOrStdout(), "open it in a browser to start composing templates.\n")
56+
return nil
57+
},
58+
}
59+
cmd.Flags().StringVarP(&output, "output", "o", "quptime-template-builder.html",
60+
"output path for the generated HTML (use - for stdout)")
61+
root.AddCommand(cmd)
62+
}
63+
64+
// renderBuilderHTML substitutes the placeholder tokens in builder.html
65+
// with JSON-encoded copies of the current default templates. JSON
66+
// encoding is exactly what we want for JS string literals — it handles
67+
// the embedded backticks, backslashes, and newlines in the Go template
68+
// constants without further escaping.
69+
func renderBuilderHTML() (string, error) {
70+
subs := map[string]string{
71+
"__QU_DEFAULT_HTTP_SUBJECT__": alerts.DefaultSubjectHTTP,
72+
"__QU_DEFAULT_HTTP_BODY__": alerts.DefaultBodyHTTP,
73+
"__QU_DEFAULT_TLS_SUBJECT__": alerts.DefaultSubjectTLS,
74+
"__QU_DEFAULT_TLS_BODY__": alerts.DefaultBodyTLS,
75+
"__QU_DEFAULT_TCP_SUBJECT__": alerts.DefaultSubjectTCP,
76+
"__QU_DEFAULT_TCP_BODY__": alerts.DefaultBodyTCP,
77+
"__QU_DEFAULT_ICMP_SUBJECT__": alerts.DefaultSubjectICMP,
78+
"__QU_DEFAULT_ICMP_BODY__": alerts.DefaultBodyICMP,
79+
"__QU_DEFAULT_DNS_SUBJECT__": alerts.DefaultSubjectDNS,
80+
"__QU_DEFAULT_DNS_BODY__": alerts.DefaultBodyDNS,
81+
"__QU_DEFAULT_GENERIC_SUBJECT__": alerts.DefaultSubjectGeneric,
82+
"__QU_DEFAULT_GENERIC_BODY__": alerts.DefaultBodyGeneric,
83+
}
84+
out := builderHTML
85+
for marker, value := range subs {
86+
encoded, err := json.Marshal(value)
87+
if err != nil {
88+
return "", fmt.Errorf("encode %s: %w", marker, err)
89+
}
90+
if !strings.Contains(out, marker) {
91+
return "", fmt.Errorf("builder.html missing placeholder %s", marker)
92+
}
93+
out = strings.ReplaceAll(out, marker, string(encoded))
94+
}
95+
return out, nil
96+
}

0 commit comments

Comments
 (0)