Basic gin site
This commit is contained in:
parent
8826bd8cb6
commit
0c410d9458
59 changed files with 640 additions and 2062 deletions
|
|
@ -3,19 +3,18 @@ package main
|
|||
import (
|
||||
"flag"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/check"
|
||||
_ "time/tzdata"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/web"
|
||||
)
|
||||
|
||||
const configPath = "/data/config.yaml"
|
||||
const dirPath = "/data/WatchYourLAN"
|
||||
const nodePath = ""
|
||||
|
||||
func main() {
|
||||
confPtr := flag.String("c", configPath, "Path to config file")
|
||||
dirPtr := flag.String("d", dirPath, "Path to config dir")
|
||||
nodePtr := flag.String("n", nodePath, "Path to node modules")
|
||||
flag.Parse()
|
||||
|
||||
check.Path(*confPtr)
|
||||
|
||||
web.Gui(*confPtr, *nodePtr) // webgui.go
|
||||
web.Gui(*dirPtr, *nodePtr) // webgui.go
|
||||
}
|
||||
|
|
|
|||
55
go.mod
55
go.mod
|
|
@ -1,28 +1,34 @@
|
|||
module github.com/aceberg/WatchYourLAN
|
||||
|
||||
go 1.21.4
|
||||
go 1.22.4
|
||||
|
||||
require (
|
||||
github.com/containrrr/shoutrrr v0.8.0
|
||||
github.com/google/uuid v1.5.0
|
||||
github.com/jmoiron/sqlx v1.3.5
|
||||
github.com/spf13/viper v1.18.2
|
||||
golang.org/x/crypto v0.17.0
|
||||
modernc.org/sqlite v1.28.0
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/spf13/viper v1.19.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/fatih/color v1.15.0 // indirect
|
||||
github.com/bytedance/sonic v1.11.6 // indirect
|
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.20.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
|
|
@ -30,22 +36,17 @@ require (
|
|||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/crypto v0.23.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/tools v0.13.0 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/text v0.15.0 // indirect
|
||||
google.golang.org/protobuf v1.34.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/uint128 v1.2.0 // indirect
|
||||
modernc.org/cc/v3 v3.40.0 // indirect
|
||||
modernc.org/ccgo/v3 v3.16.13 // indirect
|
||||
modernc.org/libc v1.29.0 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
modernc.org/memory v1.7.2 // indirect
|
||||
modernc.org/opt v0.1.3 // indirect
|
||||
modernc.org/strutil v1.1.3 // indirect
|
||||
modernc.org/token v1.0.1 // indirect
|
||||
)
|
||||
|
|
|
|||
160
go.sum
160
go.sum
|
|
@ -1,68 +1,68 @@
|
|||
github.com/containrrr/shoutrrr v0.8.0 h1:mfG2ATzIS7NR2Ec6XL+xyoHzN97H8WPjir8aYzJUSec=
|
||||
github.com/containrrr/shoutrrr v0.8.0/go.mod h1:ioyQAyu1LJY6sILuNyKaQaw+9Ttik5QePU8atnAdO2o=
|
||||
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
||||
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
|
||||
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
||||
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc=
|
||||
github.com/jarcoal/httpmock v1.3.0/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
|
||||
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
|
||||
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU=
|
||||
github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts=
|
||||
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
|
||||
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
|
||||
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
||||
|
|
@ -77,41 +77,47 @@ github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
|||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
|
||||
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
|
||||
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
||||
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
||||
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
||||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
||||
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
|
@ -120,31 +126,5 @@ gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
|||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
|
||||
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
|
||||
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
|
||||
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
|
||||
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
|
||||
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
|
||||
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
|
||||
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
|
||||
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
|
||||
modernc.org/libc v1.29.0 h1:tTFRFq69YKCF2QyGNuRUQxKBm1uZZLubf6Cjh/pVHXs=
|
||||
modernc.org/libc v1.29.0/go.mod h1:DaG/4Q3LRRdqpiLyP0C2m1B8ZMGkQ+cCgOIjEtQlYhQ=
|
||||
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
|
||||
modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=
|
||||
modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
|
||||
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ=
|
||||
modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0=
|
||||
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
|
||||
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
|
||||
modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY=
|
||||
modernc.org/tcl v1.15.2/go.mod h1:3+k/ZaEbKrC8ePv8zJWPtBSW0V7Gg9g8rkmhI1Kfs3c=
|
||||
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
|
||||
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY=
|
||||
modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Auth - main auth func
|
||||
func Auth(next http.HandlerFunc, conf *Conf) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if !conf.Auth || conf.User == "" || conf.Password == "" {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
sessionToken := getTokenFromCookie(r)
|
||||
|
||||
userSession, exists := allSessions[sessionToken]
|
||||
if !exists {
|
||||
http.Redirect(w, r, "/login/", 302)
|
||||
return
|
||||
}
|
||||
if userSession.Before(time.Now()) {
|
||||
delete(allSessions, sessionToken)
|
||||
http.Redirect(w, r, "/login/", 302)
|
||||
return
|
||||
}
|
||||
|
||||
userSession = time.Now().Add(conf.Expire)
|
||||
allSessions[sessionToken] = userSession
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func setTokenCookie(w http.ResponseWriter, token string) {
|
||||
|
||||
cookie := http.Cookie{Name: cookieName, Value: token, Path: "/"}
|
||||
http.SetCookie(w, &cookie)
|
||||
}
|
||||
|
||||
func getTokenFromCookie(r *http.Request) string {
|
||||
|
||||
cookie, err := r.Cookie(cookieName)
|
||||
if err != nil {
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
return cookie.Value
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// HashPassword - generate hash from password
|
||||
func HashPassword(pw string) string {
|
||||
|
||||
hashed, err := bcrypt.GenerateFromPassword([]byte(pw), 10)
|
||||
if err != nil {
|
||||
log.Println("PASSWORD ERROR:", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
return string(hashed)
|
||||
}
|
||||
|
||||
// MatchPasswords - check if password and hash matches
|
||||
func MatchPasswords(hash, pw string) bool {
|
||||
|
||||
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(pw))
|
||||
|
||||
return err == nil
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Conf - auth config
|
||||
type Conf struct {
|
||||
Auth bool
|
||||
User string
|
||||
Password string
|
||||
ExpStr string
|
||||
Expire time.Duration
|
||||
}
|
||||
|
||||
var allSessions = map[string]time.Time{}
|
||||
|
||||
var cookieName = "wyl_session_token"
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// StartSession for new login
|
||||
func StartSession(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
sessionToken := uuid.NewString()
|
||||
|
||||
allSessions[sessionToken] = time.Now().Add(60 * time.Second)
|
||||
|
||||
setTokenCookie(w, sessionToken)
|
||||
|
||||
http.Redirect(w, r, "/", 302)
|
||||
|
||||
}
|
||||
|
||||
// LogOut - log out
|
||||
func LogOut(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
sessionToken := getTokenFromCookie(r)
|
||||
|
||||
delete(allSessions, sessionToken)
|
||||
|
||||
setTokenCookie(w, "")
|
||||
|
||||
http.Redirect(w, r, "/", 302)
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
func timeParse(timeout string) (time.Duration, error) {
|
||||
var err error
|
||||
var t time.Duration
|
||||
|
||||
length := len(timeout)
|
||||
if length > 1 {
|
||||
suffix := timeout[length-1]
|
||||
|
||||
switch string(suffix) {
|
||||
case "h":
|
||||
t, err = time.ParseDuration(timeout)
|
||||
case "m":
|
||||
t, err = time.ParseDuration(timeout)
|
||||
case "d": //day
|
||||
t, err = time.ParseDuration(timeout[:length-1] + "h")
|
||||
t = 24 * t
|
||||
case "M": // month
|
||||
t, err = time.ParseDuration(timeout[:length-1] + "h")
|
||||
t = 730 * t
|
||||
default:
|
||||
err = errors.New("ERROR: TimeParse: wrong time format")
|
||||
}
|
||||
} else {
|
||||
err = errors.New("ERROR: TimeParse: wrong time format")
|
||||
}
|
||||
|
||||
return t, err
|
||||
}
|
||||
|
||||
// ToTime - converts string (example: 3d) to time.Duration
|
||||
func ToTime(s string) time.Duration {
|
||||
|
||||
t, err := timeParse(s)
|
||||
if err != nil {
|
||||
t, _ = timeParse("7d")
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
package check
|
||||
|
||||
import (
|
||||
"log"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
// IfError prints error, if it is not nil
|
||||
|
|
@ -10,6 +11,6 @@ func IfError(err error) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
log.Println("ERROR:", err)
|
||||
slog.Error(fmt.Sprintf("%v", err))
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,88 +0,0 @@
|
|||
package conf
|
||||
|
||||
import (
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/auth"
|
||||
"github.com/aceberg/WatchYourLAN/internal/check"
|
||||
"github.com/aceberg/WatchYourLAN/internal/models"
|
||||
)
|
||||
|
||||
// Get - get app config
|
||||
func Get(path string) (config models.Conf, authConf auth.Conf) {
|
||||
|
||||
viper.SetDefault("IFACE", "enp1s0")
|
||||
viper.SetDefault("DBPATH", "/data/db.sqlite")
|
||||
viper.SetDefault("GUIIP", "0.0.0.0")
|
||||
viper.SetDefault("GUIPORT", "8840")
|
||||
viper.SetDefault("TIMEOUT", "60")
|
||||
viper.SetDefault("SHOUTRRR_URL", "")
|
||||
viper.SetDefault("THEME", "solar")
|
||||
viper.SetDefault("COLOR", "dark")
|
||||
viper.SetDefault("IGNOREIP", "no")
|
||||
viper.SetDefault("LOGLEVEL", "verbose")
|
||||
viper.SetDefault("HISTORY_DAYS", "30")
|
||||
viper.SetDefault("ARP_TIMEOUT", "500")
|
||||
|
||||
viper.SetDefault("AUTH_USER", "")
|
||||
viper.SetDefault("AUTH_PASSWORD", "")
|
||||
viper.SetDefault("AUTH_EXPIRE", "7d")
|
||||
|
||||
viper.SetConfigFile(path)
|
||||
viper.SetConfigType("yaml")
|
||||
err := viper.ReadInConfig()
|
||||
check.IfError(err)
|
||||
|
||||
viper.AutomaticEnv() // Get ENVIRONMENT variables
|
||||
|
||||
config.Iface = viper.Get("IFACE").(string)
|
||||
config.DbPath = viper.Get("DBPATH").(string)
|
||||
config.GuiIP = viper.Get("GUIIP").(string)
|
||||
config.GuiPort = viper.Get("GUIPORT").(string)
|
||||
config.Timeout = viper.GetInt("TIMEOUT")
|
||||
config.ShoutURL = viper.Get("SHOUTRRR_URL").(string)
|
||||
config.Theme = viper.Get("THEME").(string)
|
||||
config.Color = viper.Get("COLOR").(string)
|
||||
config.IgnoreIP = viper.Get("IGNOREIP").(string)
|
||||
config.LogLevel = viper.Get("LOGLEVEL").(string)
|
||||
config.HistDays = viper.Get("HISTORY_DAYS").(string)
|
||||
config.ArpTimeout = viper.Get("ARP_TIMEOUT").(string)
|
||||
|
||||
authConf.Auth = viper.GetBool("AUTH")
|
||||
authConf.User, _ = viper.Get("AUTH_USER").(string)
|
||||
authConf.Password, _ = viper.Get("AUTH_PASSWORD").(string)
|
||||
authConf.ExpStr, _ = viper.Get("AUTH_EXPIRE").(string)
|
||||
|
||||
authConf.Expire = auth.ToTime(authConf.ExpStr)
|
||||
config.Auth = authConf.Auth
|
||||
|
||||
return config, authConf
|
||||
}
|
||||
|
||||
// Write - write config to file
|
||||
func Write(path string, config models.Conf, authConf auth.Conf) {
|
||||
|
||||
viper.SetConfigFile(path)
|
||||
viper.SetConfigType("yaml")
|
||||
|
||||
viper.Set("IFACE", config.Iface)
|
||||
viper.Set("DBPATH", config.DbPath)
|
||||
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)
|
||||
viper.Set("COLOR", config.Color)
|
||||
viper.Set("IGNOREIP", config.IgnoreIP)
|
||||
viper.Set("LOGLEVEL", config.LogLevel)
|
||||
viper.Set("HISTORY_DAYS", config.HistDays)
|
||||
viper.Set("ARP_TIMEOUT", config.ArpTimeout)
|
||||
|
||||
viper.Set("auth", authConf.Auth)
|
||||
viper.Set("auth_user", authConf.User)
|
||||
viper.Set("auth_password", authConf.Password)
|
||||
viper.Set("auth_expire", authConf.ExpStr)
|
||||
|
||||
err := viper.WriteConfig()
|
||||
check.IfError(err)
|
||||
}
|
||||
46
internal/conf/getconfig.go
Normal file
46
internal/conf/getconfig.go
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package conf
|
||||
|
||||
import (
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/check"
|
||||
"github.com/aceberg/WatchYourLAN/internal/models"
|
||||
)
|
||||
|
||||
// Get - get app config
|
||||
func Get(path string) (config models.Conf) {
|
||||
|
||||
viper.SetDefault("HOST", "0.0.0.0")
|
||||
viper.SetDefault("PORT", "8840")
|
||||
viper.SetDefault("THEME", "solar")
|
||||
viper.SetDefault("COLOR", "dark")
|
||||
|
||||
viper.SetConfigFile(path)
|
||||
viper.SetConfigType("yaml")
|
||||
err := viper.ReadInConfig()
|
||||
check.IfError(err)
|
||||
|
||||
viper.AutomaticEnv() // Get ENVIRONMENT variables
|
||||
|
||||
config.Host = viper.Get("HOST").(string)
|
||||
config.Port = viper.Get("PORT").(string)
|
||||
config.Theme = viper.Get("THEME").(string)
|
||||
config.Color = viper.Get("COLOR").(string)
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
// Write - write config to file
|
||||
func Write(config models.Conf) {
|
||||
|
||||
viper.SetConfigFile(config.ConfPath)
|
||||
viper.SetConfigType("yaml")
|
||||
|
||||
viper.Set("HOST", config.Host)
|
||||
viper.Set("PORT", config.Port)
|
||||
viper.Set("THEME", config.Theme)
|
||||
viper.Set("COLOR", config.Color)
|
||||
|
||||
err := viper.WriteConfig()
|
||||
check.IfError(err)
|
||||
}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/models"
|
||||
)
|
||||
|
||||
// Create - create DB if not exists
|
||||
func Create(path string) {
|
||||
|
||||
sqlStatement := `CREATE TABLE IF NOT EXISTS "now" (
|
||||
"ID" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
|
||||
"NAME" TEXT NOT NULL,
|
||||
"IP" TEXT,
|
||||
"MAC" TEXT,
|
||||
"HW" TEXT,
|
||||
"DATE" TEXT,
|
||||
"KNOWN" INTEGER DEFAULT 0,
|
||||
"NOW" INTEGER DEFAULT 0
|
||||
);`
|
||||
dbExec(path, sqlStatement)
|
||||
|
||||
sqlStatement = `CREATE TABLE IF NOT EXISTS "history" (
|
||||
"ID" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
|
||||
"HOST" INTEGER DEFAULT 0,
|
||||
"NAME" TEXT NOT NULL,
|
||||
"IP" TEXT,
|
||||
"MAC" TEXT,
|
||||
"HW" TEXT,
|
||||
"DATE" TEXT,
|
||||
"KNOWN" INTEGER DEFAULT 0,
|
||||
"STATE" INTEGER DEFAULT 0
|
||||
);`
|
||||
dbExec(path, sqlStatement)
|
||||
}
|
||||
|
||||
// Insert - insert host into table
|
||||
func Insert(path string, oneHost models.Host) {
|
||||
oneHost.Name = quoteStr(oneHost.Name)
|
||||
oneHost.Hw = quoteStr(oneHost.Hw)
|
||||
sqlStatement := `INSERT INTO "now" (NAME, IP, MAC, HW, DATE, KNOWN, NOW)
|
||||
VALUES ('%s','%s','%s','%s','%s','%d','%d');`
|
||||
sqlStatement = fmt.Sprintf(sqlStatement, oneHost.Name, oneHost.IP, oneHost.Mac, oneHost.Hw, oneHost.Date, oneHost.Known, oneHost.Now)
|
||||
|
||||
dbExec(path, sqlStatement)
|
||||
}
|
||||
|
||||
// Update - update host
|
||||
func Update(path string, oneHost models.Host) {
|
||||
oneHost.Name = quoteStr(oneHost.Name)
|
||||
oneHost.Hw = quoteStr(oneHost.Hw)
|
||||
sqlStatement := `UPDATE "now" set
|
||||
NAME = '%s', IP = '%s', MAC = '%s', HW = '%s', DATE = '%s',
|
||||
KNOWN = '%d', NOW = '%d'
|
||||
WHERE ID = '%d';`
|
||||
sqlStatement = fmt.Sprintf(sqlStatement, oneHost.Name, oneHost.IP, oneHost.Mac, oneHost.Hw, oneHost.Date, oneHost.Known, oneHost.Now, oneHost.ID)
|
||||
|
||||
dbExec(path, sqlStatement)
|
||||
}
|
||||
|
||||
// Delete - delete host from DB
|
||||
func Delete(path string, id int) {
|
||||
sqlStatement := `DELETE FROM "now" WHERE ID='%d';`
|
||||
sqlStatement = fmt.Sprintf(sqlStatement, id)
|
||||
dbExec(path, sqlStatement)
|
||||
}
|
||||
|
||||
// Clear - delete all hosts from table
|
||||
func Clear(path string) {
|
||||
sqlStatement := `DELETE FROM "now";`
|
||||
dbExec(path, sqlStatement)
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/models"
|
||||
)
|
||||
|
||||
// InsertHist - insert history into table
|
||||
func InsertHist(path string, hist models.History) {
|
||||
hist.Name = quoteStr(hist.Name)
|
||||
hist.Hw = quoteStr(hist.Hw)
|
||||
sqlStatement := `INSERT INTO "history" (HOST, NAME, IP, MAC, HW, DATE, KNOWN, STATE)
|
||||
VALUES ('%d','%s','%s','%s','%s','%s','%d','%d');`
|
||||
sqlStatement = fmt.Sprintf(sqlStatement, hist.Host, hist.Name, hist.IP, hist.Mac, hist.Hw, hist.Date, hist.Known, hist.State)
|
||||
//fmt.Println("Insert statement:", sqlStatement)
|
||||
dbExec(path, sqlStatement)
|
||||
}
|
||||
|
||||
// DeleteHist - delete history from DB
|
||||
func DeleteHist(path string, id int) {
|
||||
sqlStatement := `DELETE FROM "history" WHERE ID='%d';`
|
||||
sqlStatement = fmt.Sprintf(sqlStatement, id)
|
||||
dbExec(path, sqlStatement)
|
||||
}
|
||||
|
||||
// ClearHist - delete all history from table
|
||||
func ClearHist(path string) {
|
||||
sqlStatement := `DELETE FROM "history";`
|
||||
dbExec(path, sqlStatement)
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package db
|
||||
|
||||
import "strings"
|
||||
|
||||
func quoteStr(str string) string {
|
||||
return strings.ReplaceAll(str, "'", "''")
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
|
||||
// Import sqlite module
|
||||
_ "modernc.org/sqlite"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/check"
|
||||
"github.com/aceberg/WatchYourLAN/internal/models"
|
||||
)
|
||||
|
||||
var mu sync.Mutex
|
||||
|
||||
func dbExec(path, sqlStatement string) {
|
||||
|
||||
mu.Lock()
|
||||
db, err := sqlx.Connect("sqlite", path)
|
||||
check.IfError(err)
|
||||
defer db.Close()
|
||||
|
||||
_, err = db.Exec(sqlStatement)
|
||||
mu.Unlock()
|
||||
|
||||
check.IfError(err)
|
||||
}
|
||||
|
||||
// Select - select all hosts
|
||||
func Select(path string) (dbHosts []models.Host) {
|
||||
|
||||
sqlStatement := `SELECT * FROM "now" ORDER BY DATE DESC`
|
||||
|
||||
mu.Lock()
|
||||
db, _ := sqlx.Connect("sqlite", path)
|
||||
defer db.Close()
|
||||
|
||||
err := db.Select(&dbHosts, sqlStatement)
|
||||
mu.Unlock()
|
||||
|
||||
check.IfError(err)
|
||||
|
||||
return dbHosts
|
||||
}
|
||||
|
||||
// SelectHist - select all history
|
||||
func SelectHist(path string) (hist []models.History) {
|
||||
|
||||
sqlStatement := `SELECT * FROM "history" ORDER BY ID DESC`
|
||||
|
||||
mu.Lock()
|
||||
db, _ := sqlx.Connect("sqlite", path)
|
||||
defer db.Close()
|
||||
|
||||
err := db.Select(&hist, sqlStatement)
|
||||
mu.Unlock()
|
||||
|
||||
check.IfError(err)
|
||||
|
||||
return hist
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
package migrate
|
||||
|
||||
import (
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/models"
|
||||
)
|
||||
|
||||
func getOldConfig(path string) (config models.Conf) {
|
||||
|
||||
viper.SetDefault("IFACE", "enp1s0")
|
||||
viper.SetDefault("DBPATH", "/data/db.sqlite")
|
||||
viper.SetDefault("GUIIP", "localhost")
|
||||
viper.SetDefault("GUIPORT", "8840")
|
||||
viper.SetDefault("TIMEOUT", "60")
|
||||
viper.SetDefault("SHOUTRRR_URL", "")
|
||||
viper.SetDefault("THEME", "solar")
|
||||
viper.SetDefault("COLOR", "light")
|
||||
viper.SetDefault("IGNOREIP", "no")
|
||||
viper.SetDefault("LOGLEVEL", "verbose")
|
||||
|
||||
viper.SetConfigFile(path)
|
||||
viper.SetConfigType("env")
|
||||
|
||||
viper.AutomaticEnv() // Get ENVIRONMENT variables
|
||||
|
||||
err := viper.ReadInConfig()
|
||||
|
||||
if err == nil {
|
||||
|
||||
config.Iface = viper.Get("IFACE").(string)
|
||||
config.DbPath = viper.Get("DBPATH").(string)
|
||||
config.GuiIP = viper.Get("GUIIP").(string)
|
||||
config.GuiPort = viper.Get("GUIPORT").(string)
|
||||
config.Timeout = viper.GetInt("TIMEOUT")
|
||||
config.ShoutURL = viper.Get("SHOUTRRR_URL").(string)
|
||||
config.Theme = viper.Get("THEME").(string)
|
||||
config.Color = viper.Get("COLOR").(string)
|
||||
config.IgnoreIP = viper.Get("IGNOREIP").(string)
|
||||
config.LogLevel = viper.Get("LOGLEVEL").(string)
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
package migrate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/auth"
|
||||
"github.com/aceberg/WatchYourLAN/internal/check"
|
||||
"github.com/aceberg/WatchYourLAN/internal/conf"
|
||||
"github.com/aceberg/WatchYourLAN/internal/notify"
|
||||
)
|
||||
|
||||
// ToYAML - check if config file is YAML and migrate from ENV-file if not
|
||||
func ToYAML(path string) string {
|
||||
|
||||
if check.IsYaml(path) {
|
||||
if check.IsEmpty(path) {
|
||||
|
||||
oldPath := path[:len(path)-5] // path w/o .yaml
|
||||
|
||||
if !check.IsEmpty(oldPath) {
|
||||
migrateConfig(oldPath, path)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
newPath := path + ".yaml"
|
||||
check.Path(newPath)
|
||||
|
||||
if check.IsEmpty(newPath) && !check.IsEmpty(path) {
|
||||
|
||||
migrateConfig(path, newPath)
|
||||
}
|
||||
|
||||
path = newPath
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
func migrateConfig(oldPath, newPath string) {
|
||||
var authConf auth.Conf
|
||||
|
||||
config := getOldConfig(oldPath)
|
||||
conf.Write(newPath, config, authConf)
|
||||
|
||||
msg := fmt.Sprintf("Config file migrated from ENV to YAML format. \nOld config file %s is no longer used and can be deleted. \nNew config file is %s, please update your settings.", oldPath, newPath)
|
||||
|
||||
log.Println("=================================== ")
|
||||
log.Println("WARNING:", msg)
|
||||
log.Println("=================================== ")
|
||||
notify.Shoutrrr("WatchYourLAN: "+msg, config.ShoutURL)
|
||||
}
|
||||
|
|
@ -1,26 +1,14 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"github.com/aceberg/WatchYourLAN/internal/auth"
|
||||
)
|
||||
|
||||
// Conf - app config
|
||||
type Conf struct {
|
||||
Iface string
|
||||
DbPath string
|
||||
GuiIP string
|
||||
GuiPort string
|
||||
Timeout int
|
||||
ShoutURL string
|
||||
Theme string
|
||||
Color string
|
||||
IgnoreIP string
|
||||
LogLevel string
|
||||
NodePath string
|
||||
Icon string
|
||||
Auth bool
|
||||
HistDays string
|
||||
ArpTimeout string
|
||||
Host string
|
||||
Port string
|
||||
Theme string
|
||||
Color string
|
||||
DirPath string
|
||||
ConfPath string
|
||||
NodePath string
|
||||
}
|
||||
|
||||
// Host - one host
|
||||
|
|
@ -54,6 +42,5 @@ type GuiData struct {
|
|||
Hosts []Host
|
||||
Themes []string
|
||||
Version string
|
||||
Auth auth.Conf
|
||||
Hist []History
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
package notify
|
||||
|
||||
import (
|
||||
"github.com/containrrr/shoutrrr"
|
||||
"log"
|
||||
)
|
||||
|
||||
// Shoutrrr - send message with shoutrrr
|
||||
func Shoutrrr(message string, url string) {
|
||||
if url != "" {
|
||||
err := shoutrrr.Send(url, message)
|
||||
if err != nil {
|
||||
log.Println("ERROR: Notification failed (shoutrrr):", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
package port
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
func isOpen(host, proto string, port int) bool {
|
||||
|
||||
timeout := 3 * time.Second
|
||||
target := fmt.Sprintf("%s:%d", host, port)
|
||||
|
||||
conn, err := net.DialTimeout(proto, target, timeout)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if conn != nil {
|
||||
conn.Close()
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Scan - scan all TCP ports of a host
|
||||
func Scan(host, proto string, begin, end int) []string {
|
||||
var onePort string
|
||||
var ports []string
|
||||
|
||||
ports = []string{}
|
||||
|
||||
for i := begin; i < end+1; i++ {
|
||||
if isOpen(host, proto, i) {
|
||||
onePort = fmt.Sprintf("%s:%d", host, i)
|
||||
ports = append(ports, onePort)
|
||||
}
|
||||
}
|
||||
|
||||
return ports
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
package scan
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/models"
|
||||
)
|
||||
|
||||
func scanIface(iface string) string {
|
||||
cmd, err := exec.Command("arp-scan", "-glNx", "-t", appConfig.ArpTimeout, "-I", iface).Output()
|
||||
if err != nil {
|
||||
return string("")
|
||||
}
|
||||
return string(cmd)
|
||||
}
|
||||
|
||||
func parseOutput(text string) []models.Host {
|
||||
var foundHosts = []models.Host{}
|
||||
|
||||
perString := strings.Split(text, "\n")
|
||||
currentTime := time.Now()
|
||||
|
||||
for _, host := range perString {
|
||||
if host != "" {
|
||||
var oneHost models.Host
|
||||
p := strings.Split(host, " ")
|
||||
oneHost.IP = p[0]
|
||||
oneHost.Mac = p[1]
|
||||
oneHost.Hw = p[2]
|
||||
oneHost.Date = currentTime.Format("2006-01-02 15:04:05")
|
||||
oneHost.Now = 1
|
||||
foundHosts = append(foundHosts, oneHost)
|
||||
}
|
||||
}
|
||||
|
||||
return foundHosts
|
||||
}
|
||||
|
||||
// Scan all interfaces
|
||||
func arpScan() []models.Host {
|
||||
var text string
|
||||
var foundHosts = []models.Host{}
|
||||
|
||||
perString := strings.Split(appConfig.Iface, " ")
|
||||
|
||||
for _, iface := range perString {
|
||||
text = scanIface(iface)
|
||||
if appConfig.LogLevel != "short" {
|
||||
log.Println("INFO: scanning interface", iface)
|
||||
log.Println("INFO: found IPs:", text)
|
||||
}
|
||||
foundHosts = append(foundHosts, parseOutput(text)...)
|
||||
}
|
||||
|
||||
return foundHosts
|
||||
}
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
package scan
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/db"
|
||||
"github.com/aceberg/WatchYourLAN/internal/models"
|
||||
"github.com/aceberg/WatchYourLAN/internal/notify"
|
||||
)
|
||||
|
||||
func hostsCompare() {
|
||||
|
||||
for _, oneHost := range dbHosts {
|
||||
|
||||
host, exists := foundHostsMap[oneHost.Mac]
|
||||
|
||||
if exists && (appConfig.IgnoreIP == "yes" || host.IP == oneHost.IP) {
|
||||
|
||||
oneHost.Date = host.Date
|
||||
|
||||
if oneHost.Now == 0 {
|
||||
histAdd(oneHost, 1)
|
||||
}
|
||||
|
||||
oneHost.Now = 1
|
||||
|
||||
delete(foundHostsMap, oneHost.Mac)
|
||||
|
||||
db.Update(appConfig.DbPath, oneHost)
|
||||
|
||||
} else if oneHost.Now == 1 {
|
||||
oneHost.Now = 0
|
||||
oneHost.Date = time.Now().Format("2006-01-02 15:04:05")
|
||||
|
||||
db.Update(appConfig.DbPath, oneHost)
|
||||
|
||||
histAdd(oneHost, 0)
|
||||
}
|
||||
}
|
||||
|
||||
for _, oneHost := range foundHostsMap {
|
||||
|
||||
msg := fmt.Sprintf("UNKNOWN HOST IP: '%s', MAC: '%s', Hw: '%s'", oneHost.IP, oneHost.Mac, oneHost.Hw)
|
||||
log.Println("WARN:", msg)
|
||||
notify.Shoutrrr(msg, appConfig.ShoutURL) // Notify through Shoutrrr
|
||||
|
||||
db.Insert(appConfig.DbPath, oneHost)
|
||||
|
||||
histAdd(oneHost, 1)
|
||||
}
|
||||
}
|
||||
|
||||
func histAdd(oneHost models.Host, state int) {
|
||||
var history models.History
|
||||
|
||||
if oneHost.ID == 0 {
|
||||
dbHosts = db.Select(appConfig.DbPath)
|
||||
|
||||
for _, host := range dbHosts {
|
||||
if host.IP == oneHost.IP && host.Mac == oneHost.Mac {
|
||||
oneHost.ID = host.ID
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
history.Host = oneHost.ID
|
||||
history.Name = oneHost.Name
|
||||
history.IP = oneHost.IP
|
||||
history.Mac = oneHost.Mac
|
||||
history.Hw = oneHost.Hw
|
||||
history.Date = oneHost.Date
|
||||
history.Known = oneHost.Known
|
||||
history.State = state
|
||||
|
||||
db.InsertHist(appConfig.DbPath, history)
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
package scan
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/db"
|
||||
"github.com/aceberg/WatchYourLAN/internal/models"
|
||||
)
|
||||
|
||||
var appConfig models.Conf
|
||||
var dbHosts, structHosts []models.Host
|
||||
var foundHostsMap map[string]models.Host
|
||||
|
||||
// Start - start arp-scan goroutine
|
||||
func Start(config models.Conf, quit chan bool) {
|
||||
var lastDate time.Time
|
||||
|
||||
appConfig = config
|
||||
db.Create(appConfig.DbPath)
|
||||
dbHosts = db.Select(appConfig.DbPath)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-quit:
|
||||
return
|
||||
default:
|
||||
nowDate := time.Now()
|
||||
plusDate := lastDate.Add(time.Duration(appConfig.Timeout) * time.Second)
|
||||
|
||||
if nowDate.After(plusDate) {
|
||||
structHosts = arpScan() // arpscan.go
|
||||
dbHosts = db.Select(appConfig.DbPath)
|
||||
|
||||
toMap()
|
||||
hostsCompare() // compare.go
|
||||
|
||||
lastDate = time.Now()
|
||||
}
|
||||
|
||||
time.Sleep(time.Duration(1) * time.Second)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func toMap() {
|
||||
foundHostsMap = make(map[string]models.Host)
|
||||
|
||||
for _, host := range structHosts {
|
||||
foundHostsMap[host.Mac] = host
|
||||
}
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/auth"
|
||||
"github.com/aceberg/WatchYourLAN/internal/conf"
|
||||
"github.com/aceberg/WatchYourLAN/internal/models"
|
||||
)
|
||||
|
||||
func authConfHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var guiData models.GuiData
|
||||
|
||||
guiData.Config = AppConfig
|
||||
guiData.Auth.Auth = authConf.Auth
|
||||
guiData.Auth.User = authConf.User
|
||||
guiData.Auth.ExpStr = authConf.ExpStr
|
||||
|
||||
execTemplate(w, "auth", guiData)
|
||||
}
|
||||
|
||||
func saveAuthHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
authConf.User = r.FormValue("user")
|
||||
authConf.ExpStr = r.FormValue("expire")
|
||||
|
||||
authStr := r.FormValue("auth")
|
||||
pw := r.FormValue("password")
|
||||
|
||||
if authStr == "on" {
|
||||
authConf.Auth = true
|
||||
} else {
|
||||
authConf.Auth = false
|
||||
}
|
||||
AppConfig.Auth = authConf.Auth
|
||||
|
||||
if pw != "" {
|
||||
authConf.Password = auth.HashPassword(pw)
|
||||
}
|
||||
|
||||
authConf.Expire = auth.ToTime(authConf.ExpStr)
|
||||
|
||||
if authConf.Auth && (authConf.User == "" || authConf.Password == "") {
|
||||
log.Println("WARNING: Auth won't work with empty login or password.")
|
||||
authConf.Auth = false
|
||||
}
|
||||
|
||||
conf.Write(ConfigPath, AppConfig, authConf)
|
||||
|
||||
http.Redirect(w, r, r.Header.Get("Referer"), 302)
|
||||
}
|
||||
|
|
@ -1,80 +1,52 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"log"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strconv"
|
||||
// "strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/check"
|
||||
"github.com/aceberg/WatchYourLAN/internal/conf"
|
||||
"github.com/aceberg/WatchYourLAN/internal/db"
|
||||
// "github.com/aceberg/WatchYourLAN/internal/conf"
|
||||
"github.com/aceberg/WatchYourLAN/internal/models"
|
||||
"github.com/aceberg/WatchYourLAN/internal/notify"
|
||||
"github.com/aceberg/WatchYourLAN/internal/scan"
|
||||
)
|
||||
|
||||
func configHandler(w http.ResponseWriter, r *http.Request) {
|
||||
func configHandler(c *gin.Context) {
|
||||
var guiData models.GuiData
|
||||
|
||||
guiData.Config = AppConfig
|
||||
guiData.Config = appConfig
|
||||
|
||||
file, err := TemplHTML.ReadFile(TemplPath + "version")
|
||||
guiData.Themes = []string{"cerulean", "cosmo", "cyborg", "darkly", "emerald", "flatly", "grass", "grayscale", "journal", "litera", "lumen", "lux", "materia", "minty", "morph", "ocean", "pulse", "quartz", "sand", "sandstone", "simplex", "sketchy", "slate", "solar", "spacelab", "superhero", "united", "vapor", "wood", "yeti", "zephyr"}
|
||||
|
||||
file, err := pubFS.ReadFile("public/version")
|
||||
check.IfError(err)
|
||||
|
||||
version := string(file)
|
||||
guiData.Version = version[8:]
|
||||
|
||||
guiData.Themes = []string{"cerulean", "cosmo", "cyborg", "darkly", "emerald", "flatly", "grass", "journal", "litera", "lumen", "lux", "materia", "minty", "morph", "pulse", "quartz", "sand", "sandstone", "simplex", "sketchy", "slate", "solar", "spacelab", "superhero", "united", "vapor", "yeti", "zephyr"}
|
||||
|
||||
execTemplate(w, "config", guiData)
|
||||
c.HTML(http.StatusOK, "header.html", guiData)
|
||||
c.HTML(http.StatusOK, "config.html", guiData)
|
||||
}
|
||||
|
||||
func saveConfigHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
func saveConfigHandler(c *gin.Context) {
|
||||
|
||||
AppConfig.Iface = r.FormValue("iface")
|
||||
AppConfig.DbPath = r.FormValue("dbpath")
|
||||
AppConfig.GuiIP = r.FormValue("host")
|
||||
AppConfig.GuiPort = r.FormValue("port")
|
||||
AppConfig.ShoutURL = r.FormValue("shout")
|
||||
AppConfig.Theme = r.FormValue("theme")
|
||||
AppConfig.Color = r.FormValue("color")
|
||||
AppConfig.IgnoreIP = r.FormValue("ignoreip")
|
||||
AppConfig.LogLevel = r.FormValue("loglevel")
|
||||
AppConfig.HistDays = r.FormValue("history")
|
||||
AppConfig.ArpTimeout = r.FormValue("arp_timeout")
|
||||
// appConfig.Host = c.PostForm("host")
|
||||
// appConfig.Port = c.PostForm("port")
|
||||
// appConfig.Theme = c.PostForm("theme")
|
||||
// appConfig.Color = c.PostForm("color")
|
||||
// tStr := c.PostForm("timeout")
|
||||
// hStr := c.PostForm("trim")
|
||||
|
||||
timeout := r.FormValue("timeout")
|
||||
AppConfig.Timeout, err = strconv.Atoi(timeout)
|
||||
check.IfError(err)
|
||||
// if tStr != "" {
|
||||
// appConfig.Timeout, _ = strconv.Atoi(tStr)
|
||||
// }
|
||||
// if hStr != "" {
|
||||
// appConfig.HistTrim, _ = strconv.Atoi(hStr)
|
||||
// }
|
||||
|
||||
close(QuitScan)
|
||||
// conf.Write(appConfig)
|
||||
|
||||
conf.Write(ConfigPath, AppConfig, authConf)
|
||||
slog.Info("writing new config to " + appConfig.ConfPath)
|
||||
|
||||
log.Println("INFO: writing new config")
|
||||
|
||||
QuitScan = make(chan bool)
|
||||
go scan.Start(AppConfig, QuitScan)
|
||||
|
||||
http.Redirect(w, r, r.Header.Get("Referer"), 302)
|
||||
}
|
||||
|
||||
func clearHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
log.Println("INFO: delting all hosts from DB")
|
||||
|
||||
db.Clear(AppConfig.DbPath)
|
||||
|
||||
http.Redirect(w, r, r.Header.Get("Referer"), 302)
|
||||
}
|
||||
|
||||
func testNotifyHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
log.Println("INFO: Test notification send")
|
||||
|
||||
msg := "Test notification"
|
||||
notify.Shoutrrr(msg, AppConfig.ShoutURL)
|
||||
|
||||
http.Redirect(w, r, r.Header.Get("Referer"), 302)
|
||||
c.Redirect(http.StatusFound, "/config")
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -3,30 +3,20 @@ package web
|
|||
import (
|
||||
"embed"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/auth"
|
||||
"github.com/aceberg/WatchYourLAN/internal/models"
|
||||
)
|
||||
|
||||
var (
|
||||
authConf auth.Conf
|
||||
|
||||
// AppConfig - app config
|
||||
AppConfig models.Conf
|
||||
|
||||
// AllHosts - all hosts from DB
|
||||
AllHosts []models.Host
|
||||
|
||||
// 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
|
||||
// appConfig - config for Web Gui
|
||||
appConfig models.Conf
|
||||
)
|
||||
|
||||
// TemplPath - path to html templates
|
||||
const TemplPath = "templates/"
|
||||
// templFS - html templates
|
||||
//
|
||||
//go:embed templates/*
|
||||
var templFS embed.FS
|
||||
|
||||
// pubFS - public folder
|
||||
//
|
||||
//go:embed public/*
|
||||
var pubFS embed.FS
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/check"
|
||||
"github.com/aceberg/WatchYourLAN/internal/models"
|
||||
)
|
||||
|
||||
func execTemplate(w http.ResponseWriter, tpl string, guiData models.GuiData) {
|
||||
// tmpl, err := template.ParseFiles(TemplPath+tpl+".html", TemplPath+"header.html", TemplPath+"footer.html")
|
||||
tmpl, err := template.ParseFS(TemplHTML, TemplPath+tpl+".html", TemplPath+"header.html", TemplPath+"footer.html")
|
||||
check.IfError(err)
|
||||
err = tmpl.ExecuteTemplate(w, "header", guiData)
|
||||
check.IfError(err)
|
||||
err = tmpl.ExecuteTemplate(w, tpl, guiData)
|
||||
check.IfError(err)
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/db"
|
||||
"github.com/aceberg/WatchYourLAN/internal/models"
|
||||
)
|
||||
|
||||
func historyHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var guiData models.GuiData
|
||||
|
||||
guiData.Config = AppConfig
|
||||
guiData.Hist = db.SelectHist(AppConfig.DbPath)
|
||||
|
||||
execTemplate(w, "history", guiData)
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/check"
|
||||
"github.com/aceberg/WatchYourLAN/internal/db"
|
||||
"github.com/aceberg/WatchYourLAN/internal/models"
|
||||
)
|
||||
|
||||
func delHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
idStr := r.FormValue("id")
|
||||
id, err := strconv.Atoi(idStr)
|
||||
check.IfError(err)
|
||||
|
||||
log.Println("INFO: delete host ID =", id)
|
||||
|
||||
db.Delete(AppConfig.DbPath, id)
|
||||
|
||||
http.Redirect(w, r, "/", 302)
|
||||
}
|
||||
|
||||
func hostHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var guiData models.GuiData
|
||||
var host models.Host
|
||||
|
||||
guiData.Config = AppConfig
|
||||
|
||||
idStr := r.URL.Query().Get("id")
|
||||
|
||||
id, err := strconv.Atoi(idStr)
|
||||
check.IfError(err)
|
||||
|
||||
for _, oneHost := range AllHosts {
|
||||
if id == oneHost.ID {
|
||||
host = oneHost
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
guiData.Hosts = append(guiData.Hosts, host)
|
||||
|
||||
addr, err := net.LookupAddr(host.IP)
|
||||
check.IfError(err)
|
||||
|
||||
guiData.Themes = addr
|
||||
|
||||
history := db.SelectHist(AppConfig.DbPath)
|
||||
for _, hist := range history {
|
||||
if hist.Host == id {
|
||||
guiData.Hist = append(guiData.Hist, hist)
|
||||
}
|
||||
}
|
||||
|
||||
execTemplate(w, "host", guiData)
|
||||
}
|
||||
|
|
@ -3,16 +3,15 @@ package web
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/models"
|
||||
)
|
||||
|
||||
func indexHandler(w http.ResponseWriter, r *http.Request) {
|
||||
func indexHandler(c *gin.Context) {
|
||||
var guiData models.GuiData
|
||||
guiData.Config = appConfig
|
||||
|
||||
updateAllHosts()
|
||||
|
||||
guiData.Config = AppConfig
|
||||
guiData.Hosts = AllHosts
|
||||
|
||||
execTemplate(w, "index", guiData)
|
||||
c.HTML(http.StatusOK, "header.html", guiData)
|
||||
c.HTML(http.StatusOK, "index.html", guiData)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/models"
|
||||
)
|
||||
|
||||
func lineHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var guiData models.GuiData
|
||||
|
||||
guiData.Config = AppConfig
|
||||
guiData.Hosts = []models.Host{}
|
||||
|
||||
state := r.URL.Query().Get("state")
|
||||
|
||||
for _, oneHost := range AllHosts {
|
||||
if state == "off" && oneHost.Now == 0 {
|
||||
guiData.Hosts = append(guiData.Hosts, oneHost)
|
||||
}
|
||||
if state == "on" && oneHost.Now == 1 {
|
||||
guiData.Hosts = append(guiData.Hosts, oneHost)
|
||||
}
|
||||
}
|
||||
|
||||
execTemplate(w, "line", guiData)
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/auth"
|
||||
"github.com/aceberg/WatchYourLAN/internal/models"
|
||||
)
|
||||
|
||||
func loginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var guiData models.GuiData
|
||||
guiData.Config = AppConfig
|
||||
|
||||
username := r.FormValue("username")
|
||||
password := r.FormValue("password")
|
||||
logout := r.FormValue("logout")
|
||||
|
||||
if username == authConf.User && auth.MatchPasswords(authConf.Password, password) {
|
||||
|
||||
log.Println("INFO: user '"+username+"' logged in. Session expire time", authConf.Expire)
|
||||
|
||||
auth.StartSession(w, r)
|
||||
}
|
||||
if logout == "yes" {
|
||||
|
||||
auth.LogOut(w, r)
|
||||
}
|
||||
|
||||
execTemplate(w, "login", guiData)
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/check"
|
||||
"github.com/aceberg/WatchYourLAN/internal/models"
|
||||
"github.com/aceberg/WatchYourLAN/internal/port"
|
||||
)
|
||||
|
||||
func portHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var guiData models.GuiData
|
||||
|
||||
guiData.Config = AppConfig
|
||||
|
||||
ip := r.FormValue("ip")
|
||||
beginStr := r.FormValue("begin")
|
||||
endStr := r.FormValue("end")
|
||||
|
||||
begin, err := strconv.Atoi(beginStr)
|
||||
check.IfError(err)
|
||||
|
||||
end, err := strconv.Atoi(endStr)
|
||||
check.IfError(err)
|
||||
|
||||
guiData.Themes = port.Scan(ip, "tcp", begin, end)
|
||||
|
||||
execTemplate(w, "port", guiData)
|
||||
}
|
||||
9
internal/web/public/css/index.css
Normal file
9
internal/web/public/css/index.css
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
.my-button {
|
||||
height: 100%;
|
||||
background-color: #00000000;
|
||||
color: var(--bs-primary);
|
||||
text-align: center;
|
||||
}
|
||||
.my-button:hover {
|
||||
background-color: #0000001a;
|
||||
}
|
||||
BIN
internal/web/public/favicon.png
Normal file
BIN
internal/web/public/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
45
internal/web/public/js/history.js
Normal file
45
internal/web/public/js/history.js
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
var histArray = {};
|
||||
|
||||
loadHistory();
|
||||
|
||||
function createHTML(hist, i) {
|
||||
|
||||
let allState = "";
|
||||
let color = "";
|
||||
|
||||
for (let state of hist.State){
|
||||
if (state.State) {
|
||||
color = `bi-check-circle-fill" style="color:var(--bs-success);"`;
|
||||
} else {
|
||||
color = `bi-dash-circle-fill" style="color:var(--bs-danger);"`;
|
||||
}
|
||||
allState = allState + `<i class="bi ${color} title="${state.Date}"></i>`;
|
||||
}
|
||||
|
||||
let html = `
|
||||
<tr>
|
||||
<td style="opacity: 45%;">${i}.</td>
|
||||
<td><a href="/scan/?addr=${hist.Addr}">${hist.Name}</a></td>
|
||||
<td><a href="/scan/?addr=${hist.Addr}">${hist.Addr}</a></td>
|
||||
<td><a href="http://${hist.Addr}:${hist.Port}">${hist.Port}</a></td>
|
||||
<td><a href="http://${hist.Addr}:${hist.Port}">${hist.PortName}</a></td>
|
||||
<td>${allState}</td>
|
||||
</tr>`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
async function loadHistory() {
|
||||
|
||||
let url = '/api/history';
|
||||
let histMap = await (await fetch(url)).json();
|
||||
if (histMap != null) {
|
||||
histArray = Object.values(histMap);
|
||||
}
|
||||
|
||||
displayArrayData(histArray);
|
||||
}
|
||||
|
||||
function sortBy(field) {
|
||||
sortByAny(histArray, field);
|
||||
}
|
||||
46
internal/web/public/js/index.js
Normal file
46
internal/web/public/js/index.js
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
var addrsArray = {};
|
||||
|
||||
loadAddrs();
|
||||
|
||||
function createHTML(addr, i) {
|
||||
let off = '';
|
||||
|
||||
if (addr.Offline == 0) {
|
||||
off = `<span style="color:var(--bs-success);">0</span>`;
|
||||
} else {
|
||||
off = `<span style="color:var(--bs-danger);">${addr.Offline}</span>`;
|
||||
}
|
||||
|
||||
let html = `
|
||||
<tr>
|
||||
<td style="opacity: 45%;">${i}.</td>
|
||||
<td>
|
||||
<a href="/scan/?addr=${addr.Addr}">${addr.Name}</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="/scan/?addr=${addr.Addr}">${addr.Addr}</a>
|
||||
</td>
|
||||
<td>${addr.Total}</td>
|
||||
<td>${addr.Watching}</td>
|
||||
<td>${addr.Online}</td>
|
||||
<td>${off}</td>
|
||||
</tr>
|
||||
`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
async function loadAddrs() {
|
||||
|
||||
let url = '/api/all';
|
||||
let addrsMap = await (await fetch(url)).json();
|
||||
if (addrsMap != null) {
|
||||
addrsArray = Object.values(addrsMap);
|
||||
}
|
||||
|
||||
displayArrayData(addrsArray);
|
||||
}
|
||||
|
||||
function sortBy(field) {
|
||||
sortByAny(addrsArray, field);
|
||||
}
|
||||
125
internal/web/public/js/scan.js
Normal file
125
internal/web/public/js/scan.js
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
var addr = '';
|
||||
var stop = false;
|
||||
var portMap = {};
|
||||
var portArray = [];
|
||||
|
||||
// Run, when page loadad
|
||||
window.addEventListener('load', function() {
|
||||
loadSavedPorts();
|
||||
});
|
||||
|
||||
function delRow(id) {
|
||||
document.getElementById(id).innerHTML = "";
|
||||
}
|
||||
|
||||
function stopScan() {
|
||||
stop = true;
|
||||
}
|
||||
|
||||
async function scanAddr() {
|
||||
let begin = document.getElementById("begin").value;
|
||||
let end = document.getElementById("end").value;
|
||||
let savedPorts = [];
|
||||
|
||||
if (begin == "") {
|
||||
begin = 1
|
||||
}
|
||||
if (end == "") {
|
||||
end = 65535
|
||||
}
|
||||
let port = {};
|
||||
stop = false;
|
||||
|
||||
if (portMap != null) {
|
||||
savedPorts = Object.keys(portMap);
|
||||
}
|
||||
// console.log("Saved ports:", savedPorts);
|
||||
|
||||
document.getElementById('stopBtn').style.visibility = "visible";
|
||||
|
||||
for (let i = begin ; i <= end; i++) {
|
||||
|
||||
if (stop) {
|
||||
break;
|
||||
}
|
||||
|
||||
let url = '/api/port/'+addr+'/'+i;
|
||||
port = await (await fetch(url)).json();
|
||||
|
||||
document.getElementById("curPort").innerHTML = "Scanning port "+i;
|
||||
|
||||
if ((port.State) && (!savedPorts.includes(port.Port.toString()))) {
|
||||
html = createHTML(port, '', true);
|
||||
document.getElementById('tBody').insertAdjacentHTML('afterbegin', html);
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('stopBtn').style.visibility = "hidden";
|
||||
}
|
||||
|
||||
function createHTML(port, i, found) {
|
||||
let state = ``;
|
||||
let checked = ``;
|
||||
let sup = ``;
|
||||
|
||||
if (found) {
|
||||
sup = ` <sup style="background-color:var(--bs-danger);">new</sup>`;
|
||||
}
|
||||
|
||||
if (port.Watch) {
|
||||
checked = `checked`;
|
||||
|
||||
if (port.State) {
|
||||
state = `<i class="bi bi-check-circle-fill" style="color:var(--bs-success);"></i>`;
|
||||
} else {
|
||||
state = `<i class="bi bi-dash-circle-fill" style="color:var(--bs-danger);"></i>`;
|
||||
}
|
||||
} else {
|
||||
state = `<i class="bi bi-circle-fill" style="color:var(--bs-gray-500);"></i>`;
|
||||
}
|
||||
|
||||
let html = `
|
||||
<tr id="row${port.Port}">
|
||||
<td style="opacity: 45%;">${i}.</td>
|
||||
<td>
|
||||
<input name="name" type="text" class="form-control" value="${port.Name}">
|
||||
</td>
|
||||
<td>
|
||||
<a href="http://${addr}:${port.Port}">${port.Port}</a>${sup}
|
||||
<input name="port" type="hidden" value="${port.Port}">
|
||||
<input name="state" type="hidden" value="${port.State}">
|
||||
</td>
|
||||
<td>
|
||||
${state}
|
||||
</td>
|
||||
<td>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" value="${port.Port}" name="watch" ${checked}>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<a href="#" onclick="delRow('row${port.Port}')">
|
||||
<i class="bi bi-x-circle"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
async function loadSavedPorts() {
|
||||
|
||||
addr = document.getElementById("pageAddr").value;
|
||||
|
||||
let url = '/api/port/'+addr;
|
||||
portMap = await (await fetch(url)).json();
|
||||
if (portMap != null) {
|
||||
portArray = Object.values(portMap);
|
||||
}
|
||||
|
||||
displayArrayData(portArray); // sort.js
|
||||
}
|
||||
|
||||
function sortBy(field) {
|
||||
sortByAny(portArray, field); // sort.js
|
||||
}
|
||||
38
internal/web/public/js/sort.js
Normal file
38
internal/web/public/js/sort.js
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
var oldField = '';
|
||||
|
||||
function displayArrayData(someArray) {
|
||||
document.getElementById('tBody').innerHTML = "";
|
||||
|
||||
let i = 0;
|
||||
for (let item of someArray){
|
||||
i = i + 1;
|
||||
html = createHTML(item, i);
|
||||
document.getElementById('tBody').insertAdjacentHTML('beforeend', html);
|
||||
}
|
||||
}
|
||||
|
||||
function sortByAny(someArray, field) {
|
||||
// console.log("Field =", field);
|
||||
|
||||
if (field != oldField) {
|
||||
someArray.sort(byFieldDown(field));
|
||||
oldField = field;
|
||||
} else {
|
||||
someArray.sort(byFieldUp(field));
|
||||
oldField = '';
|
||||
}
|
||||
|
||||
if (field == 'State') {
|
||||
someArray.sort(byFieldUp('Watch'));
|
||||
}
|
||||
|
||||
displayArrayData(someArray);
|
||||
}
|
||||
|
||||
function byFieldUp(fieldName){
|
||||
return (a, b) => a[fieldName] < b[fieldName] ? 1 : -1;
|
||||
}
|
||||
|
||||
function byFieldDown(fieldName){
|
||||
return (a, b) => a[fieldName] > b[fieldName] ? 1 : -1;
|
||||
}
|
||||
1
internal/web/public/version
Normal file
1
internal/web/public/version
Normal file
|
|
@ -0,0 +1 @@
|
|||
VERSION=0.1.2
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/models"
|
||||
)
|
||||
|
||||
func searchHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var guiData models.GuiData
|
||||
|
||||
search := r.FormValue("search")
|
||||
|
||||
updateAllHosts()
|
||||
|
||||
foundHosts := []models.Host{}
|
||||
for _, oneHost := range AllHosts {
|
||||
if inString(oneHost.Name, search) || inString(oneHost.IP, search) || inString(oneHost.Mac, search) || inString(oneHost.Hw, search) || inString(oneHost.Date, search) {
|
||||
foundHosts = append(foundHosts, oneHost)
|
||||
}
|
||||
}
|
||||
|
||||
guiData.Config = AppConfig
|
||||
guiData.Hosts = foundHosts
|
||||
|
||||
execTemplate(w, "index", guiData)
|
||||
}
|
||||
|
||||
func inString(str1 string, str2 string) bool {
|
||||
return strings.Contains(
|
||||
strings.ToLower(str1),
|
||||
strings.ToLower(str2),
|
||||
)
|
||||
}
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/models"
|
||||
)
|
||||
|
||||
func sortByIPs(method string) {
|
||||
type ipHost struct {
|
||||
Host models.Host
|
||||
IP net.IP
|
||||
}
|
||||
toSort := []ipHost{}
|
||||
var oneSort ipHost
|
||||
|
||||
for _, oneHost := range AllHosts {
|
||||
oneSort.Host = oneHost
|
||||
oneSort.IP = net.ParseIP(oneHost.IP)
|
||||
toSort = append(toSort, oneSort)
|
||||
}
|
||||
|
||||
switch method {
|
||||
case "asc":
|
||||
sort.Slice(toSort, func(i, j int) bool {
|
||||
return bytes.Compare(toSort[i].IP, toSort[j].IP) < 0
|
||||
})
|
||||
case "desc":
|
||||
sort.Slice(toSort, func(i, j int) bool {
|
||||
return bytes.Compare(toSort[i].IP, toSort[j].IP) > 0
|
||||
})
|
||||
}
|
||||
|
||||
AllHosts = []models.Host{}
|
||||
for _, oneSort := range toSort {
|
||||
AllHosts = append(AllHosts, oneSort.Host)
|
||||
}
|
||||
}
|
||||
|
||||
func sortHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
sortMethod := r.FormValue("sort_method")
|
||||
|
||||
switch sortMethod {
|
||||
case "name-up":
|
||||
sortByField("asc", "Name")
|
||||
case "name-down":
|
||||
sortByField("desc", "Name")
|
||||
case "ip-up":
|
||||
sortByIPs("asc")
|
||||
case "ip-down":
|
||||
sortByIPs("desc")
|
||||
case "mac-up":
|
||||
sortByField("asc", "Mac")
|
||||
case "mac-down":
|
||||
sortByField("desc", "Mac")
|
||||
case "hw-up":
|
||||
sortByField("asc", "Hw")
|
||||
case "hw-down":
|
||||
sortByField("desc", "Hw")
|
||||
case "date-up":
|
||||
sortByField("asc", "Date")
|
||||
case "date-down":
|
||||
sortByField("desc", "Date")
|
||||
case "known-up":
|
||||
sortByField("asc", "Known")
|
||||
case "known-down":
|
||||
sortByField("desc", "Known")
|
||||
default:
|
||||
updateAllHosts()
|
||||
}
|
||||
|
||||
http.Redirect(w, r, r.Header.Get("Referer"), 302)
|
||||
}
|
||||
|
||||
func sortByField(method string, field string) {
|
||||
type fieldHost struct {
|
||||
Host models.Host
|
||||
F string
|
||||
}
|
||||
toSort := []fieldHost{}
|
||||
var oneSort fieldHost
|
||||
|
||||
for _, host := range AllHosts {
|
||||
oneSort.Host = host
|
||||
r := reflect.ValueOf(&host)
|
||||
f := reflect.Indirect(r).FieldByName(field)
|
||||
if field == "Known" {
|
||||
oneSort.F = strconv.FormatInt(f.Int(), 10)
|
||||
} else {
|
||||
oneSort.F = f.String()
|
||||
}
|
||||
toSort = append(toSort, oneSort)
|
||||
}
|
||||
|
||||
switch method {
|
||||
case "asc":
|
||||
sort.Slice(toSort, func(i, j int) bool {
|
||||
return toSort[i].F < toSort[j].F
|
||||
})
|
||||
case "desc":
|
||||
sort.Slice(toSort, func(i, j int) bool {
|
||||
return toSort[i].F > toSort[j].F
|
||||
})
|
||||
}
|
||||
|
||||
AllHosts = []models.Host{}
|
||||
for _, oneSort := range toSort {
|
||||
AllHosts = append(AllHosts, oneSort.Host)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
{{ define "auth" }}
|
||||
|
||||
<body>
|
||||
<div class="container-lg mt-5">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<table class="table">
|
||||
<form action="/auth_save/" method="post">
|
||||
<tr>
|
||||
<td>Enable</td>
|
||||
<td>
|
||||
<div class="form-check form-switch">
|
||||
{{ if .Auth.Auth }}
|
||||
<input class="form-check-input" type="checkbox" name="auth" checked>
|
||||
{{ else }}
|
||||
<input class="form-check-input" type="checkbox" name="auth">
|
||||
{{ end }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Expire after</td>
|
||||
<td><input name="expire" type="text" class="form-control" value="{{ .Auth.ExpStr }}"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Login</td>
|
||||
<td><input name="user" type="text" class="form-control" value="{{ .Auth.User }}"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>New password</td>
|
||||
<td><input name="password" type="password" class="form-control"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><button type="submit" class="btn btn-primary">Save</button></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</form>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="alert alert-info" role="alert">
|
||||
<p>● <b>WARNING!</b> Authentication is only safe when used over <b>https</b> and with a strong password.</p>
|
||||
<p>● <b>Expire after</b> - when user session expires. Can be set with suffixes <b>m</b>(minute), <b>h</b>(hour), <b>d</b>(day) or <b>M</b>(month). Default: <b>7d</b>.</p>
|
||||
<p>● <b>Login</b>. For Auth to work, login and password must not be empty.</p>
|
||||
<p>● <b>Password</b> is stored encrypted in config file. If you forgot your password, just set <b>auth_password:</b> field to empty there.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{{ template "footer" }}
|
||||
{{ end }}
|
||||
|
|
@ -1,36 +1,21 @@
|
|||
{{ define "config" }}
|
||||
{{ define "config.html" }}
|
||||
|
||||
<body>
|
||||
<div class="container-lg mt-5">
|
||||
<div class="container-lg">
|
||||
<div class="row">
|
||||
<div class="col-md-6 table-responsive">
|
||||
<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>Path to DB</td>
|
||||
<td><input name="dbpath" type="text" class="form-control" value="{{ .Config.DbPath }}"></td>
|
||||
</tr>
|
||||
<div class="col-md mt-4">
|
||||
<div class="card border-primary">
|
||||
<div class="card-header">Config</div>
|
||||
<div class="card-body table-responsive">
|
||||
<table class="table table-borderless">
|
||||
<form action="/config/" method="post">
|
||||
<tr>
|
||||
<td>Host</td>
|
||||
<td><input name="host" type="text" class="form-control" value="{{ .Config.GuiIP }}"></td>
|
||||
<td><input name="host" type="text" class="form-control" value="{{ .Config.Host }}"></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="number" class="form-control" value="{{ .Config.Timeout }}"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Shoutrrr URL</td>
|
||||
<td>
|
||||
<textarea name="shout" class="form-control" style="width: 100%;" rows="3" wrap="soft">{{ .Config.ShoutURL }}</textarea>
|
||||
</td>
|
||||
<td><input name="port" type="text" class="form-control" value="{{ .Config.Port }}"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Theme</td>
|
||||
|
|
@ -50,74 +35,95 @@
|
|||
</select></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Ignore IP</td>
|
||||
<td>
|
||||
<select name="ignoreip" class="form-select">
|
||||
<option selected value="{{ .Config.IgnoreIP }}">{{ .Config.IgnoreIP }}</option>
|
||||
<option value="yes">yes</option>
|
||||
<option value="no">no</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>Timeout (minutes)</td>
|
||||
<td><input name="timeout" type="number" class="form-control" value="{{ .Config.Timeout }}"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Log Level</td>
|
||||
<td>
|
||||
<select name="loglevel" class="form-select">
|
||||
<option selected value="{{ .Config.LogLevel }}">{{ .Config.LogLevel }}</option>
|
||||
<option value="short">short</option>
|
||||
<option value="verbose">verbose</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Keep history (days)</td>
|
||||
<td><input name="history" type="number" class="form-control" value="{{ .Config.HistDays }}"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Arp Timeout</td>
|
||||
<td>
|
||||
<select name="arp_timeout" class="form-select">
|
||||
<option selected value="{{ .Config.ArpTimeout }}">{{ .Config.ArpTimeout }}</option>
|
||||
<option value="100">100</option>
|
||||
<option value="250">250</option>
|
||||
<option value="500">500</option>
|
||||
<option value="1000">1000</option>
|
||||
<option value="1500">1500</option>
|
||||
<option value="2000">2000</option>
|
||||
<option value="3000">3000</option>
|
||||
<option value="5000">5000</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>Trim History</td>
|
||||
<td><input name="trim" type="number" class="form-control" value="{{ .Config.HistTrim }}"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><button type="submit" class="btn btn-primary">Save</button></td>
|
||||
</form>
|
||||
<td>
|
||||
<a href="/test_notify/"><button style="float: right;" class="btn btn-success">Test notification</button></a>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</form>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p>Current version: {{ .Version }}</p>
|
||||
<div class="alert alert-info" role="alert">
|
||||
<p>● There can be one or more <b>Interfaces</b>, space separated</p>
|
||||
<p>● After changing <b>Host</b>, <b>Port</b> or <b>Path to DB</b> you need to restart the app</p>
|
||||
<p>● <b>Timeout</b> means time between scans (in seconds)</p>
|
||||
<p>● <b>Shoutrrr URL</b> provides notifications to Discord, Email, Gotify, Telegram and other services. <a href="https://containrrr.dev/shoutrrr/v0.5/services/overview/" target="_blank">Link to documentation</a></p>
|
||||
<p>● If you want to detect unknown hosts by MAC only, set <b>Ignore IP</b> to "yes"</p>
|
||||
<p>● <b>Log Level</b> defines how much log output you want to see</p>
|
||||
<p>● The <b>Clear table</b> button below will delete all records from table. If you want to delete a single host, click on its MAC and press <b>Delete host</b> button</p>
|
||||
<p>● <b>Arp Timeout</b> per host timeout for <b>arp-scan</b> (in milliseconds). Default: 500</p>
|
||||
</div>
|
||||
<div class="card border-primary mt-4">
|
||||
<div class="card-header">Version</div>
|
||||
<div class="card-body">
|
||||
<a href="https://github.com/aceberg/WatchYourLAN/releases/tag/{{ .Version }}" target="_blank">{{ .Version }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md mt-4">
|
||||
<div class="card border-primary">
|
||||
<div class="card-header">InfluxDB2 config</div>
|
||||
<div class="card-body table-responsive">
|
||||
<table class="table table-borderless">
|
||||
<form action="/config_influx/" method="post">
|
||||
<tr>
|
||||
<td>Enable</td>
|
||||
<td>
|
||||
<div class="form-check form-switch">
|
||||
{{ if .Config.InfluxEnable }}
|
||||
<input class="form-check-input" type="checkbox" name="enable" checked>
|
||||
{{ else }}
|
||||
<input class="form-check-input" type="checkbox" name="enable">
|
||||
{{ end }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Address</td>
|
||||
<td><input name="addr" type="text" class="form-control" value="{{ .Config.InfluxAddr }}"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Token</td>
|
||||
<td><input name="token" type="text" class="form-control" value="{{ .Config.InfluxToken }}"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Org</td>
|
||||
<td><input name="org" type="text" class="form-control" value="{{ .Config.InfluxOrg }}"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Bucket</td>
|
||||
<td><input name="bucket" type="text" class="form-control" value="{{ .Config.InfluxBucket }}"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Skip TLS verify</td>
|
||||
<td>
|
||||
<div class="form-check form-switch">
|
||||
{{ if .Config.InfluxSkipTLS }}
|
||||
<input class="form-check-input" type="checkbox" name="skip" checked>
|
||||
{{ else }}
|
||||
<input class="form-check-input" type="checkbox" name="skip">
|
||||
{{ end }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><button type="submit" class="btn btn-primary">Save</button></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</form>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card border-primary mt-4 mb-4">
|
||||
<div class="card-header">About</div>
|
||||
<div class="card-body">
|
||||
<p>● After changing <b>Host</b> or <b>Port</b> the app must be restarted</p>
|
||||
<p>● <b>Timeout (minutes)</b> - how often watched ports are scanned</p>
|
||||
<p>● <b>Trim History</b> - how many port states are saved in memory and displayed on the <a href="/history/">History</a> page</p>
|
||||
<p>● If you find this app useful, please, <a href="https://github.com/aceberg#donate" target="_blank">donate</a></p>
|
||||
<p>● Commission you own app (Golang, HTML/JS). Contact and prices <a href="https://github.com/aceberg" target="_blank">here</a></p>
|
||||
</div>
|
||||
<br>
|
||||
<form action="/clear/" method="post">
|
||||
<button type="submit" class="btn btn-danger">Clear table</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{{ template "footer" }}
|
||||
{{ template "footer.html" }}
|
||||
{{ end }}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
{{ define "footer"}}
|
||||
{{ define "footer.html"}}
|
||||
</body>
|
||||
</html>
|
||||
{{ end }}
|
||||
|
|
@ -1,26 +1,32 @@
|
|||
{{ define "header"}}
|
||||
{{ define "header.html"}}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-bs-theme="{{ .Config.Color }}">
|
||||
<head>
|
||||
<title>Watch Your LAN</title>
|
||||
<title>WatchYourLAN</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!--Favicon-->
|
||||
<link href="data:image/x-icon;base64,{{ .Config.Icon }}" rel="icon" type="image/x-icon" />
|
||||
<link rel="icon" type="image/x-icon" href="/fs/public/favicon.png">
|
||||
{{ if eq .Config.NodePath "" }}
|
||||
<!-- Bootstrap and theme -->
|
||||
<link href="https://cdn.jsdelivr.net/gh/aceberg/aceberg-bootswatch-fork@v5/dist/{{ .Config.Theme }}/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
|
||||
<!-- Font for icons -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
|
||||
{{ else }}
|
||||
<link rel="stylesheet" href="{{ .Config.NodePath }}/node_modules/bootstrap-icons/font/bootstrap-icons.css">
|
||||
<link href="{{ .Config.NodePath }}/node_modules/bootswatch/dist/{{ .Config.Theme }}/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="{{ .Config.NodePath }}/node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<!-- Font for icons -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css">
|
||||
<!-- Bootstrap and theme -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/aceberg-bootswatch-fork@v5.3.3-2/dist/{{ .Config.Theme }}/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- JavaScript Bundle with Popper -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
{{ else }} <!-- Local node_modules -->
|
||||
<link rel="stylesheet" href="{{ .Config.NodePath }}/node_modules/bootstrap-icons/font/bootstrap-icons.css">
|
||||
<link href="{{ .Config.NodePath }}/node_modules/bootswatch/dist/{{ .Config.Theme }}/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="{{ .Config.NodePath }}/node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||
{{ end }}
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-md navbar-dark bg-primary">
|
||||
<div class="container-lg">
|
||||
<a class="navbar-brand" href="/">
|
||||
<img src="/fs/public/favicon.png" style="width: 2em"/>
|
||||
</a>
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<button class="btn navbar-toggler nav-link active fs-2" type="button" data-bs-toggle="collapse" data-bs-target="#navbarContent" aria-controls="navbarContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
|
|
@ -34,30 +40,12 @@
|
|||
<a class="nav-link active" href="/">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="/line?state=on">Online</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="/line?state=off">Offline</a>
|
||||
<a class="nav-link active" href="/config/">Config</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="/history/">History</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="/config/">Config</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="/auth_conf/">Auth</a>
|
||||
</li>
|
||||
{{ if .Config.Auth }}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="/login/?logout=yes">Log Out</a>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
<form action="/search_hosts/" method="post" class="d-flex mb-2 mb-md-0">
|
||||
<input name="search" type="text" class="form-control text-dark me-2" placeholder="Search">
|
||||
<button type="submit" class="btn btn-primary">Search</button>
|
||||
</form>
|
||||
</div>
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
{{ define "history"}}
|
||||
<!--Auto refresh (seconds)-->
|
||||
<meta http-equiv="refresh" content="{{ .Config.Timeout }}">
|
||||
<body>
|
||||
<div class="container-lg mt-3 table-responsive">
|
||||
<table class="table table-striped">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>IP</th>
|
||||
<th>Mac</th>
|
||||
<th>Hardware</th>
|
||||
<th>Time</th>
|
||||
<th>Known</th>
|
||||
<th>State</th>
|
||||
</tr>
|
||||
{{ range .Hist }}
|
||||
<tr>
|
||||
<td class="align-middle">{{ .Name }}</td>
|
||||
<td class="align-middle"><a href="http://{{ .IP }}" target="_blank">{{ .IP }}</a></td>
|
||||
<td class="align-middle"><a href="/host?id={{ .Host }}">{{ .Mac }}</a></td>
|
||||
<td class="align-middle">{{ .Hw }}</td>
|
||||
<td class="align-middle">{{ .Date }}</td>
|
||||
<td class="align-middle">
|
||||
{{ if eq .Known 1 }}
|
||||
Yes
|
||||
{{ else }}
|
||||
No
|
||||
{{ end }}
|
||||
</td>
|
||||
<td class="align-middle">
|
||||
{{ if eq .State 1 }}
|
||||
<i class="bi bi-circle-fill text-success"></i>
|
||||
{{ else }}
|
||||
<i class="bi bi-circle-fill text-danger"></i>
|
||||
{{ end }}
|
||||
</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{{ template "footer" }}
|
||||
{{ end }}
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
{{ define "host" }}
|
||||
|
||||
<body>
|
||||
<div class="container-lg mt-5">
|
||||
<div class="row">
|
||||
<div class="col-md table-responsive">
|
||||
<table class="table">
|
||||
{{ range .Hosts }}
|
||||
<tr>
|
||||
<td class="align-middle">ID</td>
|
||||
<td class="align-middle">{{ .ID }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="align-middle">Name</td>
|
||||
<td class="align-middle">{{ .Name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="align-middle">IP</td>
|
||||
<td class="align-middle"><a href="http://{{ .IP }}" target="_blank">{{ .IP }}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="align-middle">Mac</td>
|
||||
<td class="align-middle">{{ .Mac }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="align-middle">Hw</td>
|
||||
<td class="align-middle">{{ .Hw }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="align-middle">Date</td>
|
||||
<td class="align-middle">{{ .Date }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="align-middle">Known</td>
|
||||
<td class="align-middle">
|
||||
{{ if eq .Known 1 }}
|
||||
Yes
|
||||
{{ else }}
|
||||
No
|
||||
{{ end }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="align-middle">Now</td>
|
||||
<td class="align-middle">
|
||||
{{ if eq .Now 1 }}
|
||||
Online
|
||||
{{ else }}
|
||||
Offline
|
||||
{{ end }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="align-middle">
|
||||
<form action="/del_host/" method="post">
|
||||
<input name="id" type="hidden" class="form-control" value="{{ .ID }}"><button type="submit" class="btn btn-danger">Delete host</button>
|
||||
</form>
|
||||
</td>
|
||||
<td class="align-middle"></td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md table-responsive">
|
||||
<table class="table">
|
||||
<tr>
|
||||
<td class="align-middle">Domain name</td>
|
||||
<td class="align-middle">
|
||||
{{ range .Themes }}
|
||||
<a href="http://{{ . }}" target="_blank">{{ . }}</a>
|
||||
{{ end }}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br>
|
||||
<br>
|
||||
{{ range .Hosts }}
|
||||
{{ if eq .Now 1 }}
|
||||
<p>Scan for open TCP ports (may take some time)</p>
|
||||
<table class="table">
|
||||
<form action="/port_scan/" method="post">
|
||||
<input name="ip" type="hidden" class="form-control" value="{{ .IP }}">
|
||||
<tr>
|
||||
<td class="align-middle">Start port</td>
|
||||
<td class="align-middle"><input name="begin" type="text" class="form-control" placeholder="1"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="align-middle">End port</td>
|
||||
<td class="align-middle"><input name="end" type="text" class="form-control" placeholder="65535"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="align-middle"><button type="submit" class="btn btn-success">Scan host</button></td>
|
||||
<td class="align-middle"></td>
|
||||
</tr>
|
||||
</form>
|
||||
</table>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row table-responsive">
|
||||
<table class="table table-striped">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>IP</th>
|
||||
<th>Time</th>
|
||||
<th>Known</th>
|
||||
<th>State</th>
|
||||
</tr>
|
||||
{{ range .Hist }}
|
||||
<tr>
|
||||
<td class="align-middle">{{ .Name }}</td>
|
||||
<td class="align-middle"><a href="http://{{ .IP }}" target="_blank">{{ .IP }}</a></td>
|
||||
<td class="align-middle">{{ .Date }}</td>
|
||||
<td class="align-middle">
|
||||
{{ if eq .Known 1 }}
|
||||
Yes
|
||||
{{ else }}
|
||||
No
|
||||
{{ end }}
|
||||
</td>
|
||||
<td class="align-middle">
|
||||
{{ if eq .State 1 }}
|
||||
<i class="bi bi-circle-fill text-success"></i>
|
||||
{{ else }}
|
||||
<i class="bi bi-circle-fill text-danger"></i>
|
||||
{{ end }}
|
||||
</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{{ template "footer" }}
|
||||
{{ end }}
|
||||
|
|
@ -1,78 +1,40 @@
|
|||
{{ define "index"}}
|
||||
<!--Auto refresh (seconds)-->
|
||||
<meta http-equiv="refresh" content="{{ .Config.Timeout }}">
|
||||
{{ define "index.html" }}
|
||||
</head>
|
||||
<script src="/fs/public/js/index.js"></script>
|
||||
<script src="/fs/public/js/sort.js"></script>
|
||||
<body>
|
||||
<div class="container-lg mt-3">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h2>Online</h2>
|
||||
<p>Scanning interfaces: {{ .Config.Iface }}</p>
|
||||
<div class="table-responsive">
|
||||
<div class="container-lg">
|
||||
<div class="row">
|
||||
<div class="col-md mt-4">
|
||||
<form action="/addr_add/" method="post" class="input-group">
|
||||
<input name="name" type="text" class="form-control" placeholder="Name">
|
||||
<input name="addr" type="text" class="form-control" placeholder="Address">
|
||||
<button type="submit" class="btn btn-primary">Add</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md mt-4 mb-4">
|
||||
<div class="card border-primary">
|
||||
<div class="card-body table-responsive">
|
||||
<table class="table table-striped">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>IP</th>
|
||||
<th>Mac</th>
|
||||
<th>Hardware</th>
|
||||
<th>Known</th>
|
||||
</tr>
|
||||
{{ range .Hosts }}
|
||||
{{ if eq .Now 1 }}
|
||||
<tr>
|
||||
<td class="align-middle">{{ .Name }}</td>
|
||||
<td class="align-middle"><a href="http://{{ .IP }}" target="_blank">{{ .IP }}</a></td>
|
||||
<td class="align-middle"><a href="/host?id={{ .ID }}">{{ .Mac }}</a></td>
|
||||
<td class="align-middle">{{ .Hw }}</td>
|
||||
<td class="align-middle">
|
||||
{{ if eq .Known 1 }}
|
||||
<button type="button" class="btn btn-success" disabled>Yes</button>
|
||||
{{ else }}
|
||||
<button type="button" class="btn btn-warning" disabled>No</button>
|
||||
{{ end }}
|
||||
</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</table>
|
||||
</div>
|
||||
<h2>Log</h2>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>IP</th>
|
||||
<th>Mac</th>
|
||||
<th>Hardware</th>
|
||||
<th>Last seen</th>
|
||||
<th>Known</th>
|
||||
</tr>
|
||||
{{ range .Hosts }}
|
||||
<form action="/update_host/" method="post">
|
||||
<input name="id" type="hidden" class="form-control" value="{{ .ID }}">
|
||||
<tr>
|
||||
<td class="align-middle">
|
||||
<input name="name" type="text" class="form-control text-dark" value="{{ .Name }}">
|
||||
</td>
|
||||
<td class="align-middle"><a href="http://{{ .IP }}" target="_blank">{{ .IP }}</a></td>
|
||||
<td class="align-middle"><a href="/host?id={{ .ID }}">{{ .Mac }}</a></td>
|
||||
<td class="align-middle">{{ .Hw }}</td>
|
||||
<td class="align-middle">{{ .Date }}</td>
|
||||
<td class="align-middle">
|
||||
{{ if eq .Known 1 }}
|
||||
<button value="off" name="known" type="submit" class="btn btn-success">Yes</button>
|
||||
{{ else }}
|
||||
<button value="on" name="known" type="submit" class="btn btn-warning">No</button>
|
||||
{{ end }}
|
||||
</td>
|
||||
</tr>
|
||||
</form>
|
||||
{{ end }}
|
||||
<thead>
|
||||
<th style="width: 3em;"></th>
|
||||
<th>Name <i class="bi bi-sort-down-alt" onclick="sortBy('Name')"></i></th>
|
||||
<th>Addr <i class="bi bi-sort-down-alt" onclick="sortBy('Addr')"></i></th>
|
||||
<th>Total <i class="bi bi-sort-down-alt" onclick="sortBy('Total')"></i></th>
|
||||
<th>Watching <i class="bi bi-sort-down-alt" onclick="sortBy('Watching')"></i></th>
|
||||
<th>Online <i class="bi bi-sort-down-alt" onclick="sortBy('Online')"></i></th>
|
||||
<th>Offline <i class="bi bi-sort-down-alt" onclick="sortBy('Offline')"></i></th>
|
||||
</thead>
|
||||
<tbody id="tBody"></tbody>
|
||||
<!-- index.js -->
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ template "footer" }}
|
||||
{{ template "footer.html" }}
|
||||
{{ end }}
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
{{ define "line"}}
|
||||
<body>
|
||||
<div class="container-lg mt-3 table-responsive">
|
||||
<table class="table table-striped">
|
||||
<tr>
|
||||
<form action="/sort_hosts/" method="post">
|
||||
<th>Name<br>
|
||||
<button type="submit" name="sort_method" value="name-up" class="btn btn-primary btn-sm">
|
||||
<i class="bi bi-caret-up"></i>
|
||||
</button>
|
||||
<button type="submit" name="sort_method" value="name-down" class="btn btn-primary btn-sm">
|
||||
<i class="bi bi-caret-down"></i>
|
||||
</button>
|
||||
</th>
|
||||
<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>
|
||||
<button type="submit" name="sort_method" value="ip-down" class="btn btn-primary btn-sm">
|
||||
<i class="bi bi-caret-down"></i>
|
||||
</button>
|
||||
</th>
|
||||
<th>Mac<br>
|
||||
<button type="submit" name="sort_method" value="mac-up" class="btn btn-primary btn-sm">
|
||||
<i class="bi bi-caret-up"></i>
|
||||
</button>
|
||||
<button type="submit" name="sort_method" value="mac-down" class="btn btn-primary btn-sm">
|
||||
<i class="bi bi-caret-down"></i>
|
||||
</button>
|
||||
|
||||
</th>
|
||||
<th>Hardware<br>
|
||||
<button type="submit" name="sort_method" value="hw-up" class="btn btn-primary btn-sm">
|
||||
<i class="bi bi-caret-up"></i>
|
||||
</button>
|
||||
<button type="submit" name="sort_method" value="hw-down" class="btn btn-primary btn-sm">
|
||||
<i class="bi bi-caret-down"></i>
|
||||
</button>
|
||||
</th>
|
||||
<th>Last seen<br>
|
||||
<button type="submit" name="sort_method" value="date-up" class="btn btn-primary btn-sm">
|
||||
<i class="bi bi-caret-up"></i>
|
||||
</button>
|
||||
<button type="submit" name="sort_method" value="date-down" class="btn btn-primary btn-sm">
|
||||
<i class="bi bi-caret-down"></i>
|
||||
</button>
|
||||
</th>
|
||||
<th>Known<br>
|
||||
<button type="submit" name="sort_method" value="known-up" class="btn btn-primary btn-sm">
|
||||
<i class="bi bi-caret-up"></i>
|
||||
</button>
|
||||
<button type="submit" name="sort_method" value="known-down" class="btn btn-primary btn-sm">
|
||||
<i class="bi bi-caret-down"></i>
|
||||
</button>
|
||||
</th>
|
||||
</form>
|
||||
</tr>
|
||||
{{ range .Hosts }}
|
||||
<form action="/update_host/" method="post">
|
||||
<input name="id" type="hidden" class="form-control" value="{{ .ID }}">
|
||||
<tr>
|
||||
<td class="align-middle">
|
||||
<input name="name" type="text" class="form-control text-dark" value="{{ .Name }}">
|
||||
</td>
|
||||
<td class="align-middle"><a href="http://{{ .IP }}" target="_blank">{{ .IP }}</a></td>
|
||||
<td class="align-middle"><a href="/host?id={{ .ID }}">{{ .Mac }}</a></td>
|
||||
<td class="align-middle">{{ .Hw }}</td>
|
||||
<td class="align-middle">{{ .Date }}</td>
|
||||
<td class="align-middle">
|
||||
{{ if eq .Known 1 }}
|
||||
<button value="off" name="known" type="submit" class="btn btn-success">Yes</button>
|
||||
{{ else }}
|
||||
<button value="on" name="known" type="submit" class="btn btn-warning">No</button>
|
||||
{{ end }}
|
||||
</td>
|
||||
</tr>
|
||||
</form>
|
||||
{{ end }}
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{{ template "footer" }}
|
||||
{{ end }}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
{{ define "login" }}
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-lg">
|
||||
<br>
|
||||
<br>
|
||||
<div class="row text-center">
|
||||
<div class="d-flex align-items-center justify-content-center">
|
||||
<form action="/login/" method="post">
|
||||
<input name="username" type="text" placeholder="Login" class="form-control p-2 m-2">
|
||||
<input name="password" type="password" placeholder="Password" class="form-control p-2 m-2">
|
||||
<button type="submit" class="btn btn-primary p-2 m-2">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ template "footer" }}
|
||||
{{ end }}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
{{ define "port" }}
|
||||
|
||||
<body>
|
||||
<div class="container-lg mt-5">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<p>Open TCP ports:</p>
|
||||
{{ range .Themes }}
|
||||
<a href="http://{{ . }}" target="_blank">{{ . }}</a>
|
||||
<br>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{{ template "footer" }}
|
||||
{{ end }}
|
||||
|
|
@ -1 +0,0 @@
|
|||
VERSION=1.0.6
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/db"
|
||||
)
|
||||
|
||||
func trimHistoryRoutine() {
|
||||
|
||||
for {
|
||||
trimHistory()
|
||||
|
||||
time.Sleep(time.Duration(1) * time.Hour) // Every hour
|
||||
}
|
||||
}
|
||||
|
||||
func trimHistory() {
|
||||
|
||||
days, _ := strconv.Atoi(AppConfig.HistDays)
|
||||
|
||||
nowStr := time.Now().Format("2006-01-02 15:04:05") // This needed so all time is
|
||||
now, _ := time.Parse("2006-01-02 15:04:05", nowStr) // in one format
|
||||
nowMinus := now.Add(-time.Duration(days) * 24 * time.Hour)
|
||||
|
||||
history := db.SelectHist(AppConfig.DbPath)
|
||||
|
||||
if AppConfig.LogLevel != "short" {
|
||||
log.Println("INFO: removing all history before", nowMinus.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
|
||||
for _, hist := range history {
|
||||
date, _ := time.Parse("2006-01-02 15:04:05", hist.Date)
|
||||
|
||||
if date.Before(nowMinus) {
|
||||
|
||||
db.DeleteHist(AppConfig.DbPath, hist.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/db"
|
||||
)
|
||||
|
||||
func updateHandler(w http.ResponseWriter, r *http.Request) {
|
||||
idStr := r.FormValue("id")
|
||||
name := r.FormValue("name")
|
||||
knownStr := r.FormValue("known")
|
||||
|
||||
if idStr == "" {
|
||||
fmt.Fprintf(w, "No data!")
|
||||
} else {
|
||||
var known int
|
||||
id, _ := strconv.Atoi(idStr)
|
||||
known = 0
|
||||
if knownStr == "on" {
|
||||
known = 1
|
||||
}
|
||||
|
||||
for i, oneHost := range AllHosts {
|
||||
if oneHost.ID == id {
|
||||
AllHosts[i].Name = name
|
||||
AllHosts[i].Known = known
|
||||
db.Update(AppConfig.DbPath, AllHosts[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
http.Redirect(w, r, r.Header.Get("Referer"), 302)
|
||||
}
|
||||
|
|
@ -1,58 +1,49 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"log"
|
||||
"html/template"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/auth"
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/aceberg/WatchYourLAN/internal/check"
|
||||
"github.com/aceberg/WatchYourLAN/internal/conf"
|
||||
"github.com/aceberg/WatchYourLAN/internal/db"
|
||||
"github.com/aceberg/WatchYourLAN/internal/migrate"
|
||||
"github.com/aceberg/WatchYourLAN/internal/scan"
|
||||
)
|
||||
|
||||
// Gui - start web GUI
|
||||
func Gui(configPath, nodePath string) {
|
||||
// Gui - start web server
|
||||
func Gui(dirPath, nodePath string) {
|
||||
|
||||
ConfigPath = migrate.ToYAML(configPath)
|
||||
AppConfig, authConf = conf.Get(ConfigPath)
|
||||
AppConfig.NodePath = nodePath
|
||||
AppConfig.Icon = Icon
|
||||
confPath := dirPath + "/config.yaml"
|
||||
check.Path(confPath)
|
||||
|
||||
address := AppConfig.GuiIP + ":" + AppConfig.GuiPort
|
||||
appConfig = conf.Get(confPath)
|
||||
|
||||
QuitScan = make(chan bool)
|
||||
go scan.Start(AppConfig, QuitScan)
|
||||
go trimHistoryRoutine() // trim-history.go
|
||||
appConfig.DirPath = dirPath
|
||||
appConfig.ConfPath = confPath
|
||||
appConfig.NodePath = nodePath
|
||||
|
||||
updateAllHosts() // webgui.go
|
||||
slog.Info("config", "path", appConfig.DirPath)
|
||||
|
||||
log.Println("=================================== ")
|
||||
log.Printf("Web GUI at http://%s", address)
|
||||
log.Println("=================================== ")
|
||||
address := appConfig.Host + ":" + appConfig.Port
|
||||
|
||||
http.HandleFunc("/login/", loginHandler) // login.go
|
||||
slog.Info("=================================== ")
|
||||
slog.Info("Web GUI at http://" + address)
|
||||
slog.Info("=================================== ")
|
||||
|
||||
http.HandleFunc("/", auth.Auth(indexHandler, &authConf))
|
||||
http.HandleFunc("/auth_conf/", auth.Auth(authConfHandler, &authConf)) // auth.go
|
||||
http.HandleFunc("/auth_save/", auth.Auth(saveAuthHandler, &authConf)) // auth.go
|
||||
http.HandleFunc("/clear/", auth.Auth(clearHandler, &authConf)) // config.go
|
||||
http.HandleFunc("/config/", auth.Auth(configHandler, &authConf)) // config.go
|
||||
http.HandleFunc("/del_host/", auth.Auth(delHandler, &authConf)) // host.go
|
||||
http.HandleFunc("/history/", auth.Auth(historyHandler, &authConf)) // history.go
|
||||
http.HandleFunc("/host/", auth.Auth(hostHandler, &authConf)) // host.go
|
||||
http.HandleFunc("/line/", auth.Auth(lineHandler, &authConf)) // line.go
|
||||
http.HandleFunc("/port_scan/", auth.Auth(portHandler, &authConf)) // port.go
|
||||
http.HandleFunc("/save_config/", auth.Auth(saveConfigHandler, &authConf)) // config.go
|
||||
http.HandleFunc("/search_hosts/", auth.Auth(searchHandler, &authConf)) // search.go
|
||||
http.HandleFunc("/sort_hosts/", auth.Auth(sortHandler, &authConf)) // sort.go
|
||||
http.HandleFunc("/test_notify/", auth.Auth(testNotifyHandler, &authConf)) // config.go
|
||||
http.HandleFunc("/update_host/", auth.Auth(updateHandler, &authConf)) // update.go
|
||||
err := http.ListenAndServe(address, nil)
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
router := gin.Default()
|
||||
|
||||
templ := template.Must(template.New("").ParseFS(templFS, "templates/*"))
|
||||
router.SetHTMLTemplate(templ) // templates
|
||||
|
||||
router.StaticFS("/fs/", http.FS(pubFS)) // public
|
||||
|
||||
router.GET("/", indexHandler) // index.go
|
||||
router.GET("/config/", configHandler) // config.go
|
||||
|
||||
router.POST("/config/", saveConfigHandler) // config.go
|
||||
|
||||
err := router.Run(address)
|
||||
check.IfError(err)
|
||||
}
|
||||
|
||||
func updateAllHosts() {
|
||||
AllHosts = db.Select(AppConfig.DbPath)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue