Skip to content

Commit

Permalink
feat: Adding message, refactoring getting started and renderer pkg (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
ViBiOh authored Apr 19, 2020
1 parent 1d46479 commit 1b340bc
Show file tree
Hide file tree
Showing 20 changed files with 501 additions and 447 deletions.
25 changes: 17 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,15 @@

Thanks to [OpenEmoji](https://openmoji.org) for favicon.

## CI
## Getting started

Following variables are required for CI:
Golang binary is built with static link. You can download it directly from the [Github Release page](https://github.com/ViBiOh/ketchup/releases) or build it by yourself by cloning this repo and running `make`.

| Name | Purpose |
|:--:|:--:|
| **DOMAIN** | for setting Traefik domain for app |
| **DOCKER_USER** | for publishing Docker image |
| **DOCKER_PASS** | for publishing Docker image |
| **SCRIPTS_NO_INTERACTIVE** | for disabling prompt in CI |
A Docker image is available for `amd64`, `arm` and `arm64` platforms on Docker Hub: [vibioh/ketchup](https://hub.docker.com/r/vibioh/ketchup/tags).

You can configure app by passing CLI args or environment variables (cf. [Usage](#usage) section). CLI override environment variables.

You'll find a Kubernetes exemple (without secrets) in the [`infra/`](infra/) folder. We don't manage authentification and rely on Traefik basic-auth.

## Usage

Expand Down Expand Up @@ -96,3 +95,13 @@ Usage of ketchup:
-userAgent string
[alcotest] User-Agent for check {KETCHUP_USER_AGENT} (default "Alcotest")
```
## CI
Following variables are required for CI:
| Name | Purpose |
|:--:|:--:|
| **DOCKER_USER** | for publishing Docker image |
| **DOCKER_PASS** | for publishing Docker image |
| **SCRIPTS_NO_INTERACTIVE** | for disabling prompt in CI |
8 changes: 4 additions & 4 deletions cmd/ketchup/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ import (
"github.com/ViBiOh/httputils/v3/pkg/swagger"
"github.com/ViBiOh/ketchup/pkg/github"
"github.com/ViBiOh/ketchup/pkg/ketchup"
"github.com/ViBiOh/ketchup/pkg/renderer"
"github.com/ViBiOh/ketchup/pkg/target"
"github.com/ViBiOh/ketchup/pkg/ui"
mailer "github.com/ViBiOh/mailer/pkg/client"
)

Expand Down Expand Up @@ -63,7 +63,7 @@ func main() {
targetApp := target.New(ketchupDb, githubApp)
ketchupAp := ketchup.New(ketchupConfig, targetApp, githubApp, mailerApp)

uiApp, err := ui.New(targetApp)
rendererApp, err := renderer.New(targetApp)
logger.Fatal(err)

crudTargetApp, err := crud.New(crudTargetConfig, targetApp)
Expand All @@ -74,7 +74,7 @@ func main() {

swaggerHandler := http.StripPrefix(apiPath, swaggerApp.Handler())
crudTargetHandler := http.StripPrefix(targetPath, crudTargetApp.Handler())
uiHandler := uiApp.Handler()
rendererHandler := rendererApp.Handler()

handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(r.URL.Path, targetPath) {
Expand All @@ -90,7 +90,7 @@ func main() {
http.ServeFile(w, r, path.Join("static", r.URL.Path))
}

uiHandler.ServeHTTP(w, r)
rendererHandler.ServeHTTP(w, r)
})

go ketchupAp.Start()
Expand Down
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ module github.com/ViBiOh/ketchup
go 1.14

require (
github.com/ViBiOh/httputils/v3 v3.13.0
github.com/ViBiOh/httputils/v3 v3.14.0
github.com/ViBiOh/mailer v1.6.3
github.com/golang/protobuf v1.4.0 // indirect
github.com/prometheus/procfs v0.0.11 // indirect
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 // indirect
)
22 changes: 20 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
github.com/ViBiOh/httputils/v3 v3.8.3/go.mod h1:yOu5e4BzPMl7jjXOSf0nKYsHq9AJCY455yDcLlPjV7U=
github.com/ViBiOh/httputils/v3 v3.13.0 h1:AMsqtoQREdNeTTJToJyZ6SfmcZyuEN8uhD14Ym7BgtI=
github.com/ViBiOh/httputils/v3 v3.13.0/go.mod h1:exwp/UNyjMLfZTIl8i7x+AfBc4+9x2ar1VDvV0qWbv0=
github.com/ViBiOh/httputils/v3 v3.14.0 h1:0h6om6YRcugyyVxsnJnAPXlIZ4SBBW8cSrB4tWT6hGE=
github.com/ViBiOh/httputils/v3 v3.14.0/go.mod h1:exwp/UNyjMLfZTIl8i7x+AfBc4+9x2ar1VDvV0qWbv0=
github.com/ViBiOh/mailer v1.6.3 h1:FPau3ISU4duB/DDvvfp93s/mXV897VNFxoyZkHy1mug=
github.com/ViBiOh/mailer v1.6.3/go.mod h1:0LLlT/jx2gYWT355fUU381NFlJ0PyPuIuLoAymm8PfQ=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
Expand Down Expand Up @@ -28,6 +28,13 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
Expand Down Expand Up @@ -72,6 +79,8 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.0.11 h1:DhHlBtkHWPYi8O2y31JkK0TF+DGM+51OopZjH/Ia5qI=
github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
Expand Down Expand Up @@ -104,11 +113,20 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200103143344-a1369afcdac7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 h1:opSr2sbRXk5X5/givKrrKj9HXxFpW2sdCiP8MJSKLQY=
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
13 changes: 10 additions & 3 deletions pkg/ketchup/ketchup.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func (a app) checkUpdates(_ time.Time) error {
newReleases = append(newReleases, release)

if _, err = a.targetApp.Update(context.Background(), target); err != nil {
logger.Error("unable to update target %s: %s", target.Repository, err)
return fmt.Errorf("unable to update target %s: %s", target.Repository, err)
}
} else if release.TagName == target.CurrentVersion {
logger.Info("%s is up-to-date!", target.Repository)
Expand All @@ -96,8 +96,15 @@ func (a app) checkUpdates(_ time.Time) error {
if len(newReleases) > 0 {
if a.mailerApp == nil || !a.mailerApp.Enabled() {
logger.Warn("mailer is not configured")
} else if err := mailer.NewEmail(a.mailerApp).Template("ketchup").From("ketchup@vibioh.fr").As("Ketchup").WithSubject("Ketchup - New update").To(a.emailTo).Data(newReleases).Send(context.Background()); err != nil {
logger.Error("unable to send email to %s: %s", a.emailTo, err)
return nil
}

payload := map[string]interface{}{
"targets": newReleases,
}

if err := mailer.NewEmail(a.mailerApp).Template("ketchup").From("ketchup@vibioh.fr").As("Ketchup").WithSubject("Ketchup - New update").To(a.emailTo).Data(payload).Send(context.Background()); err != nil {
return fmt.Errorf("unable to send email to %s: %s", a.emailTo, err)
}
}

Expand Down
6 changes: 0 additions & 6 deletions pkg/model/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,3 @@ package model
type RowScanner interface {
Scan(...interface{}) error
}

// Message for render
type Message struct {
Level string
Content string
}
77 changes: 77 additions & 0 deletions pkg/renderer/handlers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package renderer

import (
"fmt"
"net/http"
"strings"

"github.com/ViBiOh/httputils/v3/pkg/httperror"
"github.com/ViBiOh/httputils/v3/pkg/logger"
"github.com/ViBiOh/httputils/v3/pkg/templates"
)

func (a app) getData(r *http.Request) (interface{}, error) {
targets, _, err := a.targetApp.List(r.Context(), 1, 100, "", false, nil)

return targets, err
}

func (a app) uiHandler(w http.ResponseWriter, r *http.Request, status int, message Message) {
targets, err := a.getData(r)
if err != nil {
a.errorHandler(w, http.StatusInternalServerError, err, nil)
return
}

content := map[string]interface{}{
"Version": a.version,
"Targets": targets,
}

if len(message.Content) > 0 {
content["Message"] = message
}

if err := templates.ResponseHTMLTemplate(a.tpl.Lookup("app"), w, content, status); err != nil {
httperror.InternalServerError(w, err)
}
}

func (a app) errorHandler(w http.ResponseWriter, status int, errs ...error) {
logger.Error("%s", errs)

content := map[string]interface{}{
"Version": a.version,
}

if len(errs) > 0 {
content["Message"] = Message{
Level: "error",
Content: errs[0].Error(),
}

if len(errs) > 1 {
content["Errors"] = errs[1:]
}
}

if err := templates.ResponseHTMLTemplate(a.tpl.Lookup("error"), w, content, status); err != nil {
httperror.InternalServerError(w, err)
return
}
}

func (a app) svg() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
tpl := a.tpl.Lookup(fmt.Sprintf("svg-%s", strings.Trim(r.URL.Path, "/")))
if tpl == nil {
httperror.NotFound(w)
return
}

w.Header().Set("Content-Type", "image/svg+xml")
if err := templates.WriteTemplate(tpl, w, r.URL.Query().Get("fill"), "text/xml"); err != nil {
httperror.InternalServerError(w, err)
}
})
}
7 changes: 7 additions & 0 deletions pkg/renderer/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package renderer

// Message for render
type Message struct {
Level string
Content string
}
47 changes: 11 additions & 36 deletions pkg/ui/ui.go → pkg/renderer/renderer.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
package ui
package renderer

import (
"errors"
"fmt"
"html/template"
"net/http"
"os"
"strings"

"github.com/ViBiOh/httputils/v3/pkg/crud"
"github.com/ViBiOh/httputils/v3/pkg/httperror"
"github.com/ViBiOh/httputils/v3/pkg/logger"
"github.com/ViBiOh/httputils/v3/pkg/query"
"github.com/ViBiOh/httputils/v3/pkg/templates"
"github.com/ViBiOh/ketchup/pkg/model"
"github.com/ViBiOh/ketchup/pkg/target"
)

Expand Down Expand Up @@ -57,42 +55,19 @@ func (a app) Handler() http.Handler {
return
}

if strings.HasPrefix(r.URL.Path, targetsPath) {
targetsHandler.ServeHTTP(w, r)
if query.IsRoot(r) {
a.uiHandler(w, r, http.StatusOK, Message{
Level: r.URL.Query().Get("messageLevel"),
Content: r.URL.Query().Get("messageContent"),
})
return
}

targets, _, err := a.targetApp.List(r.Context(), 1, 100, "", false, nil)
if err != nil {
a.handleError(w, http.StatusInternalServerError, err, nil)
if strings.HasPrefix(r.URL.Path, targetsPath) {
targetsHandler.ServeHTTP(w, r)
return
}

content := map[string]interface{}{
"Version": a.version,
"Targets": targets,
}

if err := templates.ResponseHTMLTemplate(a.tpl.Lookup("app"), w, content, http.StatusOK); err != nil {
httperror.InternalServerError(w, err)
}
a.errorHandler(w, http.StatusNotFound, errors.New("page not found"), nil)
})
}

func (a app) handleError(w http.ResponseWriter, status int, err error, errors []crud.Error) {
logger.Error("%s", err)

content := map[string]interface{}{
"Version": a.version,
"Message": model.Message{
Level: "error",
Content: err.Error(),
},
"Errors": errors,
}

if err := templates.ResponseHTMLTemplate(a.tpl.Lookup("error"), w, content, status); err != nil {
httperror.InternalServerError(w, err)
return
}
}
Loading

0 comments on commit 1b340bc

Please sign in to comment.