|
|
|
@ -186,15 +186,33 @@ type StatusUpdater interface {
|
|
|
|
|
func (st *Status) WriteHTML(w io.Writer) {
|
|
|
|
|
f := func(format string, args ...interface{}) { fmt.Fprintf(w, format, args...) }
|
|
|
|
|
|
|
|
|
|
f(`<html><head><style>
|
|
|
|
|
.owner { font-size: 80%%; color: #444; }
|
|
|
|
|
.tailaddr { font-size: 80%%; font-family: monospace: }
|
|
|
|
|
</style></head>`)
|
|
|
|
|
f("<body><h1>Tailscale State</h1>")
|
|
|
|
|
f(`<!DOCTYPE html>
|
|
|
|
|
<html lang="en">
|
|
|
|
|
<head>
|
|
|
|
|
<title>Tailscale State</title>
|
|
|
|
|
<style>
|
|
|
|
|
body { font-family: monospace; }
|
|
|
|
|
.owner { text-decoration: underline; }
|
|
|
|
|
.tailaddr { font-style: italic; }
|
|
|
|
|
.acenter { text-align: center; }
|
|
|
|
|
.aright { text-align: right; }
|
|
|
|
|
table, th, td { border: 1px solid black; border-spacing : 0; border-collapse : collapse; }
|
|
|
|
|
thead { background-color: #FFA500; }
|
|
|
|
|
th, td { padding: 5px; }
|
|
|
|
|
td { vertical-align: top; }
|
|
|
|
|
table tbody tr:nth-child(even) td { background-color: #f5f5f5; }
|
|
|
|
|
</style>
|
|
|
|
|
</head>
|
|
|
|
|
<body>
|
|
|
|
|
<h1>Tailscale State</h1>
|
|
|
|
|
`)
|
|
|
|
|
|
|
|
|
|
//f("<p><b>logid:</b> %s</p>\n", logid)
|
|
|
|
|
//f("<p><b>opts:</b> <code>%s</code></p>\n", html.EscapeString(fmt.Sprintf("%+v", opts)))
|
|
|
|
|
|
|
|
|
|
f("<table border=1 cellpadding=5><tr><th>Peer</th><th>Node</th><th>Rx</th><th>Tx</th><th>Handshake</th><th>Endpoints</th></tr>")
|
|
|
|
|
f("<table>\n<thead>\n")
|
|
|
|
|
f("<tr><th>Peer</th><th>Node</th><th>Owner</th><th>Rx</th><th>Tx</th><th>Handshake</th><th>Endpoints</th></tr>\n")
|
|
|
|
|
f("</thead>\n<tbody>\n")
|
|
|
|
|
|
|
|
|
|
now := time.Now()
|
|
|
|
|
|
|
|
|
@ -224,33 +242,35 @@ func (st *Status) WriteHTML(w io.Writer) {
|
|
|
|
|
owner = owner[:i]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
f("<tr><td>%s</td><td>%s<div class=owner>%s</div><div class=tailaddr>%s</div></td><td>%v</td><td>%v</td><td>%v</td>",
|
|
|
|
|
f("<tr><td>%s</td><td>%s %s<br><span class=\"tailaddr\">%s</span></td><td class=\"acenter owner\">%s</td><td class=\"aright\">%v</td><td class=\"aright\">%v</td><td class=\"aright\">%v</td>",
|
|
|
|
|
peer.ShortString(),
|
|
|
|
|
osEmoji(ps.OS)+" "+html.EscapeString(ps.SimpleHostName()),
|
|
|
|
|
html.EscapeString(owner),
|
|
|
|
|
html.EscapeString(ps.SimpleHostName()),
|
|
|
|
|
osEmoji(ps.OS),
|
|
|
|
|
ps.TailAddr,
|
|
|
|
|
html.EscapeString(owner),
|
|
|
|
|
ps.RxBytes,
|
|
|
|
|
ps.TxBytes,
|
|
|
|
|
hsAgo,
|
|
|
|
|
)
|
|
|
|
|
f("<td>")
|
|
|
|
|
f("<td class=\"aright\">")
|
|
|
|
|
match := false
|
|
|
|
|
for _, addr := range ps.Addrs {
|
|
|
|
|
if addr == ps.CurAddr {
|
|
|
|
|
match = true
|
|
|
|
|
f("<b>%s</b> 🔗<br>\n", addr)
|
|
|
|
|
f("🔗 <b>%s</b><br>", addr)
|
|
|
|
|
} else {
|
|
|
|
|
f("%s<br>\n", addr)
|
|
|
|
|
f("%s<br>", addr)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ps.CurAddr != "" && !match {
|
|
|
|
|
f("<b>%s</b> \xf0\x9f\xa7\xb3<br>\n", ps.CurAddr)
|
|
|
|
|
f("<b>%s</b> \xf0\x9f\xa7\xb3<br>", ps.CurAddr)
|
|
|
|
|
}
|
|
|
|
|
f("</tr>") // end Addrs
|
|
|
|
|
f("</td>") // end Addrs
|
|
|
|
|
|
|
|
|
|
f("</tr>\n")
|
|
|
|
|
}
|
|
|
|
|
f("</table>")
|
|
|
|
|
f("</tbody>\n</table>\n")
|
|
|
|
|
f("</body>\n</html>\n")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func osEmoji(os string) string {
|
|
|
|
|