mirror of
https://github.com/Luzifer/go-latestver.git
synced 2024-11-12 17:02:41 +00:00
Add feed generator
This commit is contained in:
parent
b364743a0b
commit
c18b3f2368
4 changed files with 98 additions and 14 deletions
97
api.go
97
api.go
|
@ -2,18 +2,29 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/Luzifer/go-latestver/internal/config"
|
"github.com/Luzifer/go-latestver/internal/config"
|
||||||
"github.com/Luzifer/go-latestver/internal/database"
|
"github.com/Luzifer/go-latestver/internal/database"
|
||||||
"github.com/Luzifer/go-latestver/internal/fetcher"
|
"github.com/Luzifer/go-latestver/internal/fetcher"
|
||||||
|
"github.com/gorilla/feeds"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/pkg/errors"
|
||||||
log "github.com/sirupsen/logrus"
|
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) {
|
func handleCatalogGet(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
var (
|
||||||
vars = mux.Vars(r)
|
vars = mux.Vars(r)
|
||||||
|
@ -101,6 +112,78 @@ func handleCatalogList(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleLog(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 (
|
var (
|
||||||
vars = mux.Vars(r)
|
vars = mux.Vars(r)
|
||||||
name, tag = vars["name"], vars["tag"]
|
name, tag = vars["name"], vars["tag"]
|
||||||
|
@ -124,19 +207,11 @@ func handleLog(w http.ResponseWriter, r *http.Request) {
|
||||||
} else {
|
} else {
|
||||||
ce, err := configFile.CatalogEntryByTag(name, tag)
|
ce, err := configFile.CatalogEntryByTag(name, tag)
|
||||||
if errors.Is(err, config.ErrCatalogEntryNotFound) {
|
if errors.Is(err, config.ErrCatalogEntryNotFound) {
|
||||||
http.Error(w, "Not found", http.StatusNotFound)
|
return nil, config.ErrCatalogEntryNotFound
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logs, err = storage.Logs.ListForCatalogEntry(&ce, num, page)
|
logs, err = storage.Logs.ListForCatalogEntry(&ce, num, page)
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
return logs, errors.Wrap(err, "listing log entries")
|
||||||
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) {} // FIXME
|
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -29,6 +29,7 @@ require (
|
||||||
github.com/go-git/go-git/v5 v5.4.2 // indirect
|
github.com/go-git/go-git/v5 v5.4.2 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // 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/imdario/mergo v0.3.12 // indirect
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -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.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.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/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 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
|
12
main.go
12
main.go
|
@ -18,6 +18,7 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
cfg = struct {
|
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"`
|
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"`
|
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)"`
|
LogLevel string `flag:"log-level" default:"info" description:"Log level (debug, info, warn, error, fatal)"`
|
||||||
|
@ -28,6 +29,7 @@ var (
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
configFile = config.New()
|
configFile = config.New()
|
||||||
|
router *mux.Router
|
||||||
storage *database.Client
|
storage *database.Client
|
||||||
|
|
||||||
version = "dev"
|
version = "dev"
|
||||||
|
@ -73,14 +75,18 @@ func main() {
|
||||||
scheduler.AddFunc("@every 1m", schedulerRun)
|
scheduler.AddFunc("@every 1m", schedulerRun)
|
||||||
scheduler.Start()
|
scheduler.Start()
|
||||||
|
|
||||||
router := mux.NewRouter()
|
router = mux.NewRouter()
|
||||||
router.HandleFunc("/v1/catalog", handleCatalogList).Methods(http.MethodGet)
|
router.HandleFunc("/v1/catalog", handleCatalogList).Methods(http.MethodGet)
|
||||||
router.HandleFunc("/v1/catalog/{name}/{tag}", handleCatalogGet).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", 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/catalog/{name}/{tag}/version", handleCatalogGetVersion).Methods(http.MethodGet)
|
||||||
router.HandleFunc("/v1/log", handleLog).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
|
var handler http.Handler = router
|
||||||
handler = httpHelper.GzipHandler(handler)
|
handler = httpHelper.GzipHandler(handler)
|
||||||
|
|
Loading…
Reference in a new issue