Replace www-path with www-proxy option for development (#248)

By adding a new ENV variable called `WOODPECKER_WWW_PROXY` it is possible to serve a webinterface via a proxy configured by the `WOODPECKER_WWW_PROXY` value for development instead of serving the interface from the bundled code or from some folder location as the old `WOODPECKER_WWW` option allowed. Using a proxy allows developing the UI with hot-reloading.
This commit is contained in:
Anbraten 2021-09-27 00:22:23 +02:00 committed by GitHub
parent da6fa0ec70
commit bd19f90756
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 27 additions and 47 deletions

View file

@ -64,9 +64,9 @@ var flags = []cli.Flag{
Usage: "enable quic", Usage: "enable quic",
}, },
cli.StringFlag{ cli.StringFlag{
EnvVar: "DRONE_WWW,WOODPECKER_WWW", EnvVar: "WOODPECKER_WWW_PROXY",
Name: "www", Name: "www-proxy",
Usage: "serve the website from disk", Usage: "serve the website by using a proxy (used for development)",
Hidden: true, Hidden: true,
}, },
cli.StringSliceFlag{ cli.StringSliceFlag{

View file

@ -20,6 +20,7 @@ import (
"errors" "errors"
"net" "net"
"net/http" "net/http"
"net/http/httputil"
"net/url" "net/url"
"os" "os"
"path/filepath" "path/filepath"
@ -90,13 +91,30 @@ func loop(c *cli.Context) error {
store_ := setupStore(c) store_ := setupStore(c)
setupEvilGlobals(c, store_, remote_) setupEvilGlobals(c, store_, remote_)
proxyWebUI := c.String("www-proxy")
var webUIServe func(w http.ResponseWriter, r *http.Request)
if proxyWebUI == "" {
// we are switching from gin to httpservermux|treemux, // we are switching from gin to httpservermux|treemux,
// so if this code looks strange, that is why. webUIServe = setupTree(c).ServeHTTP
tree := setupTree(c) } else {
origin, _ := url.Parse(proxyWebUI)
director := func(req *http.Request) {
req.Header.Add("X-Forwarded-Host", req.Host)
req.Header.Add("X-Origin-Host", origin.Host)
req.URL.Scheme = origin.Scheme
req.URL.Host = origin.Host
}
proxy := &httputil.ReverseProxy{Director: director}
webUIServe = proxy.ServeHTTP
}
// setup the server and start the listener // setup the server and start the listener
handler := router.Load( handler := router.Load(
tree, webUIServe,
ginrus.Ginrus(logrus.StandardLogger(), time.RFC3339, true), ginrus.Ginrus(logrus.StandardLogger(), time.RFC3339, true),
middleware.Version, middleware.Version,
middleware.Config(c), middleware.Config(c),

View file

@ -209,7 +209,6 @@ func setupCoding(c *cli.Context) (remote.Remote, error) {
func setupTree(c *cli.Context) *httptreemux.ContextMux { func setupTree(c *cli.Context) *httptreemux.ContextMux {
tree := httptreemux.NewContextMux() tree := httptreemux.NewContextMux()
web.New( web.New(
web.WithDir(c.String("www")),
web.WithSync(time.Hour*72), web.WithSync(time.Hour*72),
web.WithDocs(c.String("docs")), web.WithDocs(c.String("docs")),
).Register(tree) ).Register(tree)

View file

@ -17,7 +17,6 @@ package router
import ( import (
"net/http" "net/http"
"github.com/dimfeld/httptreemux"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/woodpecker-ci/woodpecker/server/api" "github.com/woodpecker-ci/woodpecker/server/api"
@ -30,7 +29,7 @@ import (
) )
// Load loads the router // Load loads the router
func Load(mux *httptreemux.ContextMux, middleware ...gin.HandlerFunc) http.Handler { func Load(serveHTTP func(w http.ResponseWriter, r *http.Request), middleware ...gin.HandlerFunc) http.Handler {
e := gin.New() e := gin.New()
e.Use(gin.Recovery()) e.Use(gin.Recovery())
@ -49,7 +48,7 @@ func Load(mux *httptreemux.ContextMux, middleware ...gin.HandlerFunc) http.Handl
session.User(c), session.User(c),
), ),
) )
mux.ServeHTTP(c.Writer, req) serveHTTP(c.Writer, req)
}) })
e.GET("/logout", api.GetLogout) e.GET("/logout", api.GetLogout)

View file

@ -19,7 +19,6 @@ import "time"
// Options defines website handler options. // Options defines website handler options.
type Options struct { type Options struct {
sync time.Duration sync time.Duration
path string
docs string docs string
} }
@ -34,14 +33,6 @@ func WithSync(d time.Duration) Option {
} }
} }
// WithDir configures the website handler with the directory value
// used to serve the website from the local filesystem.
func WithDir(s string) Option {
return func(o *Options) {
o.path = s
}
}
// WithDocs configures the website handler with the documentation // WithDocs configures the website handler with the documentation
// website address, which should be included in the user interface. // website address, which should be included in the user interface.
func WithDocs(s string) Option { func WithDocs(s string) Option {

View file

@ -27,14 +27,6 @@ func TestWithSync(t *testing.T) {
} }
} }
func TestWithDir(t *testing.T) {
opts := new(Options)
WithDir("/tmp/www")(opts)
if got, want := opts.path, "/tmp/www"; got != want {
t.Errorf("Want www directory %q, got %q", want, got)
}
}
func TestWithDocs(t *testing.T) { func TestWithDocs(t *testing.T) {
opts := new(Options) opts := new(Options)
WithDocs("http://docs.drone.io")(opts) WithDocs("http://docs.drone.io")(opts)

View file

@ -19,9 +19,7 @@ import (
"crypto/md5" "crypto/md5"
"fmt" "fmt"
"html/template" "html/template"
"io/ioutil"
"net/http" "net/http"
"path/filepath"
"time" "time"
"github.com/woodpecker-ci/woodpecker/model" "github.com/woodpecker-ci/woodpecker/model"
@ -45,10 +43,6 @@ func New(opt ...Option) Endpoint {
f(opts) f(opts)
} }
if opts.path != "" {
return fromPath(opts)
}
return &website{ return &website{
fs: dist.New(), fs: dist.New(),
opts: opts, opts: opts,
@ -58,19 +52,6 @@ func New(opt ...Option) Endpoint {
} }
} }
func fromPath(opts *Options) *website {
f := filepath.Join(opts.path, "index.html")
b, err := ioutil.ReadFile(f)
if err != nil {
panic(err)
}
return &website{
fs: http.Dir(opts.path),
tmpl: mustCreateTemplate(string(b)),
opts: opts,
}
}
type website struct { type website struct {
opts *Options opts *Options
fs http.FileSystem fs http.FileSystem