From aa052097cfe064f4bd83a23af0456bdd1af920a7 Mon Sep 17 00:00:00 2001
From: Roman Khavronenko <hagen1778@gmail.com>
Date: Tue, 21 Sep 2021 14:41:01 +0300
Subject: [PATCH] app/vmalert: support `http.pathPrefix` flag in UI (#1636)

The change makes UI to respect `http.pathPrefix` flag
for API or navigation items links.
---
 app/vmalert/web.go      |  38 ++-
 app/vmalert/web.qtpl    |  13 +-
 app/vmalert/web.qtpl.go | 640 ++++++++++++++++++++--------------------
 3 files changed, 349 insertions(+), 342 deletions(-)

diff --git a/app/vmalert/web.go b/app/vmalert/web.go
index a82d052c71..ef31ce3598 100644
--- a/app/vmalert/web.go
+++ b/app/vmalert/web.go
@@ -4,32 +4,56 @@ import (
 	"encoding/json"
 	"fmt"
 	"net/http"
+	"path"
 	"sort"
 	"strconv"
 	"strings"
+	"sync"
 
+	"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/tpl"
 	"github.com/VictoriaMetrics/VictoriaMetrics/lib/httpserver"
 	"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
 	"github.com/VictoriaMetrics/VictoriaMetrics/lib/procutil"
 )
 
+var (
+	once     = sync.Once{}
+	apiLinks [][2]string
+	navItems []tpl.NavItem
+)
+
+func initLinks() {
+	pathPrefix := httpserver.GetPathPrefix()
+	apiLinks = [][2]string{
+		{path.Join(pathPrefix, "api/v1/groups"), "list all loaded groups and rules"},
+		{path.Join(pathPrefix, "api/v1/alerts"), "list all active alerts"},
+		{path.Join(pathPrefix, "api/v1/groupID/alertID/status"), "get alert status by ID"},
+		{path.Join(pathPrefix, "metrics"), "list of application metrics"},
+		{path.Join(pathPrefix, "-/reload"), "reload configuration"},
+	}
+	navItems = []tpl.NavItem{
+		{Name: "vmalert", Url: path.Join(pathPrefix, "/")},
+		{Name: "Groups", Url: path.Join(pathPrefix, "groups")},
+		{Name: "Alerts", Url: path.Join(pathPrefix, "/alerts")},
+		{Name: "Docs", Url: "https://docs.victoriametrics.com/vmalert.html"},
+	}
+}
+
 type requestHandler struct {
 	m *manager
 }
 
 func (rh *requestHandler) handler(w http.ResponseWriter, r *http.Request) bool {
+	once.Do(func() {
+		initLinks()
+	})
+
 	switch r.URL.Path {
 	case "/":
 		if r.Method != "GET" {
 			return false
 		}
-		WriteWelcome(w, [][2]string{
-			{"/api/v1/groups", "list all loaded groups and rules"},
-			{"/api/v1/alerts", "list all active alerts"},
-			{"/api/v1/groupID/alertID/status", "get alert status by ID"},
-			{"/metrics", "list of application metrics"},
-			{"/-/reload", "reload configuration"},
-		})
+		WriteWelcome(w)
 		return true
 	case "/alerts":
 		WriteListAlerts(w, rh.groupAlerts())
diff --git a/app/vmalert/web.qtpl b/app/vmalert/web.qtpl
index e135bce8c5..7c5c6b2b4e 100644
--- a/app/vmalert/web.qtpl
+++ b/app/vmalert/web.qtpl
@@ -8,20 +8,11 @@
 ) %}
 
 
-{% code
-var navItems = []tpl.NavItem{
-    {Name: "vmalert", Url: "/"},
-    {Name: "Groups", Url: "/groups"},
-    {Name: "Alerts",  Url: "/alerts"},
-    {Name: "Docs", Url: "https://docs.victoriametrics.com/vmalert.html"},
-}
-%}
-
-{% func Welcome(pathList [][2]string) %}
+{% func Welcome() %}
     {%= tpl.Header("vmalert", navItems) %}
     <p>
         API:<br>
-        {% for _, p := range pathList  %}
+        {% for _, p := range apiLinks  %}
             {%code
                 p, doc := p[0], p[1]
             %}
diff --git a/app/vmalert/web.qtpl.go b/app/vmalert/web.qtpl.go
index 9cf4d0f900..a15c6523f8 100644
--- a/app/vmalert/web.qtpl.go
+++ b/app/vmalert/web.qtpl.go
@@ -25,106 +25,98 @@ var (
 	_ = qt422016.AcquireByteBuffer
 )
 
-//line app/vmalert/web.qtpl:12
-var navItems = []tpl.NavItem{
-	{Name: "vmalert", Url: "/"},
-	{Name: "Groups", Url: "/groups"},
-	{Name: "Alerts", Url: "/alerts"},
-	{Name: "Docs", Url: "https://docs.victoriametrics.com/vmalert.html"},
-}
-
-//line app/vmalert/web.qtpl:20
-func StreamWelcome(qw422016 *qt422016.Writer, pathList [][2]string) {
-//line app/vmalert/web.qtpl:20
+//line app/vmalert/web.qtpl:11
+func StreamWelcome(qw422016 *qt422016.Writer) {
+//line app/vmalert/web.qtpl:11
 	qw422016.N().S(`
     `)
-//line app/vmalert/web.qtpl:21
+//line app/vmalert/web.qtpl:12
 	tpl.StreamHeader(qw422016, "vmalert", navItems)
-//line app/vmalert/web.qtpl:21
+//line app/vmalert/web.qtpl:12
 	qw422016.N().S(`
     <p>
         API:<br>
         `)
-//line app/vmalert/web.qtpl:24
-	for _, p := range pathList {
-//line app/vmalert/web.qtpl:24
+//line app/vmalert/web.qtpl:15
+	for _, p := range apiLinks {
+//line app/vmalert/web.qtpl:15
 		qw422016.N().S(`
             `)
-//line app/vmalert/web.qtpl:26
+//line app/vmalert/web.qtpl:17
 		p, doc := p[0], p[1]
 
-//line app/vmalert/web.qtpl:27
+//line app/vmalert/web.qtpl:18
 		qw422016.N().S(`
         	<a href="`)
-//line app/vmalert/web.qtpl:28
+//line app/vmalert/web.qtpl:19
 		qw422016.E().S(p)
-//line app/vmalert/web.qtpl:28
+//line app/vmalert/web.qtpl:19
 		qw422016.N().S(`">`)
-//line app/vmalert/web.qtpl:28
+//line app/vmalert/web.qtpl:19
 		qw422016.E().S(p)
-//line app/vmalert/web.qtpl:28
+//line app/vmalert/web.qtpl:19
 		qw422016.N().S(`</a> - `)
-//line app/vmalert/web.qtpl:28
+//line app/vmalert/web.qtpl:19
 		qw422016.E().S(doc)
-//line app/vmalert/web.qtpl:28
+//line app/vmalert/web.qtpl:19
 		qw422016.N().S(`<br/>
         `)
-//line app/vmalert/web.qtpl:29
+//line app/vmalert/web.qtpl:20
 	}
-//line app/vmalert/web.qtpl:29
+//line app/vmalert/web.qtpl:20
 	qw422016.N().S(`
     </p>
     `)
-//line app/vmalert/web.qtpl:31
+//line app/vmalert/web.qtpl:22
 	tpl.StreamFooter(qw422016)
-//line app/vmalert/web.qtpl:31
+//line app/vmalert/web.qtpl:22
 	qw422016.N().S(`
 `)
-//line app/vmalert/web.qtpl:32
+//line app/vmalert/web.qtpl:23
 }
 
-//line app/vmalert/web.qtpl:32
-func WriteWelcome(qq422016 qtio422016.Writer, pathList [][2]string) {
-//line app/vmalert/web.qtpl:32
+//line app/vmalert/web.qtpl:23
+func WriteWelcome(qq422016 qtio422016.Writer) {
+//line app/vmalert/web.qtpl:23
 	qw422016 := qt422016.AcquireWriter(qq422016)
-//line app/vmalert/web.qtpl:32
-	StreamWelcome(qw422016, pathList)
-//line app/vmalert/web.qtpl:32
+//line app/vmalert/web.qtpl:23
+	StreamWelcome(qw422016)
+//line app/vmalert/web.qtpl:23
 	qt422016.ReleaseWriter(qw422016)
-//line app/vmalert/web.qtpl:32
+//line app/vmalert/web.qtpl:23
 }
 
-//line app/vmalert/web.qtpl:32
-func Welcome(pathList [][2]string) string {
-//line app/vmalert/web.qtpl:32
+//line app/vmalert/web.qtpl:23
+func Welcome() string {
+//line app/vmalert/web.qtpl:23
 	qb422016 := qt422016.AcquireByteBuffer()
-//line app/vmalert/web.qtpl:32
-	WriteWelcome(qb422016, pathList)
-//line app/vmalert/web.qtpl:32
+//line app/vmalert/web.qtpl:23
+	WriteWelcome(qb422016)
+//line app/vmalert/web.qtpl:23
 	qs422016 := string(qb422016.B)
-//line app/vmalert/web.qtpl:32
+//line app/vmalert/web.qtpl:23
 	qt422016.ReleaseByteBuffer(qb422016)
-//line app/vmalert/web.qtpl:32
+//line app/vmalert/web.qtpl:23
 	return qs422016
-//line app/vmalert/web.qtpl:32
+//line app/vmalert/web.qtpl:23
 }
 
-//line app/vmalert/web.qtpl:34
+//line app/vmalert/web.qtpl:25
 func StreamListGroups(qw422016 *qt422016.Writer, groups []APIGroup) {
-//line app/vmalert/web.qtpl:34
+//line app/vmalert/web.qtpl:25
 	qw422016.N().S(`
     `)
-//line app/vmalert/web.qtpl:35
+//line app/vmalert/web.qtpl:26
 	tpl.StreamHeader(qw422016, "Groups", navItems)
-//line app/vmalert/web.qtpl:35
+//line app/vmalert/web.qtpl:26
 	qw422016.N().S(`
     `)
-//line app/vmalert/web.qtpl:36
+//line app/vmalert/web.qtpl:27
 	if len(groups) > 0 {
-//line app/vmalert/web.qtpl:36
+//line app/vmalert/web.qtpl:27
 		qw422016.N().S(`
         `)
-//line app/vmalert/web.qtpl:38
+//line app/vmalert/web.qtpl:29
 		rOk := make(map[string]int)
 		rNotOk := make(map[string]int)
 		for _, g := range groups {
@@ -144,115 +136,115 @@ func StreamListGroups(qw422016 *qt422016.Writer, groups []APIGroup) {
 			}
 		}
 
-//line app/vmalert/web.qtpl:56
+//line app/vmalert/web.qtpl:47
 		qw422016.N().S(`
          <a class="btn btn-primary" role="button" onclick="collapseAll()">Collapse All</a>
          <a class="btn btn-primary" role="button" onclick="expandAll()">Expand All</a>
         `)
-//line app/vmalert/web.qtpl:59
+//line app/vmalert/web.qtpl:50
 		for _, g := range groups {
-//line app/vmalert/web.qtpl:59
+//line app/vmalert/web.qtpl:50
 			qw422016.N().S(`
               <div class="group-heading`)
-//line app/vmalert/web.qtpl:60
+//line app/vmalert/web.qtpl:51
 			if rNotOk[g.Name] > 0 {
-//line app/vmalert/web.qtpl:60
+//line app/vmalert/web.qtpl:51
 				qw422016.N().S(` alert-danger`)
-//line app/vmalert/web.qtpl:60
+//line app/vmalert/web.qtpl:51
 			}
-//line app/vmalert/web.qtpl:60
+//line app/vmalert/web.qtpl:51
 			qw422016.N().S(`"  data-bs-target="rules-`)
-//line app/vmalert/web.qtpl:60
+//line app/vmalert/web.qtpl:51
 			qw422016.E().S(g.ID)
-//line app/vmalert/web.qtpl:60
+//line app/vmalert/web.qtpl:51
 			qw422016.N().S(`">
                 <span class="anchor" id="group-`)
-//line app/vmalert/web.qtpl:61
+//line app/vmalert/web.qtpl:52
 			qw422016.E().S(g.ID)
-//line app/vmalert/web.qtpl:61
+//line app/vmalert/web.qtpl:52
 			qw422016.N().S(`"></span>
                 <a href="#group-`)
-//line app/vmalert/web.qtpl:62
+//line app/vmalert/web.qtpl:53
 			qw422016.E().S(g.ID)
-//line app/vmalert/web.qtpl:62
+//line app/vmalert/web.qtpl:53
 			qw422016.N().S(`">`)
-//line app/vmalert/web.qtpl:62
+//line app/vmalert/web.qtpl:53
 			qw422016.E().S(g.Name)
-//line app/vmalert/web.qtpl:62
+//line app/vmalert/web.qtpl:53
 			if g.Type != "prometheus" {
-//line app/vmalert/web.qtpl:62
+//line app/vmalert/web.qtpl:53
 				qw422016.N().S(` (`)
-//line app/vmalert/web.qtpl:62
+//line app/vmalert/web.qtpl:53
 				qw422016.E().S(g.Type)
-//line app/vmalert/web.qtpl:62
+//line app/vmalert/web.qtpl:53
 				qw422016.N().S(`)`)
-//line app/vmalert/web.qtpl:62
+//line app/vmalert/web.qtpl:53
 			}
-//line app/vmalert/web.qtpl:62
+//line app/vmalert/web.qtpl:53
 			qw422016.N().S(` (every `)
-//line app/vmalert/web.qtpl:62
+//line app/vmalert/web.qtpl:53
 			qw422016.E().S(g.Interval)
-//line app/vmalert/web.qtpl:62
+//line app/vmalert/web.qtpl:53
 			qw422016.N().S(`)</a>
                  `)
-//line app/vmalert/web.qtpl:63
+//line app/vmalert/web.qtpl:54
 			if rNotOk[g.Name] > 0 {
-//line app/vmalert/web.qtpl:63
+//line app/vmalert/web.qtpl:54
 				qw422016.N().S(`<span class="badge bg-danger" title="Number of rules withs status Error">`)
-//line app/vmalert/web.qtpl:63
+//line app/vmalert/web.qtpl:54
 				qw422016.N().D(rNotOk[g.Name])
-//line app/vmalert/web.qtpl:63
+//line app/vmalert/web.qtpl:54
 				qw422016.N().S(`</span> `)
-//line app/vmalert/web.qtpl:63
+//line app/vmalert/web.qtpl:54
 			}
-//line app/vmalert/web.qtpl:63
+//line app/vmalert/web.qtpl:54
 			qw422016.N().S(`
                 <span class="badge bg-success" title="Number of rules withs status Ok">`)
-//line app/vmalert/web.qtpl:64
+//line app/vmalert/web.qtpl:55
 			qw422016.N().D(rOk[g.Name])
-//line app/vmalert/web.qtpl:64
+//line app/vmalert/web.qtpl:55
 			qw422016.N().S(`</span>
                 <p class="fs-6 fw-lighter">`)
-//line app/vmalert/web.qtpl:65
+//line app/vmalert/web.qtpl:56
 			qw422016.E().S(g.File)
-//line app/vmalert/web.qtpl:65
+//line app/vmalert/web.qtpl:56
 			qw422016.N().S(`</p>
                 `)
-//line app/vmalert/web.qtpl:66
+//line app/vmalert/web.qtpl:57
 			if len(g.ExtraFilterLabels) > 0 {
-//line app/vmalert/web.qtpl:66
+//line app/vmalert/web.qtpl:57
 				qw422016.N().S(`
                     <div class="fs-6 fw-lighter">Extra filter labels
                     `)
-//line app/vmalert/web.qtpl:68
+//line app/vmalert/web.qtpl:59
 				for k, v := range g.ExtraFilterLabels {
-//line app/vmalert/web.qtpl:68
+//line app/vmalert/web.qtpl:59
 					qw422016.N().S(`
                             <span class="float-left badge bg-primary">`)
-//line app/vmalert/web.qtpl:69
+//line app/vmalert/web.qtpl:60
 					qw422016.E().S(k)
-//line app/vmalert/web.qtpl:69
+//line app/vmalert/web.qtpl:60
 					qw422016.N().S(`=`)
-//line app/vmalert/web.qtpl:69
+//line app/vmalert/web.qtpl:60
 					qw422016.E().S(v)
-//line app/vmalert/web.qtpl:69
+//line app/vmalert/web.qtpl:60
 					qw422016.N().S(`</span>
                     `)
-//line app/vmalert/web.qtpl:70
+//line app/vmalert/web.qtpl:61
 				}
-//line app/vmalert/web.qtpl:70
+//line app/vmalert/web.qtpl:61
 				qw422016.N().S(`
                     </div>
                 `)
-//line app/vmalert/web.qtpl:72
+//line app/vmalert/web.qtpl:63
 			}
-//line app/vmalert/web.qtpl:72
+//line app/vmalert/web.qtpl:63
 			qw422016.N().S(`
             </div>
             <div class="collapse" id="rules-`)
-//line app/vmalert/web.qtpl:74
+//line app/vmalert/web.qtpl:65
 			qw422016.E().S(g.ID)
-//line app/vmalert/web.qtpl:74
+//line app/vmalert/web.qtpl:65
 			qw422016.N().S(`">
                 <table class="table table-striped table-hover table-sm">
                     <thead>
@@ -265,280 +257,280 @@ func StreamListGroups(qw422016 *qt422016.Writer, groups []APIGroup) {
                     </thead>
                     <tbody>
                     `)
-//line app/vmalert/web.qtpl:85
+//line app/vmalert/web.qtpl:76
 			for _, ar := range g.AlertingRules {
-//line app/vmalert/web.qtpl:85
+//line app/vmalert/web.qtpl:76
 				qw422016.N().S(`
                         <tr`)
-//line app/vmalert/web.qtpl:86
+//line app/vmalert/web.qtpl:77
 				if ar.LastError != "" {
-//line app/vmalert/web.qtpl:86
+//line app/vmalert/web.qtpl:77
 					qw422016.N().S(` class="alert-danger"`)
-//line app/vmalert/web.qtpl:86
+//line app/vmalert/web.qtpl:77
 				}
-//line app/vmalert/web.qtpl:86
+//line app/vmalert/web.qtpl:77
 				qw422016.N().S(`>
                             <td>
                                 <b>alert:</b> `)
-//line app/vmalert/web.qtpl:88
+//line app/vmalert/web.qtpl:79
 				qw422016.E().S(ar.Name)
-//line app/vmalert/web.qtpl:88
+//line app/vmalert/web.qtpl:79
 				qw422016.N().S(` (for: `)
-//line app/vmalert/web.qtpl:88
+//line app/vmalert/web.qtpl:79
 				qw422016.E().V(ar.For)
-//line app/vmalert/web.qtpl:88
+//line app/vmalert/web.qtpl:79
 				qw422016.N().S(`)<br>
                                 <code><pre>`)
-//line app/vmalert/web.qtpl:89
+//line app/vmalert/web.qtpl:80
 				qw422016.E().S(ar.Expression)
-//line app/vmalert/web.qtpl:89
+//line app/vmalert/web.qtpl:80
 				qw422016.N().S(`</pre></code><br>
                                 `)
-//line app/vmalert/web.qtpl:90
+//line app/vmalert/web.qtpl:81
 				if len(ar.Labels) > 0 {
-//line app/vmalert/web.qtpl:90
+//line app/vmalert/web.qtpl:81
 					qw422016.N().S(` <b>Labels:</b>`)
-//line app/vmalert/web.qtpl:90
+//line app/vmalert/web.qtpl:81
 				}
-//line app/vmalert/web.qtpl:90
+//line app/vmalert/web.qtpl:81
 				qw422016.N().S(`
                                 `)
-//line app/vmalert/web.qtpl:91
+//line app/vmalert/web.qtpl:82
 				for k, v := range ar.Labels {
-//line app/vmalert/web.qtpl:91
+//line app/vmalert/web.qtpl:82
 					qw422016.N().S(`
                                         <span class="ms-1 badge bg-primary">`)
-//line app/vmalert/web.qtpl:92
+//line app/vmalert/web.qtpl:83
 					qw422016.E().S(k)
-//line app/vmalert/web.qtpl:92
+//line app/vmalert/web.qtpl:83
 					qw422016.N().S(`=`)
-//line app/vmalert/web.qtpl:92
+//line app/vmalert/web.qtpl:83
 					qw422016.E().S(v)
-//line app/vmalert/web.qtpl:92
+//line app/vmalert/web.qtpl:83
 					qw422016.N().S(`</span>
                                 `)
-//line app/vmalert/web.qtpl:93
+//line app/vmalert/web.qtpl:84
 				}
-//line app/vmalert/web.qtpl:93
+//line app/vmalert/web.qtpl:84
 				qw422016.N().S(`
                             </td>
                             <td><div class="error-cell">`)
-//line app/vmalert/web.qtpl:95
+//line app/vmalert/web.qtpl:86
 				qw422016.E().S(ar.LastError)
-//line app/vmalert/web.qtpl:95
+//line app/vmalert/web.qtpl:86
 				qw422016.N().S(`</div></td>
                             <td>`)
-//line app/vmalert/web.qtpl:96
+//line app/vmalert/web.qtpl:87
 				qw422016.N().D(ar.LastSamples)
-//line app/vmalert/web.qtpl:96
+//line app/vmalert/web.qtpl:87
 				qw422016.N().S(`</td>
                             <td>`)
-//line app/vmalert/web.qtpl:97
+//line app/vmalert/web.qtpl:88
 				qw422016.N().FPrec(time.Since(ar.LastExec).Seconds(), 3)
-//line app/vmalert/web.qtpl:97
+//line app/vmalert/web.qtpl:88
 				qw422016.N().S(`s ago</td>
                         </tr>
                     `)
-//line app/vmalert/web.qtpl:99
+//line app/vmalert/web.qtpl:90
 			}
-//line app/vmalert/web.qtpl:99
+//line app/vmalert/web.qtpl:90
 			qw422016.N().S(`
                     `)
-//line app/vmalert/web.qtpl:100
+//line app/vmalert/web.qtpl:91
 			for _, rr := range g.RecordingRules {
-//line app/vmalert/web.qtpl:100
+//line app/vmalert/web.qtpl:91
 				qw422016.N().S(`
                         <tr>
                             <td>
                                 <b>record:</b> `)
-//line app/vmalert/web.qtpl:103
+//line app/vmalert/web.qtpl:94
 				qw422016.E().S(rr.Name)
-//line app/vmalert/web.qtpl:103
+//line app/vmalert/web.qtpl:94
 				qw422016.N().S(`<br>
                                 <code><pre>`)
-//line app/vmalert/web.qtpl:104
+//line app/vmalert/web.qtpl:95
 				qw422016.E().S(rr.Expression)
-//line app/vmalert/web.qtpl:104
+//line app/vmalert/web.qtpl:95
 				qw422016.N().S(`</pre></code>
                                 `)
-//line app/vmalert/web.qtpl:105
+//line app/vmalert/web.qtpl:96
 				if len(rr.Labels) > 0 {
-//line app/vmalert/web.qtpl:105
+//line app/vmalert/web.qtpl:96
 					qw422016.N().S(` <b>Labels:</b>`)
-//line app/vmalert/web.qtpl:105
+//line app/vmalert/web.qtpl:96
 				}
-//line app/vmalert/web.qtpl:105
+//line app/vmalert/web.qtpl:96
 				qw422016.N().S(`
                                 `)
-//line app/vmalert/web.qtpl:106
+//line app/vmalert/web.qtpl:97
 				for k, v := range rr.Labels {
-//line app/vmalert/web.qtpl:106
+//line app/vmalert/web.qtpl:97
 					qw422016.N().S(`
                                         <span class="ms-1 badge bg-primary">`)
-//line app/vmalert/web.qtpl:107
+//line app/vmalert/web.qtpl:98
 					qw422016.E().S(k)
-//line app/vmalert/web.qtpl:107
+//line app/vmalert/web.qtpl:98
 					qw422016.N().S(`=`)
-//line app/vmalert/web.qtpl:107
+//line app/vmalert/web.qtpl:98
 					qw422016.E().S(v)
-//line app/vmalert/web.qtpl:107
+//line app/vmalert/web.qtpl:98
 					qw422016.N().S(`</span>
                                 `)
-//line app/vmalert/web.qtpl:108
+//line app/vmalert/web.qtpl:99
 				}
-//line app/vmalert/web.qtpl:108
+//line app/vmalert/web.qtpl:99
 				qw422016.N().S(`
                             </td>
                             <td><div class="error-cell">`)
-//line app/vmalert/web.qtpl:110
+//line app/vmalert/web.qtpl:101
 				qw422016.E().S(rr.LastError)
-//line app/vmalert/web.qtpl:110
+//line app/vmalert/web.qtpl:101
 				qw422016.N().S(`</div></td>
                             <td>`)
-//line app/vmalert/web.qtpl:111
+//line app/vmalert/web.qtpl:102
 				qw422016.N().D(rr.LastSamples)
-//line app/vmalert/web.qtpl:111
+//line app/vmalert/web.qtpl:102
 				qw422016.N().S(`</td>
                             <td>`)
-//line app/vmalert/web.qtpl:112
+//line app/vmalert/web.qtpl:103
 				qw422016.N().FPrec(time.Since(rr.LastExec).Seconds(), 3)
-//line app/vmalert/web.qtpl:112
+//line app/vmalert/web.qtpl:103
 				qw422016.N().S(`s ago</td>
                         </tr>
                     `)
-//line app/vmalert/web.qtpl:114
+//line app/vmalert/web.qtpl:105
 			}
-//line app/vmalert/web.qtpl:114
+//line app/vmalert/web.qtpl:105
 			qw422016.N().S(`
                  </tbody>
                 </table>
             </div>
         `)
-//line app/vmalert/web.qtpl:118
+//line app/vmalert/web.qtpl:109
 		}
-//line app/vmalert/web.qtpl:118
+//line app/vmalert/web.qtpl:109
 		qw422016.N().S(`
 
     `)
-//line app/vmalert/web.qtpl:120
+//line app/vmalert/web.qtpl:111
 	} else {
-//line app/vmalert/web.qtpl:120
+//line app/vmalert/web.qtpl:111
 		qw422016.N().S(`
         <div>
             <p>No items...</p>
         </div>
     `)
-//line app/vmalert/web.qtpl:124
+//line app/vmalert/web.qtpl:115
 	}
-//line app/vmalert/web.qtpl:124
+//line app/vmalert/web.qtpl:115
 	qw422016.N().S(`
 
     `)
-//line app/vmalert/web.qtpl:126
+//line app/vmalert/web.qtpl:117
 	tpl.StreamFooter(qw422016)
-//line app/vmalert/web.qtpl:126
+//line app/vmalert/web.qtpl:117
 	qw422016.N().S(`
 
 `)
-//line app/vmalert/web.qtpl:128
+//line app/vmalert/web.qtpl:119
 }
 
-//line app/vmalert/web.qtpl:128
+//line app/vmalert/web.qtpl:119
 func WriteListGroups(qq422016 qtio422016.Writer, groups []APIGroup) {
-//line app/vmalert/web.qtpl:128
+//line app/vmalert/web.qtpl:119
 	qw422016 := qt422016.AcquireWriter(qq422016)
-//line app/vmalert/web.qtpl:128
+//line app/vmalert/web.qtpl:119
 	StreamListGroups(qw422016, groups)
-//line app/vmalert/web.qtpl:128
+//line app/vmalert/web.qtpl:119
 	qt422016.ReleaseWriter(qw422016)
-//line app/vmalert/web.qtpl:128
+//line app/vmalert/web.qtpl:119
 }
 
-//line app/vmalert/web.qtpl:128
+//line app/vmalert/web.qtpl:119
 func ListGroups(groups []APIGroup) string {
-//line app/vmalert/web.qtpl:128
+//line app/vmalert/web.qtpl:119
 	qb422016 := qt422016.AcquireByteBuffer()
-//line app/vmalert/web.qtpl:128
+//line app/vmalert/web.qtpl:119
 	WriteListGroups(qb422016, groups)
-//line app/vmalert/web.qtpl:128
+//line app/vmalert/web.qtpl:119
 	qs422016 := string(qb422016.B)
-//line app/vmalert/web.qtpl:128
+//line app/vmalert/web.qtpl:119
 	qt422016.ReleaseByteBuffer(qb422016)
-//line app/vmalert/web.qtpl:128
+//line app/vmalert/web.qtpl:119
 	return qs422016
-//line app/vmalert/web.qtpl:128
+//line app/vmalert/web.qtpl:119
 }
 
-//line app/vmalert/web.qtpl:131
+//line app/vmalert/web.qtpl:122
 func StreamListAlerts(qw422016 *qt422016.Writer, groupAlerts []GroupAlerts) {
-//line app/vmalert/web.qtpl:131
+//line app/vmalert/web.qtpl:122
 	qw422016.N().S(`
     `)
-//line app/vmalert/web.qtpl:132
+//line app/vmalert/web.qtpl:123
 	tpl.StreamHeader(qw422016, "Alerts", navItems)
-//line app/vmalert/web.qtpl:132
+//line app/vmalert/web.qtpl:123
 	qw422016.N().S(`
     `)
-//line app/vmalert/web.qtpl:133
+//line app/vmalert/web.qtpl:124
 	if len(groupAlerts) > 0 {
-//line app/vmalert/web.qtpl:133
+//line app/vmalert/web.qtpl:124
 		qw422016.N().S(`
          <a class="btn btn-primary" role="button" onclick="collapseAll()">Collapse All</a>
          <a class="btn btn-primary" role="button" onclick="expandAll()">Expand All</a>
          `)
-//line app/vmalert/web.qtpl:136
+//line app/vmalert/web.qtpl:127
 		for _, ga := range groupAlerts {
-//line app/vmalert/web.qtpl:136
+//line app/vmalert/web.qtpl:127
 			qw422016.N().S(`
             `)
-//line app/vmalert/web.qtpl:137
+//line app/vmalert/web.qtpl:128
 			g := ga.Group
 
-//line app/vmalert/web.qtpl:137
+//line app/vmalert/web.qtpl:128
 			qw422016.N().S(`
             <div class="group-heading alert-danger" data-bs-target="rules-`)
-//line app/vmalert/web.qtpl:138
+//line app/vmalert/web.qtpl:129
 			qw422016.E().S(g.ID)
-//line app/vmalert/web.qtpl:138
+//line app/vmalert/web.qtpl:129
 			qw422016.N().S(`">
                 <span class="anchor" id="group-`)
-//line app/vmalert/web.qtpl:139
+//line app/vmalert/web.qtpl:130
 			qw422016.E().S(g.ID)
-//line app/vmalert/web.qtpl:139
+//line app/vmalert/web.qtpl:130
 			qw422016.N().S(`"></span>
                 <a href="#group-`)
-//line app/vmalert/web.qtpl:140
+//line app/vmalert/web.qtpl:131
 			qw422016.E().S(g.ID)
-//line app/vmalert/web.qtpl:140
+//line app/vmalert/web.qtpl:131
 			qw422016.N().S(`">`)
-//line app/vmalert/web.qtpl:140
+//line app/vmalert/web.qtpl:131
 			qw422016.E().S(g.Name)
-//line app/vmalert/web.qtpl:140
+//line app/vmalert/web.qtpl:131
 			if g.Type != "prometheus" {
-//line app/vmalert/web.qtpl:140
+//line app/vmalert/web.qtpl:131
 				qw422016.N().S(` (`)
-//line app/vmalert/web.qtpl:140
+//line app/vmalert/web.qtpl:131
 				qw422016.E().S(g.Type)
-//line app/vmalert/web.qtpl:140
+//line app/vmalert/web.qtpl:131
 				qw422016.N().S(`)`)
-//line app/vmalert/web.qtpl:140
+//line app/vmalert/web.qtpl:131
 			}
-//line app/vmalert/web.qtpl:140
+//line app/vmalert/web.qtpl:131
 			qw422016.N().S(`</a>
                 <span class="badge bg-danger" title="Number of active alerts">`)
-//line app/vmalert/web.qtpl:141
+//line app/vmalert/web.qtpl:132
 			qw422016.N().D(len(ga.Alerts))
-//line app/vmalert/web.qtpl:141
+//line app/vmalert/web.qtpl:132
 			qw422016.N().S(`</span>
                 <br>
                 <p class="fs-6 fw-lighter">`)
-//line app/vmalert/web.qtpl:143
+//line app/vmalert/web.qtpl:134
 			qw422016.E().S(g.File)
-//line app/vmalert/web.qtpl:143
+//line app/vmalert/web.qtpl:134
 			qw422016.N().S(`</p>
             </div>
             `)
-//line app/vmalert/web.qtpl:146
+//line app/vmalert/web.qtpl:137
 			var keys []string
 			alertsByRule := make(map[string][]*APIAlert)
 			for _, alert := range ga.Alerts {
@@ -549,20 +541,20 @@ func StreamListAlerts(qw422016 *qt422016.Writer, groupAlerts []GroupAlerts) {
 			}
 			sort.Strings(keys)
 
-//line app/vmalert/web.qtpl:155
+//line app/vmalert/web.qtpl:146
 			qw422016.N().S(`
             <div class="collapse" id="rules-`)
-//line app/vmalert/web.qtpl:156
+//line app/vmalert/web.qtpl:147
 			qw422016.E().S(g.ID)
-//line app/vmalert/web.qtpl:156
+//line app/vmalert/web.qtpl:147
 			qw422016.N().S(`">
                 `)
-//line app/vmalert/web.qtpl:157
+//line app/vmalert/web.qtpl:148
 			for _, ruleID := range keys {
-//line app/vmalert/web.qtpl:157
+//line app/vmalert/web.qtpl:148
 				qw422016.N().S(`
                     `)
-//line app/vmalert/web.qtpl:159
+//line app/vmalert/web.qtpl:150
 				defaultAR := alertsByRule[ruleID][0]
 				var labelKeys []string
 				for k := range defaultAR.Labels {
@@ -570,22 +562,22 @@ func StreamListAlerts(qw422016 *qt422016.Writer, groupAlerts []GroupAlerts) {
 				}
 				sort.Strings(labelKeys)
 
-//line app/vmalert/web.qtpl:165
+//line app/vmalert/web.qtpl:156
 				qw422016.N().S(`
                     <br>
                     <b>alert:</b> `)
-//line app/vmalert/web.qtpl:167
+//line app/vmalert/web.qtpl:158
 				qw422016.E().S(defaultAR.Name)
-//line app/vmalert/web.qtpl:167
+//line app/vmalert/web.qtpl:158
 				qw422016.N().S(` (`)
-//line app/vmalert/web.qtpl:167
+//line app/vmalert/web.qtpl:158
 				qw422016.N().D(len(alertsByRule[ruleID]))
-//line app/vmalert/web.qtpl:167
+//line app/vmalert/web.qtpl:158
 				qw422016.N().S(`)<br>
                     <b>expr:</b><code><pre>`)
-//line app/vmalert/web.qtpl:168
+//line app/vmalert/web.qtpl:159
 				qw422016.E().S(defaultAR.Expression)
-//line app/vmalert/web.qtpl:168
+//line app/vmalert/web.qtpl:159
 				qw422016.N().S(`</pre></code>
                     <table class="table table-striped table-hover table-sm">
                         <thead>
@@ -599,152 +591,152 @@ func StreamListAlerts(qw422016 *qt422016.Writer, groupAlerts []GroupAlerts) {
                         </thead>
                         <tbody>
                         `)
-//line app/vmalert/web.qtpl:180
+//line app/vmalert/web.qtpl:171
 				for _, ar := range alertsByRule[ruleID] {
-//line app/vmalert/web.qtpl:180
+//line app/vmalert/web.qtpl:171
 					qw422016.N().S(`
                             <tr>
                                 <td>
                                     `)
-//line app/vmalert/web.qtpl:183
+//line app/vmalert/web.qtpl:174
 					for _, k := range labelKeys {
-//line app/vmalert/web.qtpl:183
+//line app/vmalert/web.qtpl:174
 						qw422016.N().S(`
                                         <span class="ms-1 badge bg-primary">`)
-//line app/vmalert/web.qtpl:184
+//line app/vmalert/web.qtpl:175
 						qw422016.E().S(k)
-//line app/vmalert/web.qtpl:184
+//line app/vmalert/web.qtpl:175
 						qw422016.N().S(`=`)
-//line app/vmalert/web.qtpl:184
+//line app/vmalert/web.qtpl:175
 						qw422016.E().S(ar.Labels[k])
-//line app/vmalert/web.qtpl:184
+//line app/vmalert/web.qtpl:175
 						qw422016.N().S(`</span>
                                     `)
-//line app/vmalert/web.qtpl:185
+//line app/vmalert/web.qtpl:176
 					}
-//line app/vmalert/web.qtpl:185
+//line app/vmalert/web.qtpl:176
 					qw422016.N().S(`
                                 </td>
                                 <td><span class="badge `)
-//line app/vmalert/web.qtpl:187
+//line app/vmalert/web.qtpl:178
 					if ar.State == "firing" {
-//line app/vmalert/web.qtpl:187
+//line app/vmalert/web.qtpl:178
 						qw422016.N().S(`bg-danger`)
-//line app/vmalert/web.qtpl:187
+//line app/vmalert/web.qtpl:178
 					} else {
-//line app/vmalert/web.qtpl:187
+//line app/vmalert/web.qtpl:178
 						qw422016.N().S(` bg-warning text-dark`)
-//line app/vmalert/web.qtpl:187
+//line app/vmalert/web.qtpl:178
 					}
-//line app/vmalert/web.qtpl:187
+//line app/vmalert/web.qtpl:178
 					qw422016.N().S(`">`)
-//line app/vmalert/web.qtpl:187
+//line app/vmalert/web.qtpl:178
 					qw422016.E().S(ar.State)
-//line app/vmalert/web.qtpl:187
+//line app/vmalert/web.qtpl:178
 					qw422016.N().S(`</span></td>
                                 <td>`)
-//line app/vmalert/web.qtpl:188
+//line app/vmalert/web.qtpl:179
 					qw422016.E().S(ar.ActiveAt.Format("2006-01-02T15:04:05Z07:00"))
-//line app/vmalert/web.qtpl:188
+//line app/vmalert/web.qtpl:179
 					qw422016.N().S(`</td>
                                 <td>`)
-//line app/vmalert/web.qtpl:189
+//line app/vmalert/web.qtpl:180
 					qw422016.E().S(ar.Value)
-//line app/vmalert/web.qtpl:189
+//line app/vmalert/web.qtpl:180
 					qw422016.N().S(`</td>
                                 <td>
                                     <a href="/`)
-//line app/vmalert/web.qtpl:191
+//line app/vmalert/web.qtpl:182
 					qw422016.E().S(g.ID)
-//line app/vmalert/web.qtpl:191
+//line app/vmalert/web.qtpl:182
 					qw422016.N().S(`/`)
-//line app/vmalert/web.qtpl:191
+//line app/vmalert/web.qtpl:182
 					qw422016.E().S(ar.ID)
-//line app/vmalert/web.qtpl:191
+//line app/vmalert/web.qtpl:182
 					qw422016.N().S(`/status">Details</a>
                                 </td>
                             </tr>
                         `)
-//line app/vmalert/web.qtpl:194
+//line app/vmalert/web.qtpl:185
 				}
-//line app/vmalert/web.qtpl:194
+//line app/vmalert/web.qtpl:185
 				qw422016.N().S(`
                      </tbody>
                     </table>
                 `)
-//line app/vmalert/web.qtpl:197
+//line app/vmalert/web.qtpl:188
 			}
-//line app/vmalert/web.qtpl:197
+//line app/vmalert/web.qtpl:188
 			qw422016.N().S(`
             </div>
             <br>
         `)
-//line app/vmalert/web.qtpl:200
+//line app/vmalert/web.qtpl:191
 		}
-//line app/vmalert/web.qtpl:200
+//line app/vmalert/web.qtpl:191
 		qw422016.N().S(`
 
     `)
-//line app/vmalert/web.qtpl:202
+//line app/vmalert/web.qtpl:193
 	} else {
-//line app/vmalert/web.qtpl:202
+//line app/vmalert/web.qtpl:193
 		qw422016.N().S(`
         <div>
             <p>No items...</p>
         </div>
     `)
-//line app/vmalert/web.qtpl:206
+//line app/vmalert/web.qtpl:197
 	}
-//line app/vmalert/web.qtpl:206
+//line app/vmalert/web.qtpl:197
 	qw422016.N().S(`
 
     `)
-//line app/vmalert/web.qtpl:208
+//line app/vmalert/web.qtpl:199
 	tpl.StreamFooter(qw422016)
-//line app/vmalert/web.qtpl:208
+//line app/vmalert/web.qtpl:199
 	qw422016.N().S(`
 
 `)
-//line app/vmalert/web.qtpl:210
+//line app/vmalert/web.qtpl:201
 }
 
-//line app/vmalert/web.qtpl:210
+//line app/vmalert/web.qtpl:201
 func WriteListAlerts(qq422016 qtio422016.Writer, groupAlerts []GroupAlerts) {
-//line app/vmalert/web.qtpl:210
+//line app/vmalert/web.qtpl:201
 	qw422016 := qt422016.AcquireWriter(qq422016)
-//line app/vmalert/web.qtpl:210
+//line app/vmalert/web.qtpl:201
 	StreamListAlerts(qw422016, groupAlerts)
-//line app/vmalert/web.qtpl:210
+//line app/vmalert/web.qtpl:201
 	qt422016.ReleaseWriter(qw422016)
-//line app/vmalert/web.qtpl:210
+//line app/vmalert/web.qtpl:201
 }
 
-//line app/vmalert/web.qtpl:210
+//line app/vmalert/web.qtpl:201
 func ListAlerts(groupAlerts []GroupAlerts) string {
-//line app/vmalert/web.qtpl:210
+//line app/vmalert/web.qtpl:201
 	qb422016 := qt422016.AcquireByteBuffer()
-//line app/vmalert/web.qtpl:210
+//line app/vmalert/web.qtpl:201
 	WriteListAlerts(qb422016, groupAlerts)
-//line app/vmalert/web.qtpl:210
+//line app/vmalert/web.qtpl:201
 	qs422016 := string(qb422016.B)
-//line app/vmalert/web.qtpl:210
+//line app/vmalert/web.qtpl:201
 	qt422016.ReleaseByteBuffer(qb422016)
-//line app/vmalert/web.qtpl:210
+//line app/vmalert/web.qtpl:201
 	return qs422016
-//line app/vmalert/web.qtpl:210
+//line app/vmalert/web.qtpl:201
 }
 
-//line app/vmalert/web.qtpl:212
+//line app/vmalert/web.qtpl:203
 func StreamAlert(qw422016 *qt422016.Writer, alert *APIAlert) {
-//line app/vmalert/web.qtpl:212
+//line app/vmalert/web.qtpl:203
 	qw422016.N().S(`
     `)
-//line app/vmalert/web.qtpl:213
+//line app/vmalert/web.qtpl:204
 	tpl.StreamHeader(qw422016, "", navItems)
-//line app/vmalert/web.qtpl:213
+//line app/vmalert/web.qtpl:204
 	qw422016.N().S(`
     `)
-//line app/vmalert/web.qtpl:215
+//line app/vmalert/web.qtpl:206
 	var labelKeys []string
 	for k := range alert.Labels {
 		labelKeys = append(labelKeys, k)
@@ -757,28 +749,28 @@ func StreamAlert(qw422016 *qt422016.Writer, alert *APIAlert) {
 	}
 	sort.Strings(annotationKeys)
 
-//line app/vmalert/web.qtpl:226
+//line app/vmalert/web.qtpl:217
 	qw422016.N().S(`
     <div class="display-6 pb-3 mb-3">`)
-//line app/vmalert/web.qtpl:227
+//line app/vmalert/web.qtpl:218
 	qw422016.E().S(alert.Name)
-//line app/vmalert/web.qtpl:227
+//line app/vmalert/web.qtpl:218
 	qw422016.N().S(`<span class="ms-2 badge `)
-//line app/vmalert/web.qtpl:227
+//line app/vmalert/web.qtpl:218
 	if alert.State == "firing" {
-//line app/vmalert/web.qtpl:227
+//line app/vmalert/web.qtpl:218
 		qw422016.N().S(`bg-danger`)
-//line app/vmalert/web.qtpl:227
+//line app/vmalert/web.qtpl:218
 	} else {
-//line app/vmalert/web.qtpl:227
+//line app/vmalert/web.qtpl:218
 		qw422016.N().S(` bg-warning text-dark`)
-//line app/vmalert/web.qtpl:227
+//line app/vmalert/web.qtpl:218
 	}
-//line app/vmalert/web.qtpl:227
+//line app/vmalert/web.qtpl:218
 	qw422016.N().S(`">`)
-//line app/vmalert/web.qtpl:227
+//line app/vmalert/web.qtpl:218
 	qw422016.E().S(alert.State)
-//line app/vmalert/web.qtpl:227
+//line app/vmalert/web.qtpl:218
 	qw422016.N().S(`</span></div>
     <div class="container border-bottom p-2">
       <div class="row">
@@ -787,9 +779,9 @@ func StreamAlert(qw422016 *qt422016.Writer, alert *APIAlert) {
         </div>
         <div class="col">
           `)
-//line app/vmalert/web.qtpl:234
+//line app/vmalert/web.qtpl:225
 	qw422016.E().S(alert.ActiveAt.Format("2006-01-02T15:04:05Z07:00"))
-//line app/vmalert/web.qtpl:234
+//line app/vmalert/web.qtpl:225
 	qw422016.N().S(`
         </div>
       </div>
@@ -801,9 +793,9 @@ func StreamAlert(qw422016 *qt422016.Writer, alert *APIAlert) {
         </div>
         <div class="col">
           <code><pre>`)
-//line app/vmalert/web.qtpl:244
+//line app/vmalert/web.qtpl:235
 	qw422016.E().S(alert.Expression)
-//line app/vmalert/web.qtpl:244
+//line app/vmalert/web.qtpl:235
 	qw422016.N().S(`</pre></code>
         </div>
       </div>
@@ -815,23 +807,23 @@ func StreamAlert(qw422016 *qt422016.Writer, alert *APIAlert) {
         </div>
         <div class="col">
            `)
-//line app/vmalert/web.qtpl:254
+//line app/vmalert/web.qtpl:245
 	for _, k := range labelKeys {
-//line app/vmalert/web.qtpl:254
+//line app/vmalert/web.qtpl:245
 		qw422016.N().S(`
                 <span class="m-1 badge bg-primary">`)
-//line app/vmalert/web.qtpl:255
+//line app/vmalert/web.qtpl:246
 		qw422016.E().S(k)
-//line app/vmalert/web.qtpl:255
+//line app/vmalert/web.qtpl:246
 		qw422016.N().S(`=`)
-//line app/vmalert/web.qtpl:255
+//line app/vmalert/web.qtpl:246
 		qw422016.E().S(alert.Labels[k])
-//line app/vmalert/web.qtpl:255
+//line app/vmalert/web.qtpl:246
 		qw422016.N().S(`</span>
           `)
-//line app/vmalert/web.qtpl:256
+//line app/vmalert/web.qtpl:247
 	}
-//line app/vmalert/web.qtpl:256
+//line app/vmalert/web.qtpl:247
 	qw422016.N().S(`
         </div>
       </div>
@@ -843,24 +835,24 @@ func StreamAlert(qw422016 *qt422016.Writer, alert *APIAlert) {
         </div>
         <div class="col">
            `)
-//line app/vmalert/web.qtpl:266
+//line app/vmalert/web.qtpl:257
 	for _, k := range annotationKeys {
-//line app/vmalert/web.qtpl:266
+//line app/vmalert/web.qtpl:257
 		qw422016.N().S(`
                 <b>`)
-//line app/vmalert/web.qtpl:267
+//line app/vmalert/web.qtpl:258
 		qw422016.E().S(k)
-//line app/vmalert/web.qtpl:267
+//line app/vmalert/web.qtpl:258
 		qw422016.N().S(`:</b><br>
                 <p>`)
-//line app/vmalert/web.qtpl:268
+//line app/vmalert/web.qtpl:259
 		qw422016.E().S(alert.Annotations[k])
-//line app/vmalert/web.qtpl:268
+//line app/vmalert/web.qtpl:259
 		qw422016.N().S(`</p>
           `)
-//line app/vmalert/web.qtpl:269
+//line app/vmalert/web.qtpl:260
 	}
-//line app/vmalert/web.qtpl:269
+//line app/vmalert/web.qtpl:260
 	qw422016.N().S(`
         </div>
       </div>
@@ -872,49 +864,49 @@ func StreamAlert(qw422016 *qt422016.Writer, alert *APIAlert) {
         </div>
         <div class="col">
            <a target="_blank" href="/groups#group-`)
-//line app/vmalert/web.qtpl:279
+//line app/vmalert/web.qtpl:270
 	qw422016.E().S(alert.GroupID)
-//line app/vmalert/web.qtpl:279
+//line app/vmalert/web.qtpl:270
 	qw422016.N().S(`">`)
-//line app/vmalert/web.qtpl:279
+//line app/vmalert/web.qtpl:270
 	qw422016.E().S(alert.GroupID)
-//line app/vmalert/web.qtpl:279
+//line app/vmalert/web.qtpl:270
 	qw422016.N().S(`</a>
         </div>
       </div>
     </div>
     `)
-//line app/vmalert/web.qtpl:283
+//line app/vmalert/web.qtpl:274
 	tpl.StreamFooter(qw422016)
-//line app/vmalert/web.qtpl:283
+//line app/vmalert/web.qtpl:274
 	qw422016.N().S(`
 
 `)
-//line app/vmalert/web.qtpl:285
+//line app/vmalert/web.qtpl:276
 }
 
-//line app/vmalert/web.qtpl:285
+//line app/vmalert/web.qtpl:276
 func WriteAlert(qq422016 qtio422016.Writer, alert *APIAlert) {
-//line app/vmalert/web.qtpl:285
+//line app/vmalert/web.qtpl:276
 	qw422016 := qt422016.AcquireWriter(qq422016)
-//line app/vmalert/web.qtpl:285
+//line app/vmalert/web.qtpl:276
 	StreamAlert(qw422016, alert)
-//line app/vmalert/web.qtpl:285
+//line app/vmalert/web.qtpl:276
 	qt422016.ReleaseWriter(qw422016)
-//line app/vmalert/web.qtpl:285
+//line app/vmalert/web.qtpl:276
 }
 
-//line app/vmalert/web.qtpl:285
+//line app/vmalert/web.qtpl:276
 func Alert(alert *APIAlert) string {
-//line app/vmalert/web.qtpl:285
+//line app/vmalert/web.qtpl:276
 	qb422016 := qt422016.AcquireByteBuffer()
-//line app/vmalert/web.qtpl:285
+//line app/vmalert/web.qtpl:276
 	WriteAlert(qb422016, alert)
-//line app/vmalert/web.qtpl:285
+//line app/vmalert/web.qtpl:276
 	qs422016 := string(qb422016.B)
-//line app/vmalert/web.qtpl:285
+//line app/vmalert/web.qtpl:276
 	qt422016.ReleaseByteBuffer(qb422016)
-//line app/vmalert/web.qtpl:285
+//line app/vmalert/web.qtpl:276
 	return qs422016
-//line app/vmalert/web.qtpl:285
+//line app/vmalert/web.qtpl:276
 }