From 0c410d9458380e3ac4663361632a8048bb905e1c Mon Sep 17 00:00:00 2001 From: aceberg <1502200+aceberg@users.noreply.github.com> Date: Sun, 30 Jun 2024 01:17:35 +0700 Subject: [PATCH] Basic gin site --- LICENSE.md => LICENSE | 0 cmd/WatchYourLAN/main.go | 11 +- go.mod | 55 ++++----- go.sum | 160 ++++++++++++-------------- internal/auth/auth.go | 35 ------ internal/auth/cookie.go | 22 ---- internal/auth/hash.go | 27 ----- internal/auth/models-vars.go | 18 --- internal/auth/session.go | 33 ------ internal/auth/timeparse.go | 46 -------- internal/check/error.go | 5 +- internal/conf/config.go | 88 -------------- internal/conf/getconfig.go | 46 ++++++++ internal/db/edit.go | 73 ------------ internal/db/hist-edit.go | 31 ----- internal/db/quote_str.go | 7 -- internal/db/sqlite.go | 62 ---------- internal/migrate/get-old.go | 44 ------- internal/migrate/migrate.go | 52 --------- internal/models/models.go | 27 ++--- internal/notify/shoutrrr.go | 16 --- internal/port/scan.go | 42 ------- internal/scan/arpscan.go | 59 ---------- internal/scan/compare.go | 79 ------------- internal/scan/start.go | 51 --------- internal/web/auth.go | 52 --------- internal/web/config.go | 84 +++++--------- internal/web/const-icon.go | 4 - internal/web/const-var.go | 32 ++---- internal/web/exec-tpl.go | 19 ---- internal/web/history.go | 17 --- internal/web/host.go | 60 ---------- internal/web/index.go | 13 +-- internal/web/line.go | 27 ----- internal/web/login.go | 31 ----- internal/web/port.go | 30 ----- internal/web/public/css/index.css | 9 ++ internal/web/public/favicon.png | Bin 0 -> 13194 bytes internal/web/public/js/history.js | 45 ++++++++ internal/web/public/js/index.js | 46 ++++++++ internal/web/public/js/scan.js | 125 ++++++++++++++++++++ internal/web/public/js/sort.js | 38 +++++++ internal/web/public/version | 1 + internal/web/search.go | 35 ------ internal/web/sort.go | 116 ------------------- internal/web/templates/auth.html | 53 --------- internal/web/templates/config.html | 170 ++++++++++++++-------------- internal/web/templates/footer.html | 4 +- internal/web/templates/header.html | 52 ++++----- internal/web/templates/history.html | 43 ------- internal/web/templates/host.html | 137 ---------------------- internal/web/templates/index.html | 100 +++++----------- internal/web/templates/line.html | 83 -------------- internal/web/templates/login.html | 19 ---- internal/web/templates/port.html | 18 --- internal/web/templates/version | 1 - internal/web/trim-history.go | 42 ------- internal/web/update.go | 36 ------ internal/web/webgui.go | 71 +++++------- 59 files changed, 640 insertions(+), 2062 deletions(-) rename LICENSE.md => LICENSE (100%) delete mode 100644 internal/auth/auth.go delete mode 100644 internal/auth/cookie.go delete mode 100644 internal/auth/hash.go delete mode 100644 internal/auth/models-vars.go delete mode 100644 internal/auth/session.go delete mode 100644 internal/auth/timeparse.go delete mode 100644 internal/conf/config.go create mode 100644 internal/conf/getconfig.go delete mode 100644 internal/db/edit.go delete mode 100644 internal/db/hist-edit.go delete mode 100644 internal/db/quote_str.go delete mode 100644 internal/db/sqlite.go delete mode 100644 internal/migrate/get-old.go delete mode 100644 internal/migrate/migrate.go delete mode 100644 internal/notify/shoutrrr.go delete mode 100644 internal/port/scan.go delete mode 100644 internal/scan/arpscan.go delete mode 100644 internal/scan/compare.go delete mode 100644 internal/scan/start.go delete mode 100644 internal/web/auth.go delete mode 100644 internal/web/const-icon.go delete mode 100644 internal/web/exec-tpl.go delete mode 100644 internal/web/history.go delete mode 100644 internal/web/host.go delete mode 100644 internal/web/line.go delete mode 100644 internal/web/login.go delete mode 100644 internal/web/port.go create mode 100644 internal/web/public/css/index.css create mode 100644 internal/web/public/favicon.png create mode 100644 internal/web/public/js/history.js create mode 100644 internal/web/public/js/index.js create mode 100644 internal/web/public/js/scan.js create mode 100644 internal/web/public/js/sort.js create mode 100644 internal/web/public/version delete mode 100644 internal/web/search.go delete mode 100644 internal/web/sort.go delete mode 100644 internal/web/templates/auth.html delete mode 100644 internal/web/templates/history.html delete mode 100644 internal/web/templates/host.html delete mode 100644 internal/web/templates/line.html delete mode 100644 internal/web/templates/login.html delete mode 100644 internal/web/templates/port.html delete mode 100644 internal/web/templates/version delete mode 100644 internal/web/trim-history.go delete mode 100644 internal/web/update.go diff --git a/LICENSE.md b/LICENSE similarity index 100% rename from LICENSE.md rename to LICENSE diff --git a/cmd/WatchYourLAN/main.go b/cmd/WatchYourLAN/main.go index aeaf5c8..96de135 100644 --- a/cmd/WatchYourLAN/main.go +++ b/cmd/WatchYourLAN/main.go @@ -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 } diff --git a/go.mod b/go.mod index 067384d..02e048f 100644 --- a/go.mod +++ b/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 ) diff --git a/go.sum b/go.sum index 516f0b7..2614f17 100644 --- a/go.sum +++ b/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= diff --git a/internal/auth/auth.go b/internal/auth/auth.go deleted file mode 100644 index e213246..0000000 --- a/internal/auth/auth.go +++ /dev/null @@ -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) - } -} diff --git a/internal/auth/cookie.go b/internal/auth/cookie.go deleted file mode 100644 index 90c2b8d..0000000 --- a/internal/auth/cookie.go +++ /dev/null @@ -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 -} diff --git a/internal/auth/hash.go b/internal/auth/hash.go deleted file mode 100644 index 3ae9368..0000000 --- a/internal/auth/hash.go +++ /dev/null @@ -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 -} diff --git a/internal/auth/models-vars.go b/internal/auth/models-vars.go deleted file mode 100644 index 1df8033..0000000 --- a/internal/auth/models-vars.go +++ /dev/null @@ -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" diff --git a/internal/auth/session.go b/internal/auth/session.go deleted file mode 100644 index ea7794f..0000000 --- a/internal/auth/session.go +++ /dev/null @@ -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) -} diff --git a/internal/auth/timeparse.go b/internal/auth/timeparse.go deleted file mode 100644 index a6dd940..0000000 --- a/internal/auth/timeparse.go +++ /dev/null @@ -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 -} diff --git a/internal/check/error.go b/internal/check/error.go index ea7c3fe..c6c0208 100644 --- a/internal/check/error.go +++ b/internal/check/error.go @@ -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 } diff --git a/internal/conf/config.go b/internal/conf/config.go deleted file mode 100644 index 8bafd2d..0000000 --- a/internal/conf/config.go +++ /dev/null @@ -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) -} diff --git a/internal/conf/getconfig.go b/internal/conf/getconfig.go new file mode 100644 index 0000000..462478b --- /dev/null +++ b/internal/conf/getconfig.go @@ -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) +} diff --git a/internal/db/edit.go b/internal/db/edit.go deleted file mode 100644 index af68063..0000000 --- a/internal/db/edit.go +++ /dev/null @@ -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) -} diff --git a/internal/db/hist-edit.go b/internal/db/hist-edit.go deleted file mode 100644 index 7b75d4a..0000000 --- a/internal/db/hist-edit.go +++ /dev/null @@ -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) -} diff --git a/internal/db/quote_str.go b/internal/db/quote_str.go deleted file mode 100644 index aa2d36c..0000000 --- a/internal/db/quote_str.go +++ /dev/null @@ -1,7 +0,0 @@ -package db - -import "strings" - -func quoteStr(str string) string { - return strings.ReplaceAll(str, "'", "''") -} diff --git a/internal/db/sqlite.go b/internal/db/sqlite.go deleted file mode 100644 index ef07bdf..0000000 --- a/internal/db/sqlite.go +++ /dev/null @@ -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 -} diff --git a/internal/migrate/get-old.go b/internal/migrate/get-old.go deleted file mode 100644 index 8f70957..0000000 --- a/internal/migrate/get-old.go +++ /dev/null @@ -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 -} diff --git a/internal/migrate/migrate.go b/internal/migrate/migrate.go deleted file mode 100644 index e65d6ac..0000000 --- a/internal/migrate/migrate.go +++ /dev/null @@ -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) -} diff --git a/internal/models/models.go b/internal/models/models.go index c9c602f..f59dcb7 100644 --- a/internal/models/models.go +++ b/internal/models/models.go @@ -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 } diff --git a/internal/notify/shoutrrr.go b/internal/notify/shoutrrr.go deleted file mode 100644 index 38bbf75..0000000 --- a/internal/notify/shoutrrr.go +++ /dev/null @@ -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) - } - } -} diff --git a/internal/port/scan.go b/internal/port/scan.go deleted file mode 100644 index 38a2b31..0000000 --- a/internal/port/scan.go +++ /dev/null @@ -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 -} diff --git a/internal/scan/arpscan.go b/internal/scan/arpscan.go deleted file mode 100644 index 09c9fab..0000000 --- a/internal/scan/arpscan.go +++ /dev/null @@ -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 -} diff --git a/internal/scan/compare.go b/internal/scan/compare.go deleted file mode 100644 index 1b126fb..0000000 --- a/internal/scan/compare.go +++ /dev/null @@ -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) -} diff --git a/internal/scan/start.go b/internal/scan/start.go deleted file mode 100644 index 770df5f..0000000 --- a/internal/scan/start.go +++ /dev/null @@ -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 - } -} diff --git a/internal/web/auth.go b/internal/web/auth.go deleted file mode 100644 index 27452dc..0000000 --- a/internal/web/auth.go +++ /dev/null @@ -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) -} diff --git a/internal/web/config.go b/internal/web/config.go index 2cbcb5f..cb47e05 100644 --- a/internal/web/config.go +++ b/internal/web/config.go @@ -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") } diff --git a/internal/web/const-icon.go b/internal/web/const-icon.go deleted file mode 100644 index abd236f..0000000 --- a/internal/web/const-icon.go +++ /dev/null @@ -1,4 +0,0 @@ -package web - -// Icon - favicon -const Icon = "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" diff --git a/internal/web/const-var.go b/internal/web/const-var.go index fd3fbc0..2c3c8e5 100644 --- a/internal/web/const-var.go +++ b/internal/web/const-var.go @@ -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 diff --git a/internal/web/exec-tpl.go b/internal/web/exec-tpl.go deleted file mode 100644 index 52dea0a..0000000 --- a/internal/web/exec-tpl.go +++ /dev/null @@ -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) -} diff --git a/internal/web/history.go b/internal/web/history.go deleted file mode 100644 index b4c0929..0000000 --- a/internal/web/history.go +++ /dev/null @@ -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) -} diff --git a/internal/web/host.go b/internal/web/host.go deleted file mode 100644 index ea2e710..0000000 --- a/internal/web/host.go +++ /dev/null @@ -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) -} diff --git a/internal/web/index.go b/internal/web/index.go index 33bec38..b3f0424 100644 --- a/internal/web/index.go +++ b/internal/web/index.go @@ -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) } diff --git a/internal/web/line.go b/internal/web/line.go deleted file mode 100644 index 2cce657..0000000 --- a/internal/web/line.go +++ /dev/null @@ -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) -} diff --git a/internal/web/login.go b/internal/web/login.go deleted file mode 100644 index 94b14ec..0000000 --- a/internal/web/login.go +++ /dev/null @@ -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) -} diff --git a/internal/web/port.go b/internal/web/port.go deleted file mode 100644 index 9ffbd97..0000000 --- a/internal/web/port.go +++ /dev/null @@ -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) -} diff --git a/internal/web/public/css/index.css b/internal/web/public/css/index.css new file mode 100644 index 0000000..27bc4cf --- /dev/null +++ b/internal/web/public/css/index.css @@ -0,0 +1,9 @@ +.my-button { + height: 100%; + background-color: #00000000; + color: var(--bs-primary); + text-align: center; +} +.my-button:hover { + background-color: #0000001a; +} \ No newline at end of file diff --git a/internal/web/public/favicon.png b/internal/web/public/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..16dd7e7a14c9566264838fb8a9885fdc611cc52c GIT binary patch literal 13194 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 + } + + let html = ` + + ${i}. + ${hist.Name} + ${hist.Addr} + ${hist.Port} + ${hist.PortName} + ${allState} + `; + + 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); +} \ No newline at end of file diff --git a/internal/web/public/js/index.js b/internal/web/public/js/index.js new file mode 100644 index 0000000..34e29ed --- /dev/null +++ b/internal/web/public/js/index.js @@ -0,0 +1,46 @@ +var addrsArray = {}; + +loadAddrs(); + +function createHTML(addr, i) { + let off = ''; + + if (addr.Offline == 0) { + off = `0`; + } else { + off = `${addr.Offline}`; + } + + let html = ` + + ${i}. + + ${addr.Name} + + + ${addr.Addr} + + ${addr.Total} + ${addr.Watching} + ${addr.Online} + ${off} + + `; + + 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); +} \ No newline at end of file diff --git a/internal/web/public/js/scan.js b/internal/web/public/js/scan.js new file mode 100644 index 0000000..4c62cbb --- /dev/null +++ b/internal/web/public/js/scan.js @@ -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 = ` new`; + } + + if (port.Watch) { + checked = `checked`; + + if (port.State) { + state = ``; + } else { + state = ``; + } + } else { + state = ``; + } + + let html = ` + + ${i}. + + + + + ${port.Port}${sup} + + + + + ${state} + + +
+ +
+ + + + + + + `; + + 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 +} \ No newline at end of file diff --git a/internal/web/public/js/sort.js b/internal/web/public/js/sort.js new file mode 100644 index 0000000..1f06271 --- /dev/null +++ b/internal/web/public/js/sort.js @@ -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; +} \ No newline at end of file diff --git a/internal/web/public/version b/internal/web/public/version new file mode 100644 index 0000000..cd3d0a4 --- /dev/null +++ b/internal/web/public/version @@ -0,0 +1 @@ +VERSION=0.1.2 \ No newline at end of file diff --git a/internal/web/search.go b/internal/web/search.go deleted file mode 100644 index 42caed5..0000000 --- a/internal/web/search.go +++ /dev/null @@ -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), - ) -} diff --git a/internal/web/sort.go b/internal/web/sort.go deleted file mode 100644 index 04ac5d3..0000000 --- a/internal/web/sort.go +++ /dev/null @@ -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) - } -} diff --git a/internal/web/templates/auth.html b/internal/web/templates/auth.html deleted file mode 100644 index 2a32429..0000000 --- a/internal/web/templates/auth.html +++ /dev/null @@ -1,53 +0,0 @@ -{{ define "auth" }} - - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - -
Enable -
- {{ if .Auth.Auth }} - - {{ else }} - - {{ end }} -
-
Expire after
Login
New password
-
-
- -
-
-
- - -{{ template "footer" }} -{{ end }} diff --git a/internal/web/templates/config.html b/internal/web/templates/config.html index 998c100..a7fa462 100644 --- a/internal/web/templates/config.html +++ b/internal/web/templates/config.html @@ -1,36 +1,21 @@ -{{ define "config" }} +{{ define "config.html" }} -
+
-
- - - - - - - - - - +
+
+
Config
+
+
Interfaces
Path to DB
+ - + - - - - - - - - - + @@ -50,74 +35,95 @@ - - + + - - - - - - - - - - + + - - + +
Host
Port
Timeout (seconds)
Shoutrrr URL - -
Theme
Ignore IP - - Timeout (minutes)
Log Level - -
Keep history (days)
Arp Timeout - - Trim History
- -
-
-

Current version: {{ .Version }}

- +
+
Version
+ +
+
+
+
+
InfluxDB2 config
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Enable +
+ {{ if .Config.InfluxEnable }} + + {{ else }} + + {{ end }} +
+
Address
Token
Org
Bucket
Skip TLS verify +
+ {{ if .Config.InfluxSkipTLS }} + + {{ else }} + + {{ end }} +
+
+
+
+
+
About
+
+

● After changing Host or Port the app must be restarted

+

Timeout (minutes) - how often watched ports are scanned

+

Trim History - how many port states are saved in memory and displayed on the History page

+

● If you find this app useful, please, donate

+

● Commission you own app (Golang, HTML/JS). Contact and prices here

-
-
- -
-{{ template "footer" }} -{{ end }} +{{ template "footer.html" }} +{{ end }} \ No newline at end of file diff --git a/internal/web/templates/footer.html b/internal/web/templates/footer.html index 31b3bd0..ff9a2d2 100644 --- a/internal/web/templates/footer.html +++ b/internal/web/templates/footer.html @@ -1,4 +1,4 @@ -{{ define "footer"}} +{{ define "footer.html"}} -{{ end }} +{{ end }} \ No newline at end of file diff --git a/internal/web/templates/header.html b/internal/web/templates/header.html index f2a7fe1..1d2cd2a 100644 --- a/internal/web/templates/header.html +++ b/internal/web/templates/header.html @@ -1,26 +1,32 @@ -{{ define "header"}} +{{ define "header.html"}} + + - Watch Your LAN + WatchYourLAN - + {{ if eq .Config.NodePath "" }} - - - - - - {{ else }} - - - + + + + + + + {{ else }} + + + {{ end }}
-{{ end }} +{{ end }} \ No newline at end of file diff --git a/internal/web/templates/history.html b/internal/web/templates/history.html deleted file mode 100644 index 2589e0c..0000000 --- a/internal/web/templates/history.html +++ /dev/null @@ -1,43 +0,0 @@ -{{ define "history"}} - - - -
- - - - - - - - - - - {{ range .Hist }} - - - - - - - - - - {{ end }} -
NameIPMacHardwareTimeKnownState
{{ .Name }}{{ .IP }}{{ .Mac }}{{ .Hw }}{{ .Date }} - {{ if eq .Known 1 }} - Yes - {{ else }} - No - {{ end }} - - {{ if eq .State 1 }} - - {{ else }} - - {{ end }} -
-
- -{{ template "footer" }} -{{ end }} diff --git a/internal/web/templates/host.html b/internal/web/templates/host.html deleted file mode 100644 index d6dc008..0000000 --- a/internal/web/templates/host.html +++ /dev/null @@ -1,137 +0,0 @@ -{{ define "host" }} - - -
-
-
- - {{ range .Hosts }} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {{ end }} -
ID{{ .ID }}
Name{{ .Name }}
IP{{ .IP }}
Mac{{ .Mac }}
Hw{{ .Hw }}
Date{{ .Date }}
Known - {{ if eq .Known 1 }} - Yes - {{ else }} - No - {{ end }} -
Now - {{ if eq .Now 1 }} - Online - {{ else }} - Offline - {{ end }} -
-
- -
-
-
-
- - - - - -
Domain name - {{ range .Themes }} - {{ . }} - {{ end }} -
-
-
- {{ range .Hosts }} - {{ if eq .Now 1 }} -

Scan for open TCP ports (may take some time)

- - - - - - - - - - - - - - - - -
Start port
End port
- {{ end }} - {{ end }} -
-
-
- - - - - - - - - {{ range .Hist }} - - - - - - - - {{ end }} -
NameIPTimeKnownState
{{ .Name }}{{ .IP }}{{ .Date }} - {{ if eq .Known 1 }} - Yes - {{ else }} - No - {{ end }} - - {{ if eq .State 1 }} - - {{ else }} - - {{ end }} -
-
-
- - -{{ template "footer" }} -{{ end }} diff --git a/internal/web/templates/index.html b/internal/web/templates/index.html index dfe624b..f57cbd8 100644 --- a/internal/web/templates/index.html +++ b/internal/web/templates/index.html @@ -1,78 +1,40 @@ -{{ define "index"}} - - +{{ define "index.html" }} + + -
-
-
-

Online

-

Scanning interfaces: {{ .Config.Iface }}

-
+
+
+
+
+ + + +
+
+
+
+
+
+
- - - - - - - - {{ range .Hosts }} - {{ if eq .Now 1 }} - - - - - - - - {{ end }} - {{ end }} -
NameIPMacHardwareKnown
{{ .Name }}{{ .IP }}{{ .Mac }}{{ .Hw }} - {{ if eq .Known 1 }} - - {{ else }} - - {{ end }} -
-
-

Log

-
- - - - - - - - - - {{ range .Hosts }} - - - - - - - - - - - - {{ end }} + + + + + + + + + + +
NameIPMacHardwareLast seenKnown
- - {{ .IP }}{{ .Mac }}{{ .Hw }}{{ .Date }} - {{ if eq .Known 1 }} - - {{ else }} - - {{ end }} -
Name Addr Total Watching Online Offline
+
-{{ template "footer" }} -{{ end }} +{{ template "footer.html" }} +{{ end }} \ No newline at end of file diff --git a/internal/web/templates/line.html b/internal/web/templates/line.html deleted file mode 100644 index 832c98f..0000000 --- a/internal/web/templates/line.html +++ /dev/null @@ -1,83 +0,0 @@ -{{ define "line"}} - -
- - - - - - - - - - - - {{ range .Hosts }} - - - - - - - - - - - - {{ end }} -
Name
- - -
IP
- - -
Mac
- - - -
Hardware
- - -
Last seen
- - -
Known
- - -
- - {{ .IP }}{{ .Mac }}{{ .Hw }}{{ .Date }} - {{ if eq .Known 1 }} - - {{ else }} - - {{ end }} -
-
- -{{ template "footer" }} -{{ end }} diff --git a/internal/web/templates/login.html b/internal/web/templates/login.html deleted file mode 100644 index 887446f..0000000 --- a/internal/web/templates/login.html +++ /dev/null @@ -1,19 +0,0 @@ -{{ define "login" }} - - -
-
-
-
-
-
- - - -
-
-
-
- -{{ template "footer" }} -{{ end }} diff --git a/internal/web/templates/port.html b/internal/web/templates/port.html deleted file mode 100644 index a4b91d7..0000000 --- a/internal/web/templates/port.html +++ /dev/null @@ -1,18 +0,0 @@ -{{ define "port" }} - - -
-
-
-

Open TCP ports:

- {{ range .Themes }} - {{ . }} -
- {{ end }} -
-
-
- - -{{ template "footer" }} -{{ end }} diff --git a/internal/web/templates/version b/internal/web/templates/version deleted file mode 100644 index 623b223..0000000 --- a/internal/web/templates/version +++ /dev/null @@ -1 +0,0 @@ -VERSION=1.0.6 \ No newline at end of file diff --git a/internal/web/trim-history.go b/internal/web/trim-history.go deleted file mode 100644 index 0b3f383..0000000 --- a/internal/web/trim-history.go +++ /dev/null @@ -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) - } - } -} diff --git a/internal/web/update.go b/internal/web/update.go deleted file mode 100644 index 824b627..0000000 --- a/internal/web/update.go +++ /dev/null @@ -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) -} diff --git a/internal/web/webgui.go b/internal/web/webgui.go index e8542e5..b287c00 100644 --- a/internal/web/webgui.go +++ b/internal/web/webgui.go @@ -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) -}