Add -web.prefix / WEB_PREFIX option

This allows people to use unsee with a sub uri setup, which is pretty common usa case, fixes #22
This commit is contained in:
Łukasz Mierzwa
2017-03-29 17:24:59 -07:00
committed by Jeppe Toustrup
parent 039240bd73
commit 0d82cffd79
9 changed files with 136 additions and 31 deletions

View File

@@ -260,6 +260,22 @@ This option can also be set using `-strip.labels` flag. Example:
This variable is optional and default is not set (all labels will be shown).
#### WEB_PREFIX
URL root for unsee, you can use to if you wish to serve it from location other
than /. Examples:
WEB_PREFIX=/unsee/
This will configure unsee to serve requests from http://localhost/unsee/
instead http://localhost/.
This option can also be set using `-web.prefix` flag. Example:
$ unsee -web.prefix /unsee/
Default is `/`.
## Contributing
Please see [CONTRIBUTING](/CONTRIBUTING.md) for details.

View File

@@ -13,9 +13,9 @@
<title>(◕︵◕)</title>
{{ range .CSSFiles }}
<link rel="stylesheet" href="/static/managed/css/{{ . }}"> {{- end }}
<link rel="stylesheet" href="{{ $.WebPrefix }}static/managed/css/{{ . }}"> {{- end }}
<link rel="stylesheet" href="/static/base.css">
<link rel="stylesheet" href="{{ .WebPrefix }}static/base.css">
</head>
@@ -248,7 +248,7 @@
<nav>
<ul class="pager">
<li>
<a href="/">
<a href="{{ .WebPrefix }}">
<i class="fa fa-arrow-circle-left"></i> Back
</a>
</li>

View File

@@ -8,15 +8,15 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<link rel="shortcut icon" id="favicon" href="/static/favicon.ico">
<link rel="shortcut icon" id="favicon" href="{{ .WebPrefix }}static/favicon.ico">
<title>(◕︵◕)</title>
{{ range .CSSFiles }}
<link rel="stylesheet" href="/static/managed/css/{{ . }}">
<link rel="stylesheet" href="{{ $.WebPrefix }}static/managed/css/{{ . }}">
{{- end }}
<link rel="stylesheet" href="/static/base.css?_={{ .NowQ }}">
<link rel="stylesheet" href="{{ .WebPrefix }}static/base.css?_={{ .NowQ }}">
</head>
@@ -144,7 +144,7 @@
</div>
{{ range .JSFiles }}
<script type="text/javascript" src="/static/managed/js/{{ . }}"></script>
<script type="text/javascript" src="{{ $.WebPrefix }}static/managed/js/{{ . }}"></script>
{{- end }}
{{ template "templates/js.html" .}}

View File

@@ -1,18 +1,18 @@
<script type="text/javascript" src="/static/jquery.typing-0.2.0.js"></script>
<script type="text/javascript" src="/static/lru.js"></script>
<script type="text/javascript" src="{{ .WebPrefix }}static/jquery.typing-0.2.0.js"></script>
<script type="text/javascript" src="{{ .WebPrefix }}static/lru.js"></script>
<script type="text/javascript" src="/static/alerts.js?_={{ .NowQ }}"></script>
<script type="text/javascript" src="/static/autocomplete.js?_={{ .NowQ }}"></script>
<script type="text/javascript" src="/static/config.js?_={{ .NowQ }}"></script>
<script type="text/javascript" src="/static/colors.js?_={{ .NowQ }}"></script>
<script type="text/javascript" src="/static/counter.js?_={{ .NowQ }}"></script>
<script type="text/javascript" src="/static/filters.js?_={{ .NowQ }}"></script>
<script type="text/javascript" src="/static/grid.js?_={{ .NowQ }}"></script>
<script type="text/javascript" src="/static/progress.js?_={{ .NowQ }}"></script>
<script type="text/javascript" src="/static/summary.js?_={{ .NowQ }}"></script>
<script type="text/javascript" src="/static/watchdog.js?_={{ .NowQ }}"></script>
<script type="text/javascript" src="/static/querystring.js?_={{ .NowQ }}"></script>
<script type="text/javascript" src="/static/unsee.js?_={{ .NowQ }}"></script>
<script type="text/javascript" src="{{ .WebPrefix }}static/alerts.js?_={{ .NowQ }}"></script>
<script type="text/javascript" src="{{ .WebPrefix }}static/autocomplete.js?_={{ .NowQ }}"></script>
<script type="text/javascript" src="{{ .WebPrefix }}static/config.js?_={{ .NowQ }}"></script>
<script type="text/javascript" src="{{ .WebPrefix }}static/colors.js?_={{ .NowQ }}"></script>
<script type="text/javascript" src="{{ .WebPrefix }}static/counter.js?_={{ .NowQ }}"></script>
<script type="text/javascript" src="{{ .WebPrefix }}static/filters.js?_={{ .NowQ }}"></script>
<script type="text/javascript" src="{{ .WebPrefix }}static/grid.js?_={{ .NowQ }}"></script>
<script type="text/javascript" src="{{ .WebPrefix }}static/progress.js?_={{ .NowQ }}"></script>
<script type="text/javascript" src="{{ .WebPrefix }}static/summary.js?_={{ .NowQ }}"></script>
<script type="text/javascript" src="{{ .WebPrefix }}static/watchdog.js?_={{ .NowQ }}"></script>
<script type="text/javascript" src="{{ .WebPrefix }}static/querystring.js?_={{ .NowQ }}"></script>
<script type="text/javascript" src="{{ .WebPrefix }}static/unsee.js?_={{ .NowQ }}"></script>
{{ if .SentryDSN }}
<script type="application/javascript">

File diff suppressed because one or more lines are too long

View File

@@ -36,6 +36,7 @@ type configEnvs struct {
SentryDSN string `envconfig:"SENTRY_DSN" help:"Sentry DSN for Go exceptions"`
SentryPublicDSN string `envconfig:"SENTRY_PUBLIC_DSN" help:"Sentry DSN for javascript exceptions"`
StripLabels spaceSeparatedList `envconfig:"STRIP_LABELS" help:"List of labels to ignore"`
WebPrefix string `envconfig:"WEB_PREFIX" default:"/" help:"URL prefix"`
}
// Config exposes all options required to run

26
main.go
View File

@@ -1,6 +1,8 @@
package main
import (
"path"
"strings"
"sync"
"time"
@@ -69,14 +71,24 @@ func init() {
metricAlertmanagerErrors.With(prometheus.Labels{"endpoint": "silences"}).Set(0)
}
func setupRouter(router *gin.Engine) {
router.Use(static.Serve("/static", newBinaryFileSystem("static")))
func getViewURL(sub string) string {
u := path.Join(config.Config.WebPrefix, sub)
if strings.HasSuffix(sub, "/") {
// if sub path had trailing slash then add it here, since path.Join will
// skip it
return u + "/"
}
return u
}
router.GET("/favicon.ico", favicon)
router.GET("/", index)
router.GET("/help", help)
router.GET("/alerts.json", alerts)
router.GET("/autocomplete.json", autocomplete)
func setupRouter(router *gin.Engine) {
router.Use(static.Serve(getViewURL("/static"), newBinaryFileSystem("static")))
router.GET(getViewURL("/favicon.ico"), favicon)
router.GET(getViewURL("/"), index)
router.GET(getViewURL("/help"), help)
router.GET(getViewURL("/alerts.json"), alerts)
router.GET(getViewURL("/autocomplete.json"), autocomplete)
}
func main() {

View File

@@ -61,6 +61,7 @@ func index(c *gin.Context) {
"QFilter": q,
"DefaultUsed": defaultUsed,
"StaticColorLabels": strings.Join(config.Config.ColorLabelsStatic, " "),
"WebPrefix": config.Config.WebPrefix,
})
log.Infof("[%s] %s %s took %s", c.ClientIP(), c.Request.Method, c.Request.RequestURI, time.Since(start))
@@ -74,6 +75,7 @@ func help(c *gin.Context) {
c.HTML(http.StatusOK, "templates/help.html", gin.H{
"CSSFiles": cssFiles,
"SentryDSN": config.Config.SentryPublicDSN,
"WebPrefix": config.Config.WebPrefix,
})
log.Infof("[%s] <%d> %s %s took %s", c.ClientIP(), http.StatusOK, c.Request.Method, c.Request.RequestURI, time.Since(start))
}
@@ -268,5 +270,8 @@ func autocomplete(c *gin.Context) {
}
func favicon(c *gin.Context) {
if config.Config.WebPrefix != "/" {
c.Request.URL.Path = strings.TrimPrefix(c.Request.URL.Path, config.Config.WebPrefix)
}
faviconFileServer.ServeHTTP(c.Writer, c.Request)
}

View File

@@ -45,6 +45,19 @@ func TestIndex(t *testing.T) {
}
}
func TestIndexPrefix(t *testing.T) {
os.Setenv("WEB_PREFIX", "/prefix")
defer os.Unsetenv("WEB_PREFIX")
mockConfig()
r := ginTestEngine()
req, _ := http.NewRequest("GET", "/prefix/", nil)
resp := httptest.NewRecorder()
r.ServeHTTP(resp, req)
if resp.Code != http.StatusOK {
t.Errorf("GET /prefix/ returned status %d", resp.Code)
}
}
func TestHelp(t *testing.T) {
mockConfig()
r := ginTestEngine()
@@ -56,6 +69,25 @@ func TestHelp(t *testing.T) {
}
}
func TestHelpPrefix(t *testing.T) {
os.Setenv("WEB_PREFIX", "/prefix")
defer os.Unsetenv("WEB_PREFIX")
mockConfig()
r := ginTestEngine()
req, _ := http.NewRequest("GET", "/prefix/help", nil)
resp := httptest.NewRecorder()
r.ServeHTTP(resp, req)
if resp.Code != http.StatusOK {
t.Errorf("GET /prefix/help returned status %d", resp.Code)
}
req, _ = http.NewRequest("GET", "/help", nil)
resp = httptest.NewRecorder()
r.ServeHTTP(resp, req)
if resp.Code != http.StatusNotFound {
t.Errorf("GET /help returned status %d, expected 404", resp.Code)
}
}
func mockAlerts() {
httpmock.Activate()
defer httpmock.DeactivateAndReset()
@@ -345,3 +377,42 @@ func TestStaticFiles(t *testing.T) {
}
}
}
var staticFilePrefixTests = []staticFileTestCase{
staticFileTestCase{
path: "/sub/favicon.ico",
code: 200,
},
staticFileTestCase{
path: "/sub/static/unsee.js",
code: 200,
},
staticFileTestCase{
path: "/sub/static/managed/js/assets.txt",
code: 200,
},
staticFileTestCase{
path: "/sub/xxx",
code: 404,
},
staticFileTestCase{
path: "/sub/static/abcd",
code: 404,
},
}
func TestStaticFilesPrefix(t *testing.T) {
os.Setenv("WEB_PREFIX", "/sub")
defer os.Unsetenv("WEB_PREFIX")
mockConfig()
mockAlerts()
r := ginTestEngine()
for _, staticFileTest := range staticFilePrefixTests {
req, _ := http.NewRequest("GET", staticFileTest.path, nil)
resp := httptest.NewRecorder()
r.ServeHTTP(resp, req)
if resp.Code != staticFileTest.code {
t.Errorf("Invalid status code for GET %s: %d", staticFileTest.path, resp.Code)
}
}
}