added support for running gowatch under a reverse proxy path
This commit is contained in:
parent
ecac18a0b7
commit
1a83bdcd86
16 changed files with 177 additions and 96 deletions
10
README.md
10
README.md
|
@ -13,6 +13,7 @@ A change detection server that can notify through various services, written in G
|
|||
- [Pruning](#pruning)
|
||||
- [Proxy](#proxy)
|
||||
- [Proxy Pools](#proxy-pools)
|
||||
- [Reverse Proxy](#reverse-proxy)
|
||||
- [Browserless](#browserless)
|
||||
- [Authentication](#Authentication)
|
||||
- [Filters](#filters)
|
||||
|
@ -222,6 +223,15 @@ cache_peer proxy2.com parent 3128 0 round-robin no-query login=user:pass
|
|||
|
||||
An example `squid.conf` can be found in [docs/proxy/squid-1.conf](docs/proxy/squid-1.conf).
|
||||
|
||||
## Reverse Proxy
|
||||
|
||||
GoWatch can be run behind a reverse proxy, if it's hosted under a subdomain (https://gowatch.domain.tld), not changes to the config are needed.
|
||||
But if you want to run GoWatch under a path (https://domain.tld/gowatch), you can set the `gin.urlprefix` value in the config or the `GOWATCH_URLPREFIX` environment variable can be used.
|
||||
```
|
||||
gin:
|
||||
urlprefix: "/gowatch"
|
||||
```
|
||||
|
||||
## Browserless
|
||||
|
||||
Some websites (Amazon for example) don't send all content on the first request, it's added later through javascript.
|
||||
|
|
|
@ -47,5 +47,6 @@ browserless:
|
|||
url: http://your.browserless:1234
|
||||
gin:
|
||||
debug: false
|
||||
urlprefix: "/"
|
||||
schedule:
|
||||
delay: "5s"
|
165
main.go
165
main.go
|
@ -16,6 +16,7 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -46,6 +47,7 @@ type Web struct {
|
|||
db *gorm.DB // gorm db instance
|
||||
notifiers map[string]notifiers.Notifier // holds notifierName -> notifier
|
||||
startupWarnings []string // simple list of warnings/errors found during startup, displayed on / page
|
||||
urlPrefix string // allows gowatch to run behind a reverse proxy on a subpath
|
||||
}
|
||||
|
||||
// NewWeb creates a new web instance and calls .init() before returning it
|
||||
|
@ -168,39 +170,49 @@ func (web *Web) initDB() {
|
|||
func (web *Web) initRouter() {
|
||||
web.router = gin.Default()
|
||||
|
||||
web.initTemplates()
|
||||
web.router.HTMLRender = web.templates
|
||||
|
||||
if viper.IsSet("gin.urlprefix") {
|
||||
urlPrefix := viper.GetString("gin.urlprefix")
|
||||
urlPrefix = path.Join("/", urlPrefix) + "/"
|
||||
web.urlPrefix = urlPrefix
|
||||
log.Println("Running under path: " + web.urlPrefix)
|
||||
} else {
|
||||
web.urlPrefix = "/"
|
||||
}
|
||||
|
||||
staticFS, err := fs.Sub(EMBED_FS, "static")
|
||||
if err != nil {
|
||||
log.Fatalln("Could not load static embed fs")
|
||||
}
|
||||
web.router.StaticFS("/static", http.FS(staticFS))
|
||||
web.router.StaticFileFS("/favicon.ico", "favicon.ico", http.FS(staticFS))
|
||||
web.router.StaticFS(web.urlPrefix+"static", http.FS(staticFS))
|
||||
web.router.StaticFileFS(web.urlPrefix+"favicon.ico", "favicon.ico", http.FS(staticFS))
|
||||
|
||||
web.initTemplates()
|
||||
web.router.HTMLRender = web.templates
|
||||
gowatch := web.router.Group(web.urlPrefix)
|
||||
|
||||
web.router.GET("/", web.index)
|
||||
gowatch.GET("", web.index)
|
||||
gowatch.GET("watch/view/:id", web.watchView)
|
||||
gowatch.GET("watch/edit/:id", web.watchEdit)
|
||||
gowatch.GET("watch/create", web.watchCreate)
|
||||
gowatch.POST("watch/create", web.watchCreatePost)
|
||||
gowatch.POST("watch/update", web.watchUpdate)
|
||||
gowatch.POST("watch/delete", web.deleteWatch)
|
||||
gowatch.GET("watch/export/:id", web.exportWatch)
|
||||
gowatch.POST("watch/import/:id", web.importWatch)
|
||||
|
||||
web.router.GET("/watch/view/:id", web.watchView)
|
||||
web.router.GET("/watch/edit/:id", web.watchEdit)
|
||||
web.router.GET("/watch/create", web.watchCreate)
|
||||
web.router.POST("/watch/create", web.watchCreatePost)
|
||||
web.router.POST("/watch/update", web.watchUpdate)
|
||||
web.router.POST("/watch/delete", web.deleteWatch)
|
||||
web.router.GET("/watch/export/:id", web.exportWatch)
|
||||
web.router.POST("/watch/import/:id", web.importWatch)
|
||||
gowatch.GET("cache/view", web.cacheView)
|
||||
gowatch.POST("cache/clear", web.cacheClear)
|
||||
|
||||
web.router.GET("/cache/view", web.cacheView)
|
||||
web.router.POST("/cache/clear", web.cacheClear)
|
||||
gowatch.GET("notifiers/view", web.notifiersView)
|
||||
gowatch.POST("notifiers/test", web.notifiersTest)
|
||||
|
||||
web.router.GET("/notifiers/view", web.notifiersView)
|
||||
web.router.POST("/notifiers/test", web.notifiersTest)
|
||||
|
||||
web.router.GET("/backup/view", web.backupView)
|
||||
web.router.GET("/backup/create", web.backupCreate)
|
||||
web.router.POST("/backup/test", web.backupTest)
|
||||
web.router.POST("/backup/restore", web.backupRestore)
|
||||
web.router.POST("/backup/delete", web.backupDelete)
|
||||
web.router.GET("/backup/download/:id", web.backupDownload)
|
||||
gowatch.GET("backup/view", web.backupView)
|
||||
gowatch.GET("backup/create", web.backupCreate)
|
||||
gowatch.POST("backup/test", web.backupTest)
|
||||
gowatch.POST("backup/restore", web.backupRestore)
|
||||
gowatch.POST("backup/delete", web.backupDelete)
|
||||
gowatch.GET("backup/download/:id", web.backupDownload)
|
||||
|
||||
web.router.SetTrustedProxies(nil)
|
||||
}
|
||||
|
@ -448,14 +460,18 @@ func (web *Web) index(c *gin.Context) {
|
|||
}
|
||||
|
||||
c.HTML(http.StatusOK, "index", gin.H{
|
||||
"watches": watches,
|
||||
"warnings": web.startupWarnings,
|
||||
"watches": watches,
|
||||
"warnings": web.startupWarnings,
|
||||
"urlPrefix": web.urlPrefix,
|
||||
})
|
||||
}
|
||||
|
||||
// notifiersView (/notifiers/view) shows the notifiers and a test button
|
||||
func (web *Web) notifiersView(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "notifiersView", web.notifiers)
|
||||
c.HTML(http.StatusOK, "notifiersView", gin.H{
|
||||
"notifiers": web.notifiers,
|
||||
"urlPrefix": web.urlPrefix,
|
||||
})
|
||||
}
|
||||
|
||||
// notifiersTest (/notifiers/test) sends a test message to notifier_name
|
||||
|
@ -463,11 +479,11 @@ func (web *Web) notifiersTest(c *gin.Context) {
|
|||
notifierName := c.PostForm("notifier_name")
|
||||
notifier, exists := web.notifiers[notifierName]
|
||||
if !exists {
|
||||
c.Redirect(http.StatusSeeOther, "/notifiers/view")
|
||||
c.Redirect(http.StatusSeeOther, web.urlPrefix+"notifiers/view")
|
||||
return
|
||||
}
|
||||
notifier.Message("GoWatch Test")
|
||||
c.Redirect(http.StatusSeeOther, "/notifiers/view")
|
||||
c.Redirect(http.StatusSeeOther, web.urlPrefix+"notifiers/view")
|
||||
}
|
||||
|
||||
// watchCreate (/watch/create) allows user to create a new watch
|
||||
|
@ -486,6 +502,7 @@ func (web *Web) watchCreate(c *gin.Context) {
|
|||
}
|
||||
c.HTML(http.StatusOK, "watchCreate", gin.H{
|
||||
"templates": templates,
|
||||
"urlPrefix": web.urlPrefix,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -506,7 +523,7 @@ func (web *Web) watchCreatePost(c *gin.Context) {
|
|||
|
||||
if templateID == 0 { // empty new watch
|
||||
web.db.Create(&watch)
|
||||
c.Redirect(http.StatusSeeOther, fmt.Sprintf("/watch/edit/%d", watch.ID))
|
||||
c.Redirect(http.StatusSeeOther, fmt.Sprintf(web.urlPrefix+"watch/edit/%d", watch.ID))
|
||||
return // nothing else to do
|
||||
}
|
||||
|
||||
|
@ -552,7 +569,7 @@ func (web *Web) watchCreatePost(c *gin.Context) {
|
|||
}
|
||||
|
||||
if templateID > len(templateFiles) {
|
||||
log.Println("/watch/create POSTed with", templateID, "but only", len(templateFiles), "templates")
|
||||
log.Println(web.urlPrefix+"watch/create POSTed with", templateID, "but only", len(templateFiles), "templates")
|
||||
c.AbortWithError(http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
@ -621,7 +638,7 @@ func (web *Web) watchCreatePost(c *gin.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
c.Redirect(http.StatusSeeOther, fmt.Sprintf("/watch/edit/%d", watch.ID))
|
||||
c.Redirect(http.StatusSeeOther, fmt.Sprintf(web.urlPrefix+"watch/edit/%d", watch.ID))
|
||||
}
|
||||
|
||||
// deleteWatch (/watch/delete) removes a watch and it's cronjobs
|
||||
|
@ -629,7 +646,7 @@ func (web *Web) deleteWatch(c *gin.Context) {
|
|||
id, err := strconv.Atoi(c.PostForm("watch_id"))
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
c.Redirect(http.StatusSeeOther, "/")
|
||||
c.Redirect(http.StatusSeeOther, web.urlPrefix)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -648,7 +665,7 @@ func (web *Web) deleteWatch(c *gin.Context) {
|
|||
web.db.Delete(&Filter{}, "watch_id = ?", id)
|
||||
|
||||
web.db.Delete(&Watch{}, id)
|
||||
c.Redirect(http.StatusSeeOther, "/")
|
||||
c.Redirect(http.StatusSeeOther, web.urlPrefix)
|
||||
}
|
||||
|
||||
// watchView (/watch/view) shows the watch page with a graph and/or a table of stored values
|
||||
|
@ -703,6 +720,7 @@ func (web *Web) watchView(c *gin.Context) {
|
|||
"numericalMap": numericalMap,
|
||||
"categoricalMap": categoricalMap,
|
||||
"colorMap": colorMap,
|
||||
"urlPrefix": web.urlPrefix,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -734,6 +752,7 @@ func (web *Web) watchEdit(c *gin.Context) {
|
|||
"Filters": filters,
|
||||
"Connections": connections,
|
||||
"Notifiers": notifiers,
|
||||
"urlPrefix": web.urlPrefix,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -803,12 +822,15 @@ func (web *Web) watchUpdate(c *gin.Context) {
|
|||
web.db.Create(&newConnections)
|
||||
}
|
||||
|
||||
c.Redirect(http.StatusSeeOther, fmt.Sprintf("/watch/edit/%d", watch.ID))
|
||||
c.Redirect(http.StatusSeeOther, fmt.Sprintf(web.urlPrefix+"watch/edit/%d", watch.ID))
|
||||
}
|
||||
|
||||
// cacheView (/cache/view) shows the items in the web.urlCache
|
||||
func (web *Web) cacheView(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "cacheView", web.urlCache)
|
||||
c.HTML(http.StatusOK, "cacheView", gin.H{
|
||||
"cache": web.urlCache,
|
||||
"urlPrefix": web.urlPrefix,
|
||||
})
|
||||
}
|
||||
|
||||
// cacheClear (/cache/clear) clears all items in web.urlCache
|
||||
|
@ -820,15 +842,24 @@ func (web *Web) cacheClear(c *gin.Context) {
|
|||
// backupView (/backup/view) lists the stored backups
|
||||
func (web *Web) backupView(c *gin.Context) {
|
||||
if !viper.IsSet("database.backup") {
|
||||
c.HTML(http.StatusOK, "backupView", gin.H{"Error": "database.backup not set"})
|
||||
c.HTML(http.StatusOK, "backupView", gin.H{
|
||||
"Error": "database.backup not set",
|
||||
"urlPrefix": web.urlPrefix,
|
||||
})
|
||||
return
|
||||
}
|
||||
if !viper.IsSet("database.backup.schedule") {
|
||||
c.HTML(http.StatusOK, "backupView", gin.H{"Error": "database.backup.schedule not set"})
|
||||
c.HTML(http.StatusOK, "backupView", gin.H{
|
||||
"Error": "database.backup.schedule not set",
|
||||
"urlPrefix": web.urlPrefix,
|
||||
})
|
||||
return
|
||||
}
|
||||
if !viper.IsSet("database.backup.path") {
|
||||
c.HTML(http.StatusOK, "backupView", gin.H{"Error": "database.backup.path not set"})
|
||||
c.HTML(http.StatusOK, "backupView", gin.H{
|
||||
"Error": "database.backup.path not set",
|
||||
"urlPrefix": web.urlPrefix,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -836,13 +867,19 @@ func (web *Web) backupView(c *gin.Context) {
|
|||
|
||||
backupDir, err := filepath.Abs(filepath.Dir(backupPath))
|
||||
if err != nil {
|
||||
c.HTML(http.StatusOK, "backupView", gin.H{"Error": err})
|
||||
c.HTML(http.StatusOK, "backupView", gin.H{
|
||||
"Error": err,
|
||||
"urlPrefix": web.urlPrefix,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
filesInBackupDir, err := ioutil.ReadDir(backupDir)
|
||||
if err != nil {
|
||||
c.HTML(http.StatusOK, "backupView", gin.H{"Error": err})
|
||||
c.HTML(http.StatusOK, "backupView", gin.H{
|
||||
"Error": err,
|
||||
"urlPrefix": web.urlPrefix,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -852,7 +889,10 @@ func (web *Web) backupView(c *gin.Context) {
|
|||
filePaths = append(filePaths, fullPath)
|
||||
}
|
||||
|
||||
c.HTML(http.StatusOK, "backupView", gin.H{"Backups": filePaths})
|
||||
c.HTML(http.StatusOK, "backupView", gin.H{
|
||||
"Backups": filePaths,
|
||||
"urlPrefix": web.urlPrefix,
|
||||
})
|
||||
}
|
||||
|
||||
// backupCreate (/backup/create) creates a new backup
|
||||
|
@ -875,7 +915,7 @@ func (web *Web) backupCreate(c *gin.Context) {
|
|||
c.HTML(http.StatusBadRequest, "backupView", gin.H{"Error": err})
|
||||
return
|
||||
}
|
||||
c.Redirect(http.StatusSeeOther, "/backup/view")
|
||||
c.Redirect(http.StatusSeeOther, web.urlPrefix+"backup/view")
|
||||
}
|
||||
|
||||
func (web *Web) scheduledBackup() {
|
||||
|
@ -984,7 +1024,7 @@ func (web *Web) backupTest(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
if importID < -1 {
|
||||
c.Redirect(http.StatusSeeOther, "/backup/view")
|
||||
c.Redirect(http.StatusSeeOther, web.urlPrefix+"backup/view")
|
||||
return
|
||||
}
|
||||
if !viper.IsSet("database.backup") {
|
||||
|
@ -1016,6 +1056,7 @@ func (web *Web) backupTest(c *gin.Context) {
|
|||
c.HTML(http.StatusOK, "backupTest", gin.H{
|
||||
"Backup": backup,
|
||||
"BackupPath": backupFullPath,
|
||||
"urlPrefix": web.urlPrefix,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1091,20 +1132,29 @@ func (web *Web) backupRestore(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
if importID < -1 {
|
||||
c.Redirect(http.StatusSeeOther, "/backup/view")
|
||||
c.Redirect(http.StatusSeeOther, web.urlPrefix+"backup/view")
|
||||
return
|
||||
}
|
||||
|
||||
if !viper.IsSet("database.backup") {
|
||||
c.HTML(http.StatusOK, "backupRestore", gin.H{"Error": "database.backup not set"})
|
||||
c.HTML(http.StatusOK, "backupRestore", gin.H{
|
||||
"Error": "database.backup not set",
|
||||
"urlPrefix": web.urlPrefix,
|
||||
})
|
||||
return
|
||||
}
|
||||
if !viper.IsSet("database.backup.schedule") {
|
||||
c.HTML(http.StatusOK, "backupRestore", gin.H{"Error": "database.backup.schedule not set"})
|
||||
c.HTML(http.StatusOK, "backupRestore", gin.H{
|
||||
"Error": "database.backup.schedule not set",
|
||||
"urlPrefix": web.urlPrefix,
|
||||
})
|
||||
return
|
||||
}
|
||||
if !viper.IsSet("database.backup.path") {
|
||||
c.HTML(http.StatusOK, "backupRestore", gin.H{"Error": "database.backup.path not set"})
|
||||
c.HTML(http.StatusOK, "backupRestore", gin.H{
|
||||
"Error": "database.backup.path not set",
|
||||
"urlPrefix": web.urlPrefix,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1117,7 +1167,10 @@ func (web *Web) backupRestore(c *gin.Context) {
|
|||
}
|
||||
|
||||
if err != nil {
|
||||
c.HTML(http.StatusOK, "backupRestore", gin.H{"Error": err})
|
||||
c.HTML(http.StatusOK, "backupRestore", gin.H{
|
||||
"Error": err,
|
||||
"urlPrefix": web.urlPrefix,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1149,13 +1202,17 @@ func (web *Web) backupRestore(c *gin.Context) {
|
|||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
c.HTML(http.StatusOK, "backupRestore", gin.H{"Error": err})
|
||||
c.HTML(http.StatusOK, "backupRestore", gin.H{
|
||||
"Error": err,
|
||||
"urlPrefix": web.urlPrefix,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.HTML(http.StatusOK, "backupRestore", gin.H{
|
||||
"Backup": backup,
|
||||
"BackupPath": backupFullPath,
|
||||
"urlPrefix": web.urlPrefix,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1167,7 +1224,7 @@ func (web *Web) backupDelete(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
if importID <= 0 {
|
||||
c.Redirect(http.StatusSeeOther, "/backup/view")
|
||||
c.Redirect(http.StatusSeeOther, web.urlPrefix+"backup/view")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1210,7 +1267,7 @@ func (web *Web) backupDelete(c *gin.Context) {
|
|||
c.HTML(http.StatusOK, "backupView", gin.H{"Error": err})
|
||||
return
|
||||
}
|
||||
c.Redirect(http.StatusSeeOther, "/backup/view")
|
||||
c.Redirect(http.StatusSeeOther, web.urlPrefix+"backup/view")
|
||||
}
|
||||
|
||||
// backupDownload (/backup/download) serves the backup file in index 'id'
|
||||
|
@ -1221,7 +1278,7 @@ func (web *Web) backupDownload(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
if importID < 0 {
|
||||
c.Redirect(http.StatusSeeOther, "/backup/view")
|
||||
c.Redirect(http.StatusSeeOther, web.urlPrefix+"backup/view")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1397,7 +1454,7 @@ func (web *Web) importWatch(c *gin.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
c.Redirect(http.StatusSeeOther, fmt.Sprintf("/watch/edit/%d", watchID))
|
||||
c.Redirect(http.StatusSeeOther, fmt.Sprintf(web.urlPrefix+"watch/edit/%d", watchID))
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
// @ts-ignore
|
||||
var urlPrefix = getURLPrefix();
|
||||
function testSubmit() {
|
||||
var form = document.getElementById("uploadForm");
|
||||
form.action = "/backup/test";
|
||||
form.action = urlPrefix + "backup/test";
|
||||
form.submit();
|
||||
}
|
||||
function restoreSubmit() {
|
||||
var form = document.getElementById("uploadForm");
|
||||
form.action = "/backup/restore";
|
||||
form.action = urlPrefix + "backup/restore";
|
||||
form.submit();
|
||||
}
|
||||
function initUploadSubmit() {
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
// @ts-ignore
|
||||
let urlPrefix = getURLPrefix();
|
||||
function testSubmit() {
|
||||
let form = document.getElementById("uploadForm") as HTMLFormElement;
|
||||
form.action = "/backup/test";
|
||||
form.action = urlPrefix + "backup/test";
|
||||
form.submit();
|
||||
}
|
||||
|
||||
function restoreSubmit() {
|
||||
let form = document.getElementById("uploadForm") as HTMLFormElement;
|
||||
form.action = "/backup/restore";
|
||||
form.action = urlPrefix + "backup/restore";
|
||||
form.submit();
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@ var __spread = (this && this.__spread) || function () {
|
|||
function onTypeChange(node) {
|
||||
var e_1, _a, e_2, _b;
|
||||
if (node === void 0) { node = null; }
|
||||
// @ts-ignore
|
||||
var urlPrefix = getURLPrefix();
|
||||
var select = document.getElementById("typeInput");
|
||||
var type = select.value;
|
||||
var var1Div = document.getElementById("var1Div");
|
||||
|
@ -1189,7 +1191,7 @@ function clearCache() {
|
|||
return; // do nothing
|
||||
}
|
||||
var data = new URLSearchParams();
|
||||
fetch("/cache/clear", {
|
||||
fetch(urlPrefix + "cache/clear", {
|
||||
method: "POST"
|
||||
}).then(function (response) {
|
||||
if (response.ok) {
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
|
||||
function onTypeChange(node: DiagramNode | null = null){
|
||||
// @ts-ignore
|
||||
let urlPrefix = getURLPrefix();
|
||||
let select = document.getElementById("typeInput") as HTMLSelectElement;
|
||||
let type = select.value;
|
||||
|
||||
|
@ -1325,7 +1328,7 @@ function clearCache(){
|
|||
return // do nothing
|
||||
}
|
||||
let data = new URLSearchParams();
|
||||
fetch("/cache/clear", {
|
||||
fetch(urlPrefix+"cache/clear", {
|
||||
method: "POST"
|
||||
}).then((response) => {
|
||||
if(response.ok){
|
||||
|
|
|
@ -3,7 +3,7 @@ GoWatch Backups
|
|||
{{end}}
|
||||
|
||||
{{define "head"}}
|
||||
<script src="/static/backup.js"></script>
|
||||
<script src="{{.urlPrefix}}static/backup.js"></script>
|
||||
{{ end }}
|
||||
|
||||
{{define "content"}}
|
||||
|
@ -30,7 +30,7 @@ GoWatch Backups
|
|||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<form id="uploadForm" action="/backup/restore" enctype="multipart/form-data" method="POST">
|
||||
<form id="uploadForm" action="{{.urlPrefix}}backup/restore" enctype="multipart/form-data" method="POST">
|
||||
<input type="hidden" value="-1" name="id">
|
||||
<input class="form-control" type="file" id="upload" name="upload">
|
||||
</form>
|
||||
|
@ -48,25 +48,25 @@ GoWatch Backups
|
|||
<tr>
|
||||
<td class="h5">{{ $backup }}</td>
|
||||
<td>
|
||||
<form action="/backup/test" enctype="multipart/form-data" method="POST">
|
||||
<form action="{{$.urlPrefix}}backup/test" enctype="multipart/form-data" method="POST">
|
||||
<input type="hidden" value="{{ $i }}" name="id">
|
||||
<button class="btn btn-success">Test</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form action="/backup/restore" enctype="multipart/form-data" method="POST">
|
||||
<form action="{{$.urlPrefix}}backup/restore" enctype="multipart/form-data" method="POST">
|
||||
<input type="hidden" value="{{ $i }}" name="id">
|
||||
<button class="btn btn-warning">Restore</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form action="/backup/delete" enctype="multipart/form-data" method="POST">
|
||||
<form action="{{$.urlPrefix}}backup/delete" enctype="multipart/form-data" method="POST">
|
||||
<input type="hidden" value="{{ $i }}" name="id">
|
||||
<button class="btn btn-danger">Delete</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<a href="/backup/download/{{ $i }}" class="btn btn-secondary">
|
||||
<a href="{{$.urlPrefix}}backup/download/{{ $i }}" class="btn btn-secondary">
|
||||
Download
|
||||
</a>
|
||||
</td>
|
||||
|
@ -74,7 +74,7 @@ GoWatch Backups
|
|||
{{ end }}
|
||||
</tbody>
|
||||
</table>
|
||||
<a href="/backup/create" class="btn btn-warning btn-lg">
|
||||
<a href="{{.urlPrefix}}backup/create" class="btn btn-warning btn-lg">
|
||||
Backup Now
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
@ -4,9 +4,13 @@
|
|||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="/static/bootstrap.5.2.0.min.css" rel="stylesheet">
|
||||
<link href="/static/style.css" rel="stylesheet">
|
||||
|
||||
<link href="{{.urlPrefix}}static/bootstrap.5.2.0.min.css" rel="stylesheet">
|
||||
<link href="{{.urlPrefix}}static/style.css" rel="stylesheet">
|
||||
<script>
|
||||
function getURLPrefix() {
|
||||
return "{{.urlPrefix}}";
|
||||
}
|
||||
</script>
|
||||
<title>{{ template "title" .}}</title>
|
||||
{{ template "head" .}}
|
||||
</head>
|
||||
|
@ -21,21 +25,21 @@
|
|||
<div class="collapse navbar-collapse" id="navbarCollapse">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-md-0">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" aria-current="page" href="/">Home</a>
|
||||
<a class="nav-link active" aria-current="page" href="{{.urlPrefix}}">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" aria-current="page" href="/notifiers/view">Notifiers</a>
|
||||
<a class="nav-link" aria-current="page" href="{{.urlPrefix}}notifiers/view">Notifiers</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" aria-current="page" id="newWatchLink" href="/watch/create">New</a>
|
||||
<a class="nav-link" aria-current="page" id="newWatchLink" href="{{.urlPrefix}}watch/create">New</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" aria-current="page" href="/backup/view">Backups</a>
|
||||
<a class="nav-link" aria-current="page" href="{{.urlPrefix}}backup/view">Backups</a>
|
||||
</li>
|
||||
{{ template "navbar" .}}
|
||||
</ul>
|
||||
<div class="d-flex">
|
||||
<a class="nav-link btn" href="/config">Config</a>
|
||||
<a class="nav-link btn" href="{{.urlPrefix}}config">Config</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -64,8 +68,8 @@
|
|||
<span class="text-muted">Broodjeaap 2023</span>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="/static/bootstrap.5.2.0.bundle.min.js"></script>
|
||||
<script src="/static/script.js"></script>
|
||||
<script src="{{.urlPrefix}}static/bootstrap.5.2.0.bundle.min.js"></script>
|
||||
<script src="{{.urlPrefix}}static/script.js"></script>
|
||||
{{ template "scripts" .}}
|
||||
</body>
|
||||
</html>
|
||||
|
|
4
templates/cache/view.html
vendored
4
templates/cache/view.html
vendored
|
@ -3,11 +3,11 @@ GoWatch URL Cache
|
|||
{{end}}
|
||||
|
||||
{{define "content"}}
|
||||
{{ range $url, $content := . }}
|
||||
{{ range $url, $content := .cache }}
|
||||
<div class="card text-center">
|
||||
<div class="card-header">
|
||||
{{ $url }}
|
||||
<form action="/cache/clear" method="post">
|
||||
<form action="{{$.urlPrefix}}cache/clear" method="post">
|
||||
<input type="hidden" name="url" value="{{ $url }}">
|
||||
<input type="submit" class="btn btn-danger btn-sm" value="Clear">
|
||||
</form>
|
||||
|
|
|
@ -28,7 +28,7 @@ GoWatch
|
|||
</thead>
|
||||
<tbody>
|
||||
{{ range .watches }}
|
||||
<tr class="pointer" onclick="window.location='/watch/view/{{ .ID }}'">
|
||||
<tr class="pointer" onclick="window.location='{{$.urlPrefix}}watch/view/{{ .ID }}'">
|
||||
<td class="h3">{{ .Name }}</td>
|
||||
{{ if .CronEntry }}
|
||||
<td class="h3">{{ .CronEntry.Prev.Format "2006-01-02 15:04:05" }}</td>
|
||||
|
@ -41,10 +41,10 @@ GoWatch
|
|||
{{ .LastValue }}
|
||||
</td>
|
||||
<td>
|
||||
<a href="/watch/edit/{{ .ID }}" class="btn btn-success">Edit</a>
|
||||
<a href="{{$.urlPrefix}}watch/edit/{{ .ID }}" class="btn btn-success">Edit</a>
|
||||
</td>
|
||||
<td>
|
||||
<form action="/watch/delete" method="post">
|
||||
<form action="{{$.urlPrefix}}watch/delete" method="post">
|
||||
<input type="hidden" name="watch_id" value="{{ .ID }}">
|
||||
<input type="submit" class="btn btn-danger" value="Delete">
|
||||
</form>
|
||||
|
|
|
@ -11,11 +11,11 @@ GoWatch
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{ range $name, $notifier := . }}
|
||||
{{ range $name, $notifier := .notifiers }}
|
||||
<tr>
|
||||
<td class="h3">{{ $name }}</td>
|
||||
<td>
|
||||
<form action="/notifiers/test" method="post">
|
||||
<form action="{{.urlPrefix}}notifiers/test" method="post">
|
||||
<input type="hidden" name="notifier_name" value="{{ $name }}">
|
||||
<input type="submit" class="btn btn-primary" value="Test">
|
||||
</form>
|
||||
|
|
|
@ -3,12 +3,12 @@ GoWatch Create
|
|||
{{end}}
|
||||
|
||||
{{define "head"}}
|
||||
<script src="/static/create.js"></script>
|
||||
<script src="{{.urlPrefix}}static/create.js"></script>
|
||||
{{ end }}
|
||||
|
||||
{{define "content"}}
|
||||
|
||||
<form action="/watch/create" enctype="multipart/form-data" method="POST">
|
||||
<form action="{{.urlPrefix}}watch/create" enctype="multipart/form-data" method="POST">
|
||||
<div class="container">
|
||||
<div class="row g-3 justify-content-center">
|
||||
<div class="col-auto">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{{define "head"}}
|
||||
<script src="/static/diagram.js"></script>
|
||||
<script src="/static/edit.js"></script>
|
||||
<script src="{{.urlPrefix}}static/diagram.js"></script>
|
||||
<script src="{{.urlPrefix}}static/edit.js"></script>
|
||||
{{ end }}
|
||||
{{define "content"}}
|
||||
<div class="canvas_parent">
|
||||
|
@ -34,7 +34,7 @@ GoWatch Edit {{ .Watch.Name }}
|
|||
</button>
|
||||
</td>
|
||||
<td>
|
||||
<a href="/watch/export/{{ .Watch.ID }}" class="btn btn-success btn-sm">
|
||||
<a href="{{.urlPrefix}}watch/export/{{ .Watch.ID }}" class="btn btn-success btn-sm">
|
||||
Export Watch
|
||||
</a>
|
||||
</td>
|
||||
|
@ -49,7 +49,7 @@ GoWatch Edit {{ .Watch.Name }}
|
|||
</button>
|
||||
</td>
|
||||
<td>
|
||||
<a href="/watch/view/{{ .Watch.ID }}" class="btn btn-outline-secondary btn-sm">
|
||||
<a href="{{.urlPrefix}}watch/view/{{ .Watch.ID }}" class="btn btn-outline-secondary btn-sm">
|
||||
View Watch
|
||||
</a>
|
||||
</td>
|
||||
|
@ -66,7 +66,7 @@ GoWatch Edit {{ .Watch.Name }}
|
|||
</div>
|
||||
<div class="modal-body">
|
||||
<div>
|
||||
<form action="/filter/create" method="post">
|
||||
<form action="{{.urlPrefix}}filter/create" method="post">
|
||||
<div class="mb-3 m-3 row">
|
||||
<label for="nameInput" class="col-sm-2 col-form-label">Name:</label>
|
||||
<div class="col-sm-10 p-2">
|
||||
|
@ -136,7 +136,7 @@ GoWatch Edit {{ .Watch.Name }}
|
|||
</div>
|
||||
<div class="modal-body">
|
||||
<div>
|
||||
<form action="/watch/update" id="saveWatchForm" method="post">
|
||||
<form action="{{.urlPrefix}}watch/update" id="saveWatchForm" method="post">
|
||||
<div class="mb-3 m-3 row">
|
||||
<input type="hidden" id="watch_id" name="watch_id" value="{{ .Watch.ID }}">
|
||||
<label for="watchNameInput" class="col-sm-2 col-form-label">Name:</label>
|
||||
|
@ -148,7 +148,7 @@ GoWatch Edit {{ .Watch.Name }}
|
|||
</div>
|
||||
</form>
|
||||
<button class="btn btn-primary mt-4 float-start" id="saveButtonModal" data-bs-dismiss="modal" id="submitWatchButton">Save</button>
|
||||
<form action="/watch/delete" method="post">
|
||||
<form action="{{.urlPrefix}}watch/delete" method="post">
|
||||
<input type="hidden" name="watch_id" value="{{ .Watch.ID }}">
|
||||
<button class="btn btn-danger mt-4 float-end" data-bs-dismiss="modal" id="deleteFilterButton">Delete Watch</button>
|
||||
</form>
|
||||
|
@ -166,7 +166,7 @@ GoWatch Edit {{ .Watch.Name }}
|
|||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form action="/watch/import/{{ .Watch.ID }}" enctype="multipart/form-data" method="post" class="row g-3">
|
||||
<form action="{{.urlPrefix}}watch/import/{{ .Watch.ID }}" enctype="multipart/form-data" method="post" class="row g-3">
|
||||
<div class="mb-3">
|
||||
<label for="json" class="form-label">Upload JSON to import</label>
|
||||
<input class="form-control" type="file" id="json" name="json">
|
||||
|
|
|
@ -13,7 +13,7 @@ GoWatch {{ .Watch.Name }}
|
|||
<div class="card-body">
|
||||
<div class="card-title text-center h4">
|
||||
{{ .Watch.Name }}
|
||||
<a href="/watch/edit/{{ .Watch.ID }}" class="btn btn-sm btn-success">Edit</a>
|
||||
<a href="{{.urlPrefix}}watch/edit/{{ .Watch.ID }}" class="btn btn-sm btn-success">Edit</a>
|
||||
</div>
|
||||
{{ if not .Watch.CronEntry }}
|
||||
<h5>No Schedule</h5>
|
||||
|
|
2
todo.md
2
todo.md
|
@ -5,5 +5,5 @@
|
|||
- util.go
|
||||
- edit.ts
|
||||
- diagram.ts
|
||||
- url path support
|
||||
- config in config?
|
||||
- refactor project structure
|
Loading…
Add table
Reference in a new issue