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:
|
||||
workflow_dispatch:
|
||||
# push:
|
||||
# branches: [ "main" ]
|
||||
# paths:
|
||||
# - 'README.md'
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
paths:
|
||||
- 'README.md'
|
||||
|
||||
env:
|
||||
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/)
|
||||
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
|
||||
### Fixed
|
||||
- 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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"flag"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/conf"
|
||||
"github.com/aceberg/WatchYourLAN/internal/db"
|
||||
"github.com/aceberg/WatchYourLAN/internal/check"
|
||||
"github.com/aceberg/WatchYourLAN/internal/web"
|
||||
)
|
||||
|
||||
const configPath = "/data/config"
|
||||
|
||||
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(appConfig) // Start web GUI
|
||||
web.Gui(*confPtr) // 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
|
||||
}
|
||||
|
||||
// // Write - write config to file
|
||||
// func Write(path, theme string) {
|
||||
// viper.SetConfigFile(path)
|
||||
// viper.SetConfigType("env")
|
||||
// viper.Set("THEME", theme)
|
||||
// viper.WriteConfig()
|
||||
// }
|
||||
// Write - write config to file
|
||||
func Write(path string, config models.Conf) {
|
||||
|
||||
viper.SetConfigFile(path)
|
||||
viper.SetConfigType("env")
|
||||
|
||||
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
|
||||
func Start(appConfig models.Conf) {
|
||||
func Start(appConfig models.Conf, quit chan bool) {
|
||||
var foundHosts []models.Host
|
||||
var dbHosts []models.Host
|
||||
for { // Endless
|
||||
foundHosts = arpScan(appConfig.Iface) // Scan interfaces
|
||||
dbHosts = db.Select(appConfig.DbPath) // Select everything from DB
|
||||
db.SetNow(appConfig.DbPath) // Mark hosts in DB as offline
|
||||
hostsCompare(appConfig, foundHosts, dbHosts) // Compare hosts online and in DB and add them to DB
|
||||
time.Sleep(time.Duration(appConfig.Timeout) * time.Second) // Timeout
|
||||
var lastDate time.Time
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-quit:
|
||||
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.Icon = Icon
|
||||
|
||||
// tmpl, _ := template.ParseFiles(TemplPath+"index.html", TemplPath+"header.html", TemplPath+"footer.html")
|
||||
tmpl, _ := template.ParseFS(TemplHTML, TemplPath+"index.html", TemplPath+"header.html", TemplPath+"footer.html")
|
||||
err := tmpl.ExecuteTemplate(w, "header", guiData)
|
||||
tmpl, err := template.ParseFS(TemplHTML, TemplPath+"index.html", TemplPath+"header.html", TemplPath+"footer.html")
|
||||
check.IfError(err)
|
||||
err = tmpl.ExecuteTemplate(w, "header", guiData)
|
||||
check.IfError(err)
|
||||
err = tmpl.ExecuteTemplate(w, "index", guiData)
|
||||
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")
|
||||
err := tmpl.ExecuteTemplate(w, "header", guiData)
|
||||
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")
|
||||
err := tmpl.ExecuteTemplate(w, "header", guiData)
|
||||
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">
|
||||
<a class="nav-link active" href="/offline/">Offline</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="/config/">Config</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<form action="/search_hosts/" method="post">
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
<table class="table table-striped">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Ip</th>
|
||||
<th>IP</th>
|
||||
<th>Mac</th>
|
||||
<th>Hardware</th>
|
||||
<th>Last seen</th>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<i class="bi bi-caret-down"></i>
|
||||
</button>
|
||||
</th>
|
||||
<th>Ip<br>
|
||||
<th>IP<br>
|
||||
<button type="submit" name="sort_method" value="ip-up" class="btn btn-primary btn-sm">
|
||||
<i class="bi bi-caret-up"></i>
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/check"
|
||||
"github.com/aceberg/WatchYourLAN/internal/conf"
|
||||
"github.com/aceberg/WatchYourLAN/internal/db"
|
||||
"github.com/aceberg/WatchYourLAN/internal/models"
|
||||
"github.com/aceberg/WatchYourLAN/internal/scan"
|
||||
|
|
@ -17,38 +18,50 @@ var (
|
|||
|
||||
// AllHosts - all hosts from DB
|
||||
AllHosts []models.Host
|
||||
)
|
||||
|
||||
// TemplHTML - embed templates
|
||||
//
|
||||
//go:embed templates/*
|
||||
var TemplHTML embed.FS
|
||||
// ConfigPath - path to config file
|
||||
ConfigPath string
|
||||
|
||||
// QuitScan - send stop signal to scan
|
||||
QuitScan chan bool
|
||||
|
||||
// TemplHTML - embed templates
|
||||
//
|
||||
//go:embed templates/*
|
||||
TemplHTML embed.FS
|
||||
)
|
||||
|
||||
// TemplPath - path to html templates
|
||||
const TemplPath = "templates/"
|
||||
|
||||
// const TemplPath = "../../internal/web/templates/"
|
||||
|
||||
// Gui - start web GUI
|
||||
func Gui(appConfig models.Conf) {
|
||||
AppConfig = appConfig
|
||||
func Gui(configPath string) {
|
||||
|
||||
ConfigPath = configPath
|
||||
AppConfig = conf.Get(ConfigPath)
|
||||
|
||||
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.Printf("Web GUI at http://%s", address)
|
||||
log.Println("=================================== ")
|
||||
|
||||
AllHosts = db.Select(AppConfig.DbPath)
|
||||
|
||||
http.HandleFunc("/", indexHandler)
|
||||
http.HandleFunc("/config/", configHandler)
|
||||
http.HandleFunc("/home/", homeHandler)
|
||||
http.HandleFunc("/offline/", offlineHandler)
|
||||
http.HandleFunc("/online/", onlineHandler)
|
||||
http.HandleFunc("/save_config/", saveConfigHandler)
|
||||
http.HandleFunc("/search_hosts/", searchHandler)
|
||||
http.HandleFunc("/sort_hosts/", sortHandler)
|
||||
// http.HandleFunc("/theme/", theme)
|
||||
http.HandleFunc("/update_host/", updateHandler)
|
||||
err := http.ListenAndServe(address, nil)
|
||||
check.IfError(err)
|
||||
|
|
|
|||
Loading…
Reference in a new issue