Add feed generator

This commit is contained in:
Knut Ahlers 2021-11-22 16:05:03 +01:00
parent b364743a0b
commit c18b3f2368
Signed by: luzifer
GPG key ID: 0066F03ED215AD7D
4 changed files with 98 additions and 14 deletions

97
api.go
View file

@ -2,18 +2,29 @@ package main
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
"time"
"github.com/Luzifer/go-latestver/internal/config"
"github.com/Luzifer/go-latestver/internal/database"
"github.com/Luzifer/go-latestver/internal/fetcher"
"github.com/gorilla/feeds"
"github.com/gorilla/mux"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)
func buildFullURL(u *url.URL) string {
return strings.Join([]string{
strings.TrimRight(cfg.BaseURL, "/"),
strings.TrimLeft(u.String(), "/"),
}, "/")
}
func handleCatalogGet(w http.ResponseWriter, r *http.Request) {
var (
vars = mux.Vars(r)
@ -101,6 +112,78 @@ func handleCatalogList(w http.ResponseWriter, r *http.Request) {
}
func handleLog(w http.ResponseWriter, r *http.Request) {
logs, err := prepareLogForRequest(r)
switch err {
case nil:
// This is fine
case config.ErrCatalogEntryNotFound:
http.Error(w, "Not found", http.StatusNotFound)
default:
http.Error(w, "Unable to fetch logs", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
if err = json.NewEncoder(w).Encode(logs); err != nil {
log.WithError(err).Error("Unable to encode logs")
http.Error(w, "Unable to encode logs", http.StatusInternalServerError)
return
}
}
func handleLogFeed(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
logs, err := prepareLogForRequest(r)
switch err {
case nil:
// This is fine
case config.ErrCatalogEntryNotFound:
http.Error(w, "Not found", http.StatusNotFound)
default:
http.Error(w, "Unable to fetch logs", http.StatusInternalServerError)
return
}
var (
feedTitle = "Latestver Update Log"
feedURL, _ = router.Get("catalog").URL()
)
if vars["name"] != "" {
feedTitle = fmt.Sprintf("Latestver Update Log of %s:%s", vars["name"], vars["tag"])
feedURL, _ = router.Get("catalog-entry").URL("name", vars["name"], "tag", vars["tag"])
}
feed := &feeds.Feed{
Description: "Generated by go-latestver: https://github.com/Luzifer/go-latestver",
Link: &feeds.Link{Href: buildFullURL(feedURL)},
Title: feedTitle,
}
for _, le := range logs {
catalogEntryURL, _ := router.Get("catalog-entry").URL("name", le.CatalogName, "tag", le.CatalogTag)
feed.Add(&feeds.Item{
Created: le.Timestamp.UTC(),
Description: fmt.Sprintf("%s:%s updated to version %s from %s", le.CatalogName, le.CatalogTag, le.VersionTo, le.VersionFrom),
Id: fmt.Sprintf("%s:%s-%s", le.CatalogName, le.CatalogTag, le.Timestamp.UTC().Format(time.RFC3339)),
Link: &feeds.Link{Href: buildFullURL(catalogEntryURL)},
Title: fmt.Sprintf("%s:%s %s", le.CatalogName, le.CatalogTag, le.VersionTo),
})
}
w.Header().Set("Content-Type", "application/rss+xml; charset=utf-8")
if err = feed.WriteRss(w); err != nil {
log.WithError(err).Error("Unable to render RSS")
http.Error(w, "Unable to render RSS", http.StatusInternalServerError)
return
}
}
func prepareLogForRequest(r *http.Request) ([]database.LogEntry, error) {
var (
vars = mux.Vars(r)
name, tag = vars["name"], vars["tag"]
@ -124,19 +207,11 @@ func handleLog(w http.ResponseWriter, r *http.Request) {
} else {
ce, err := configFile.CatalogEntryByTag(name, tag)
if errors.Is(err, config.ErrCatalogEntryNotFound) {
http.Error(w, "Not found", http.StatusNotFound)
return
return nil, config.ErrCatalogEntryNotFound
}
logs, err = storage.Logs.ListForCatalogEntry(&ce, num, page)
}
w.Header().Set("Content-Type", "application/json")
if err = json.NewEncoder(w).Encode(logs); err != nil {
log.WithError(err).Error("Unable to encode logs")
http.Error(w, "Unable to encode logs", http.StatusInternalServerError)
return
}
return logs, errors.Wrap(err, "listing log entries")
}
func handleLogFeed(w http.ResponseWriter, r *http.Request) {} // FIXME

1
go.mod
View file

@ -29,6 +29,7 @@ require (
github.com/go-git/go-git/v5 v5.4.2 // indirect
github.com/go-sql-driver/mysql v1.6.0 // indirect
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/gorilla/feeds v1.1.1 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect

2
go.sum
View file

@ -66,6 +66,8 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/gorilla/feeds v1.1.1 h1:HwKXxqzcRNg9to+BbvJog4+f3s/xzvtZXICcQGutYfY=
github.com/gorilla/feeds v1.1.1/go.mod h1:Nk0jZrvPFZX1OBe5NPiddPw7CfwF6Q9eqzaBbaightA=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=

12
main.go
View file

@ -18,6 +18,7 @@ import (
var (
cfg = struct {
BaseURL string `flag:"base-url" default:"https://example.com/" description:"Base-URL the application is reachable at"`
Config string `flag:"config,c" default:"config.yaml" description:"Configuration file with catalog entries"`
Listen string `flag:"listen" default:":3000" description:"Port/IP to listen on"`
LogLevel string `flag:"log-level" default:"info" description:"Log level (debug, info, warn, error, fatal)"`
@ -28,6 +29,7 @@ var (
}{}
configFile = config.New()
router *mux.Router
storage *database.Client
version = "dev"
@ -73,14 +75,18 @@ func main() {
scheduler.AddFunc("@every 1m", schedulerRun)
scheduler.Start()
router := mux.NewRouter()
router = mux.NewRouter()
router.HandleFunc("/v1/catalog", handleCatalogList).Methods(http.MethodGet)
router.HandleFunc("/v1/catalog/{name}/{tag}", handleCatalogGet).Methods(http.MethodGet)
router.HandleFunc("/v1/catalog/{name}/{tag}/log", handleLog).Methods(http.MethodGet)
router.HandleFunc("/v1/catalog/{name}/{tag}/log.rss", handleLogFeed).Methods(http.MethodGet)
router.HandleFunc("/v1/catalog/{name}/{tag}/version", handleCatalogGetVersion).Methods(http.MethodGet)
router.HandleFunc("/v1/log", handleLog).Methods(http.MethodGet)
router.HandleFunc("/v1/log.rss", handleLogFeed).Methods(http.MethodGet)
router.HandleFunc("/", nil).Methods(http.MethodGet).Name("catalog")
router.HandleFunc("/{name}/{tag}", nil).Methods(http.MethodGet).Name("catalog-entry")
router.HandleFunc("/{name}/{tag}/log.rss", handleLogFeed).Methods(http.MethodGet).Name("catalog-entry-rss")
router.HandleFunc("/log", nil).Methods(http.MethodGet)
router.HandleFunc("/log.rss", handleLogFeed).Methods(http.MethodGet).Name("log-rss")
var handler http.Handler = router
handler = httpHelper.GzipHandler(handler)