mirror of
https://github.com/mjl-/mox.git
synced 2024-12-27 08:53:48 +03:00
add updates serve command to serve a json or html version of the changelog.json, with contents based on ?from= query string
This commit is contained in:
parent
dfd7b721ae
commit
f867bcfa21
2 changed files with 144 additions and 0 deletions
1
main.go
1
main.go
|
@ -145,6 +145,7 @@ var commands = []struct {
|
||||||
{"updates addsigned", cmdUpdatesAddSigned},
|
{"updates addsigned", cmdUpdatesAddSigned},
|
||||||
{"updates genkey", cmdUpdatesGenkey},
|
{"updates genkey", cmdUpdatesGenkey},
|
||||||
{"updates pubkey", cmdUpdatesPubkey},
|
{"updates pubkey", cmdUpdatesPubkey},
|
||||||
|
{"updates serve", cmdUpdatesServe},
|
||||||
{"updates verify", cmdUpdatesVerify},
|
{"updates verify", cmdUpdatesVerify},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
143
updates.go
143
updates.go
|
@ -7,10 +7,14 @@ import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
htmltemplate "html/template"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mjl-/mox/moxio"
|
||||||
"github.com/mjl-/mox/updates"
|
"github.com/mjl-/mox/updates"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -132,3 +136,142 @@ func cmdUpdatesPubkey(c *cmd) {
|
||||||
err = enc.Close()
|
err = enc.Close()
|
||||||
xcheckf(err, "writing public key")
|
xcheckf(err, "writing public key")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var updatesTemplate = htmltemplate.Must(htmltemplate.New("changelog").Parse(`<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>mox changelog</title>
|
||||||
|
<style>
|
||||||
|
body, html { padding: 1em; font-size: 16px; }
|
||||||
|
* { font-size: inherit; font-family: ubuntu, lato, sans-serif; margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
h1, h2, h3, h4 { margin-bottom: 1ex; }
|
||||||
|
h1 { font-size: 1.2rem; }
|
||||||
|
.literal { background-color: #fdfdfd; padding: .5em 1em; border: 1px solid #eee; border-radius: 4px; white-space: pre-wrap; font-family: monospace; font-size: 15px; tab-size: 4; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Changes{{ if .FromVersion }} since {{ .FromVersion }}{{ end }}</h1>
|
||||||
|
{{ if not .Changes }}
|
||||||
|
<div>No changes</div>
|
||||||
|
{{ end }}
|
||||||
|
{{ range .Changes }}
|
||||||
|
<pre class="literal">{{ .Text }}</pre>
|
||||||
|
<hr style="margin:1ex 0" />
|
||||||
|
{{ end }}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`))
|
||||||
|
|
||||||
|
func cmdUpdatesServe(c *cmd) {
|
||||||
|
c.unlisted = true
|
||||||
|
c.help = "Serve changelog.json with updates."
|
||||||
|
var address, changelog string
|
||||||
|
c.flag.StringVar(&address, "address", "127.0.0.1:8596", "address to serve /changelog on")
|
||||||
|
c.flag.StringVar(&changelog, "changelog", "changelog.json", "changelog file to serve")
|
||||||
|
args := c.Parse()
|
||||||
|
if len(args) != 0 {
|
||||||
|
c.Usage()
|
||||||
|
}
|
||||||
|
|
||||||
|
parseFile := func() (*updates.Changelog, error) {
|
||||||
|
f, err := os.Open(changelog)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
var cl updates.Changelog
|
||||||
|
if err := json.NewDecoder(f).Decode(&cl); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &cl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := parseFile()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("parsing %s: %v", changelog, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
srv := http.NewServeMux()
|
||||||
|
srv.HandleFunc("/changelog", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
cl, err := parseFile()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("parsing %s: %v", changelog, err)
|
||||||
|
http.Error(w, "500 - internal server error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
from := r.URL.Query().Get("from")
|
||||||
|
var fromVersion *updates.Version
|
||||||
|
if from != "" {
|
||||||
|
v, err := updates.ParseVersion(from)
|
||||||
|
if err == nil {
|
||||||
|
fromVersion = &v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if fromVersion != nil {
|
||||||
|
nextchange:
|
||||||
|
for i, c := range cl.Changes {
|
||||||
|
for _, line := range strings.Split(strings.Split(c.Text, "\n\n")[0], "\n") {
|
||||||
|
if strings.HasPrefix(line, "version:") {
|
||||||
|
v, err := updates.ParseVersion(strings.TrimSpace(strings.TrimPrefix(line, "version:")))
|
||||||
|
if err == nil && !v.After(*fromVersion) {
|
||||||
|
cl.Changes = cl.Changes[:i]
|
||||||
|
break nextchange
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if client accepts html. If so, we'll provide a human-readable version.
|
||||||
|
accept := r.Header.Get("Accept")
|
||||||
|
var html bool
|
||||||
|
accept:
|
||||||
|
for _, ac := range strings.Split(accept, ",") {
|
||||||
|
var ok bool
|
||||||
|
for i, kv := range strings.Split(strings.TrimSpace(ac), ";") {
|
||||||
|
if i == 0 {
|
||||||
|
ct := strings.TrimSpace(kv)
|
||||||
|
if strings.EqualFold(ct, "text/html") || strings.EqualFold(ct, "text/*") {
|
||||||
|
ok = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
continue accept
|
||||||
|
}
|
||||||
|
t := strings.SplitN(strings.TrimSpace(kv), "=", 2)
|
||||||
|
if !strings.EqualFold(t[0], "q") || len(t) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch t[1] {
|
||||||
|
case "0", "0.", "0.0", "0.00", "0.000":
|
||||||
|
ok = false
|
||||||
|
continue accept
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
html = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if html {
|
||||||
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
|
err := updatesTemplate.Execute(w, map[string]any{
|
||||||
|
"FromVersion": fromVersion,
|
||||||
|
"Changes": cl.Changes,
|
||||||
|
})
|
||||||
|
if err != nil && !moxio.IsClosed(err) {
|
||||||
|
log.Printf("writing changelog html: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
if err := json.NewEncoder(w).Encode(cl); err != nil && !moxio.IsClosed(err) {
|
||||||
|
log.Printf("writing changelog json: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
log.Printf("listening on %s", address)
|
||||||
|
log.Fatalln(http.ListenAndServe(address, srv))
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue