GUI config
This commit is contained in:
parent
a49a668d4d
commit
9029d46ea8
15 changed files with 221 additions and 47 deletions
8
.github/workflows/docker-readme.yml
vendored
8
.github/workflows/docker-readme.yml
vendored
|
|
@ -2,10 +2,10 @@ name: DockerHub-README
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
# push:
|
push:
|
||||||
# branches: [ "main" ]
|
branches: [ "main" ]
|
||||||
# paths:
|
paths:
|
||||||
# - 'README.md'
|
- 'README.md'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
IMAGE_NAME: watchyourlan
|
IMAGE_NAME: watchyourlan
|
||||||
|
|
|
||||||
2
.version
2
.version
|
|
@ -1 +1 @@
|
||||||
VERSION=0.7.5
|
VERSION=0.8.1
|
||||||
|
|
@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
|
## [v0.8.1] - 2022-12-29
|
||||||
|
### Changed
|
||||||
|
- Full code refactoring
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Embed templates
|
||||||
|
- GUI config page
|
||||||
|
|
||||||
## [v0.7.5] - 2022-09-14
|
## [v0.7.5] - 2022-09-14
|
||||||
### Fixed
|
### Fixed
|
||||||
- Sort by IP [Issue #4](https://github.com/aceberg/WatchYourLAN/issues/4), [Issue #14](https://github.com/aceberg/WatchYourLAN/issues/14)
|
- Sort by IP [Issue #4](https://github.com/aceberg/WatchYourLAN/issues/4), [Issue #14](https://github.com/aceberg/WatchYourLAN/issues/14)
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,19 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"flag"
|
||||||
|
|
||||||
"github.com/aceberg/WatchYourLAN/internal/conf"
|
"github.com/aceberg/WatchYourLAN/internal/check"
|
||||||
"github.com/aceberg/WatchYourLAN/internal/db"
|
|
||||||
"github.com/aceberg/WatchYourLAN/internal/web"
|
"github.com/aceberg/WatchYourLAN/internal/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
const configPath = "/data/config"
|
const configPath = "/data/config"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
appConfig := conf.Get(configPath) // Get config from Defaults, Config file, Env
|
confPtr := flag.String("c", configPath, "Path to config file")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
fmt.Println("CONF =", appConfig)
|
check.Path(*confPtr)
|
||||||
|
|
||||||
db.Create(appConfig.DbPath) // Check if DB exists. Create if not
|
web.Gui(*confPtr) // Start web GUI
|
||||||
|
|
||||||
web.Gui(appConfig) // Start web GUI
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
23
internal/check/path.go
Normal file
23
internal/check/path.go
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
package check
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Path - create path if not exist
|
||||||
|
func Path(path string) {
|
||||||
|
|
||||||
|
_, err := os.Stat(path)
|
||||||
|
|
||||||
|
if path != "" && err != nil {
|
||||||
|
|
||||||
|
dir := filepath.Dir(path)
|
||||||
|
|
||||||
|
err = os.MkdirAll(dir, os.ModePerm)
|
||||||
|
IfError(err)
|
||||||
|
|
||||||
|
_, err = os.Create(path)
|
||||||
|
IfError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -35,10 +35,19 @@ func Get(path string) (config models.Conf) {
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
// // Write - write config to file
|
// Write - write config to file
|
||||||
// func Write(path, theme string) {
|
func Write(path string, config models.Conf) {
|
||||||
// viper.SetConfigFile(path)
|
|
||||||
// viper.SetConfigType("env")
|
viper.SetConfigFile(path)
|
||||||
// viper.Set("THEME", theme)
|
viper.SetConfigType("env")
|
||||||
// viper.WriteConfig()
|
|
||||||
// }
|
viper.Set("IFACE", config.Iface)
|
||||||
|
viper.Set("GUIIP", config.GuiIP)
|
||||||
|
viper.Set("GUIPORT", config.GuiPort)
|
||||||
|
viper.Set("TIMEOUT", config.Timeout)
|
||||||
|
viper.Set("SHOUTRRR_URL", config.ShoutURL)
|
||||||
|
viper.Set("THEME", config.Theme)
|
||||||
|
|
||||||
|
err := viper.WriteConfig()
|
||||||
|
check.IfError(err)
|
||||||
|
}
|
||||||
|
|
@ -8,14 +8,29 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Start - start arp-scan goroutine
|
// Start - start arp-scan goroutine
|
||||||
func Start(appConfig models.Conf) {
|
func Start(appConfig models.Conf, quit chan bool) {
|
||||||
var foundHosts []models.Host
|
var foundHosts []models.Host
|
||||||
var dbHosts []models.Host
|
var dbHosts []models.Host
|
||||||
for { // Endless
|
var lastDate time.Time
|
||||||
foundHosts = arpScan(appConfig.Iface) // Scan interfaces
|
|
||||||
dbHosts = db.Select(appConfig.DbPath) // Select everything from DB
|
for {
|
||||||
db.SetNow(appConfig.DbPath) // Mark hosts in DB as offline
|
select {
|
||||||
hostsCompare(appConfig, foundHosts, dbHosts) // Compare hosts online and in DB and add them to DB
|
case <-quit:
|
||||||
time.Sleep(time.Duration(appConfig.Timeout) * time.Second) // Timeout
|
return
|
||||||
|
default:
|
||||||
|
nowDate := time.Now()
|
||||||
|
plusDate := lastDate.Add(time.Duration(appConfig.Timeout) * time.Second)
|
||||||
|
|
||||||
|
if nowDate.After(plusDate) {
|
||||||
|
foundHosts = arpScan(appConfig.Iface)
|
||||||
|
dbHosts = db.Select(appConfig.DbPath)
|
||||||
|
db.SetNow(appConfig.DbPath)
|
||||||
|
hostsCompare(appConfig, foundHosts, dbHosts)
|
||||||
|
|
||||||
|
lastDate = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(time.Duration(1) * time.Second)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
51
internal/web/config.go
Normal file
51
internal/web/config.go
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"html/template"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/aceberg/WatchYourLAN/internal/check"
|
||||||
|
"github.com/aceberg/WatchYourLAN/internal/conf"
|
||||||
|
"github.com/aceberg/WatchYourLAN/internal/models"
|
||||||
|
"github.com/aceberg/WatchYourLAN/internal/scan"
|
||||||
|
)
|
||||||
|
|
||||||
|
func configHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var guiData models.GuiData
|
||||||
|
|
||||||
|
guiData.Config = AppConfig
|
||||||
|
guiData.Icon = Icon
|
||||||
|
|
||||||
|
guiData.Themes = []string{"cerulean", "cosmo", "cyborg", "darkly", "flatly", "journal", "litera", "lumen", "lux", "materia", "minty", "morph", "pulse", "quartz", "sandstone", "simplex", "sketchy", "slate", "solar", "spacelab", "superhero", "united", "vapor", "yeti", "zephyr"}
|
||||||
|
|
||||||
|
tmpl, err := template.ParseFS(TemplHTML, TemplPath+"config.html", TemplPath+"header.html", TemplPath+"footer.html")
|
||||||
|
check.IfError(err)
|
||||||
|
err = tmpl.ExecuteTemplate(w, "header", guiData)
|
||||||
|
check.IfError(err)
|
||||||
|
err = tmpl.ExecuteTemplate(w, "config", guiData)
|
||||||
|
check.IfError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveConfigHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
AppConfig.Iface = r.FormValue("iface")
|
||||||
|
AppConfig.GuiIP = r.FormValue("host")
|
||||||
|
AppConfig.GuiPort = r.FormValue("port")
|
||||||
|
AppConfig.ShoutURL = r.FormValue("shout")
|
||||||
|
AppConfig.Theme = r.FormValue("theme")
|
||||||
|
|
||||||
|
timeout := r.FormValue("timeout")
|
||||||
|
AppConfig.Timeout, err = strconv.Atoi(timeout)
|
||||||
|
check.IfError(err)
|
||||||
|
|
||||||
|
close(QuitScan)
|
||||||
|
|
||||||
|
conf.Write(ConfigPath, AppConfig)
|
||||||
|
|
||||||
|
QuitScan = make(chan bool)
|
||||||
|
go scan.Start(AppConfig, QuitScan)
|
||||||
|
|
||||||
|
http.Redirect(w, r, r.Header.Get("Referer"), 302)
|
||||||
|
}
|
||||||
|
|
@ -17,9 +17,9 @@ func indexHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
guiData.Hosts = AllHosts
|
guiData.Hosts = AllHosts
|
||||||
guiData.Icon = Icon
|
guiData.Icon = Icon
|
||||||
|
|
||||||
// tmpl, _ := template.ParseFiles(TemplPath+"index.html", TemplPath+"header.html", TemplPath+"footer.html")
|
tmpl, err := template.ParseFS(TemplHTML, TemplPath+"index.html", TemplPath+"header.html", TemplPath+"footer.html")
|
||||||
tmpl, _ := template.ParseFS(TemplHTML, TemplPath+"index.html", TemplPath+"header.html", TemplPath+"footer.html")
|
check.IfError(err)
|
||||||
err := tmpl.ExecuteTemplate(w, "header", guiData)
|
err = tmpl.ExecuteTemplate(w, "header", guiData)
|
||||||
check.IfError(err)
|
check.IfError(err)
|
||||||
err = tmpl.ExecuteTemplate(w, "index", guiData)
|
err = tmpl.ExecuteTemplate(w, "index", guiData)
|
||||||
check.IfError(err)
|
check.IfError(err)
|
||||||
|
|
@ -21,7 +21,6 @@ func offlineHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// tmpl, _ := template.ParseFiles(TemplPath+"offline.html", TemplPath+"header.html", TemplPath+"footer.html")
|
|
||||||
tmpl, _ := template.ParseFS(TemplHTML, TemplPath+"offline.html", TemplPath+"header.html", TemplPath+"footer.html")
|
tmpl, _ := template.ParseFS(TemplHTML, TemplPath+"offline.html", TemplPath+"header.html", TemplPath+"footer.html")
|
||||||
err := tmpl.ExecuteTemplate(w, "header", guiData)
|
err := tmpl.ExecuteTemplate(w, "header", guiData)
|
||||||
check.IfError(err)
|
check.IfError(err)
|
||||||
|
|
@ -42,7 +41,6 @@ func onlineHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// tmpl, _ := template.ParseFiles(TemplPath+"offline.html", TemplPath+"header.html", TemplPath+"footer.html")
|
|
||||||
tmpl, _ := template.ParseFS(TemplHTML, TemplPath+"offline.html", TemplPath+"header.html", TemplPath+"footer.html")
|
tmpl, _ := template.ParseFS(TemplHTML, TemplPath+"offline.html", TemplPath+"header.html", TemplPath+"footer.html")
|
||||||
err := tmpl.ExecuteTemplate(w, "header", guiData)
|
err := tmpl.ExecuteTemplate(w, "header", guiData)
|
||||||
check.IfError(err)
|
check.IfError(err)
|
||||||
|
|
|
||||||
56
internal/web/templates/config.html
Normal file
56
internal/web/templates/config.html
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
{{ define "config" }}
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container mt-5">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<table class="table">
|
||||||
|
<form action="/save_config/" method="post">
|
||||||
|
<tr>
|
||||||
|
<td>Interfaces</td>
|
||||||
|
<td><input name="iface" type="text" class="form-control" value="{{ .Config.Iface }}"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Host</td>
|
||||||
|
<td><input name="host" type="text" class="form-control" value="{{ .Config.GuiIP }}"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Port</td>
|
||||||
|
<td><input name="port" type="text" class="form-control" value="{{ .Config.GuiPort }}"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Timeout (seconds)</td>
|
||||||
|
<td><input name="timeout" type="text" class="form-control" value="{{ .Config.Timeout }}"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Shoutrrr URL</td>
|
||||||
|
<td><input name="shout" type="text" class="form-control" value="{{ .Config.ShoutURL }}"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Theme</td>
|
||||||
|
<td><select name="theme" class="form-select">
|
||||||
|
<option selected>{{ .Config.Theme }}</option>
|
||||||
|
{{ range .Themes }}
|
||||||
|
<option value="{{ . }}">{{ . }}</option>
|
||||||
|
{{ end }}
|
||||||
|
</select></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><button type="submit" class="btn btn-primary">Save</button></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
</form>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div class="alert alert-info" role="alert">
|
||||||
|
<h4 class="alert-heading">Warning!</h4>
|
||||||
|
<p>After changing <b>Host</b> or <b>Port</b> you need to restart the app</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{{ template "footer" }}
|
||||||
|
{{ end }}
|
||||||
|
|
@ -23,6 +23,9 @@
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link active" href="/offline/">Offline</a>
|
<a class="nav-link active" href="/offline/">Offline</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link active" href="/config/">Config</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="nav navbar-nav navbar-right">
|
<ul class="nav navbar-nav navbar-right">
|
||||||
<form action="/search_hosts/" method="post">
|
<form action="/search_hosts/" method="post">
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Ip</th>
|
<th>IP</th>
|
||||||
<th>Mac</th>
|
<th>Mac</th>
|
||||||
<th>Hardware</th>
|
<th>Hardware</th>
|
||||||
<th>Last seen</th>
|
<th>Last seen</th>
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
<i class="bi bi-caret-down"></i>
|
<i class="bi bi-caret-down"></i>
|
||||||
</button>
|
</button>
|
||||||
</th>
|
</th>
|
||||||
<th>Ip<br>
|
<th>IP<br>
|
||||||
<button type="submit" name="sort_method" value="ip-up" class="btn btn-primary btn-sm">
|
<button type="submit" name="sort_method" value="ip-up" class="btn btn-primary btn-sm">
|
||||||
<i class="bi bi-caret-up"></i>
|
<i class="bi bi-caret-up"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/aceberg/WatchYourLAN/internal/check"
|
"github.com/aceberg/WatchYourLAN/internal/check"
|
||||||
|
"github.com/aceberg/WatchYourLAN/internal/conf"
|
||||||
"github.com/aceberg/WatchYourLAN/internal/db"
|
"github.com/aceberg/WatchYourLAN/internal/db"
|
||||||
"github.com/aceberg/WatchYourLAN/internal/models"
|
"github.com/aceberg/WatchYourLAN/internal/models"
|
||||||
"github.com/aceberg/WatchYourLAN/internal/scan"
|
"github.com/aceberg/WatchYourLAN/internal/scan"
|
||||||
|
|
@ -17,38 +18,50 @@ var (
|
||||||
|
|
||||||
// AllHosts - all hosts from DB
|
// AllHosts - all hosts from DB
|
||||||
AllHosts []models.Host
|
AllHosts []models.Host
|
||||||
)
|
|
||||||
|
|
||||||
// TemplHTML - embed templates
|
// ConfigPath - path to config file
|
||||||
//
|
ConfigPath string
|
||||||
//go:embed templates/*
|
|
||||||
var TemplHTML embed.FS
|
// QuitScan - send stop signal to scan
|
||||||
|
QuitScan chan bool
|
||||||
|
|
||||||
|
// TemplHTML - embed templates
|
||||||
|
//
|
||||||
|
//go:embed templates/*
|
||||||
|
TemplHTML embed.FS
|
||||||
|
)
|
||||||
|
|
||||||
// TemplPath - path to html templates
|
// TemplPath - path to html templates
|
||||||
const TemplPath = "templates/"
|
const TemplPath = "templates/"
|
||||||
|
|
||||||
// const TemplPath = "../../internal/web/templates/"
|
|
||||||
|
|
||||||
// Gui - start web GUI
|
// Gui - start web GUI
|
||||||
func Gui(appConfig models.Conf) {
|
func Gui(configPath string) {
|
||||||
AppConfig = appConfig
|
|
||||||
|
ConfigPath = configPath
|
||||||
|
AppConfig = conf.Get(ConfigPath)
|
||||||
|
|
||||||
address := AppConfig.GuiIP + ":" + AppConfig.GuiPort
|
address := AppConfig.GuiIP + ":" + AppConfig.GuiPort
|
||||||
|
|
||||||
go scan.Start(AppConfig)
|
db.Create(AppConfig.DbPath)
|
||||||
|
|
||||||
|
QuitScan = make(chan bool)
|
||||||
|
|
||||||
|
go scan.Start(AppConfig, QuitScan)
|
||||||
|
|
||||||
|
AllHosts = db.Select(AppConfig.DbPath)
|
||||||
|
|
||||||
log.Println("=================================== ")
|
log.Println("=================================== ")
|
||||||
log.Printf("Web GUI at http://%s", address)
|
log.Printf("Web GUI at http://%s", address)
|
||||||
log.Println("=================================== ")
|
log.Println("=================================== ")
|
||||||
|
|
||||||
AllHosts = db.Select(AppConfig.DbPath)
|
|
||||||
|
|
||||||
http.HandleFunc("/", indexHandler)
|
http.HandleFunc("/", indexHandler)
|
||||||
|
http.HandleFunc("/config/", configHandler)
|
||||||
http.HandleFunc("/home/", homeHandler)
|
http.HandleFunc("/home/", homeHandler)
|
||||||
http.HandleFunc("/offline/", offlineHandler)
|
http.HandleFunc("/offline/", offlineHandler)
|
||||||
http.HandleFunc("/online/", onlineHandler)
|
http.HandleFunc("/online/", onlineHandler)
|
||||||
|
http.HandleFunc("/save_config/", saveConfigHandler)
|
||||||
http.HandleFunc("/search_hosts/", searchHandler)
|
http.HandleFunc("/search_hosts/", searchHandler)
|
||||||
http.HandleFunc("/sort_hosts/", sortHandler)
|
http.HandleFunc("/sort_hosts/", sortHandler)
|
||||||
// http.HandleFunc("/theme/", theme)
|
|
||||||
http.HandleFunc("/update_host/", updateHandler)
|
http.HandleFunc("/update_host/", updateHandler)
|
||||||
err := http.ListenAndServe(address, nil)
|
err := http.ListenAndServe(address, nil)
|
||||||
check.IfError(err)
|
check.IfError(err)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue