From b8f7d3bfb396a3fab1a5132bc9a63f306d4e422d Mon Sep 17 00:00:00 2001 From: aceberg <1502200+aceberg@users.noreply.github.com> Date: Fri, 6 Oct 2023 13:36:11 +0700 Subject: [PATCH] DB: history --- internal/db/edit.go | 51 +++++++++++++------------- internal/db/hist-edit.go | 31 ++++++++++++++++ internal/db/sqlite.go | 21 ++++++++++- internal/models/models.go | 29 ++++++++------- internal/scan/compare.go | 38 ++++++++++++++++++- internal/scan/start.go | 7 +++- internal/web/{auth-conf.go => auth.go} | 0 internal/web/history.go | 17 +++++++++ internal/web/host.go | 6 +-- internal/web/templates/header.html | 3 ++ internal/web/templates/history.html | 45 +++++++++++++++++++++++ internal/web/templates/host.html | 2 +- internal/web/update.go | 4 +- internal/web/webgui.go | 5 ++- 14 files changed, 208 insertions(+), 51 deletions(-) create mode 100644 internal/db/hist-edit.go rename internal/web/{auth-conf.go => auth.go} (100%) create mode 100644 internal/web/history.go create mode 100644 internal/web/templates/history.html diff --git a/internal/db/edit.go b/internal/db/edit.go index e9cfb5b..af68063 100644 --- a/internal/db/edit.go +++ b/internal/db/edit.go @@ -2,35 +2,36 @@ package db import ( "fmt" - "log" - "os" "github.com/aceberg/WatchYourLAN/internal/models" ) // Create - create DB if not exists func Create(path string) { - if _, err := os.Stat(path); err == nil { - log.Println("INFO: DB exists") - } else { - sqlStatement := `CREATE TABLE "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) - log.Println("INFO: Table created!") - } -} -// SetNow - mark all hosts as offline -func SetNow(path string) { - sqlStatement := `UPDATE "now" set NOW = '0';` + 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) } @@ -41,7 +42,7 @@ func Insert(path string, oneHost models.Host) { 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) - //fmt.Println("Insert statement:", sqlStatement) + dbExec(path, sqlStatement) } @@ -54,12 +55,12 @@ func Update(path string, oneHost models.Host) { 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) - //fmt.Println("Update statement:", sqlStatement) + dbExec(path, sqlStatement) } // Delete - delete host from DB -func Delete(path string, id uint16) { +func Delete(path string, id int) { sqlStatement := `DELETE FROM "now" WHERE ID='%d';` sqlStatement = fmt.Sprintf(sqlStatement, id) dbExec(path, sqlStatement) diff --git a/internal/db/hist-edit.go b/internal/db/hist-edit.go new file mode 100644 index 0000000..7b75d4a --- /dev/null +++ b/internal/db/hist-edit.go @@ -0,0 +1,31 @@ +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/sqlite.go b/internal/db/sqlite.go index e0340b0..bf83438 100644 --- a/internal/db/sqlite.go +++ b/internal/db/sqlite.go @@ -41,8 +41,27 @@ func Select(path string) (dbHosts []models.Host) { mu.Unlock() if err != nil { - log.Fatal("ERROR: db_select: ", err) + log.Fatal("ERROR: db.Select: ", err) } return dbHosts } + +// SelectHist - select all history +func SelectHist(path string) (hist []models.History) { + + sqlStatement := `SELECT * FROM "history" ORDER BY DATE DESC` + + mu.Lock() + db, _ := sqlx.Connect("sqlite", path) + defer db.Close() + + err := db.Select(&hist, sqlStatement) + mu.Unlock() + + if err != nil { + log.Fatal("ERROR: db.SelectHist: ", err) + } + + return hist +} diff --git a/internal/models/models.go b/internal/models/models.go index e690e73..57a0acf 100644 --- a/internal/models/models.go +++ b/internal/models/models.go @@ -23,25 +23,28 @@ type Conf struct { // Host - one host type Host struct { - ID uint16 `db:"ID"` + ID int `db:"ID"` Name string `db:"NAME"` IP string `db:"IP"` Mac string `db:"MAC"` Hw string `db:"HW"` Date string `db:"DATE"` - Known uint16 `db:"KNOWN"` - Now uint16 `db:"NOW"` + Known int `db:"KNOWN"` + Now int `db:"NOW"` } -// // History for hosts -// type History struct { -// ID int -// HostID int -// Name string -// IP string -// Date string -// State bool -// } +// History for hosts +type History struct { + ID int `db:"ID"` + Host int `db:"HOST"` + Name string `db:"NAME"` + IP string `db:"IP"` + Mac string `db:"MAC"` + Hw string `db:"HW"` + Date string `db:"DATE"` + Known int `db:"KNOWN"` + State int `db:"STATE"` +} // GuiData - all data sent to html page type GuiData struct { @@ -50,5 +53,5 @@ type GuiData struct { Themes []string Version string Auth auth.Conf - // HistLog []History + Hist []History } diff --git a/internal/scan/compare.go b/internal/scan/compare.go index 6d56eb0..5b6f22c 100644 --- a/internal/scan/compare.go +++ b/internal/scan/compare.go @@ -9,14 +9,20 @@ import ( "github.com/aceberg/WatchYourLAN/internal/notify" ) -func hostsCompare(appConfig models.Conf) { +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) @@ -26,6 +32,8 @@ func hostsCompare(appConfig models.Conf) { } else if oneHost.Now == 1 { oneHost.Now = 0 db.Update(appConfig.DbPath, oneHost) + + histAdd(oneHost, 0) } } @@ -36,5 +44,33 @@ func hostsCompare(appConfig models.Conf) { 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 index 78449a8..5e0c407 100644 --- a/internal/scan/start.go +++ b/internal/scan/start.go @@ -7,14 +7,17 @@ import ( "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(appConfig models.Conf, quit chan bool) { +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 { @@ -29,7 +32,7 @@ func Start(appConfig models.Conf, quit chan bool) { dbHosts = db.Select(appConfig.DbPath) toMap() - hostsCompare(appConfig) // compare.go + hostsCompare() // compare.go lastDate = time.Now() } diff --git a/internal/web/auth-conf.go b/internal/web/auth.go similarity index 100% rename from internal/web/auth-conf.go rename to internal/web/auth.go diff --git a/internal/web/history.go b/internal/web/history.go new file mode 100644 index 0000000..b4c0929 --- /dev/null +++ b/internal/web/history.go @@ -0,0 +1,17 @@ +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 index 4f0f2e2..d46e157 100644 --- a/internal/web/host.go +++ b/internal/web/host.go @@ -19,7 +19,7 @@ func delHandler(w http.ResponseWriter, r *http.Request) { log.Println("INFO: delete host ID =", id) - db.Delete(AppConfig.DbPath, uint16(id)) + db.Delete(AppConfig.DbPath, id) http.Redirect(w, r, "/", 302) } @@ -35,10 +35,8 @@ func hostHandler(w http.ResponseWriter, r *http.Request) { id, err := strconv.Atoi(idStr) check.IfError(err) - id16 := uint16(id) - for _, oneHost := range AllHosts { - if id16 == oneHost.ID { + if id == oneHost.ID { host = oneHost break } diff --git a/internal/web/templates/header.html b/internal/web/templates/header.html index fb4e04b..dedd2c9 100644 --- a/internal/web/templates/header.html +++ b/internal/web/templates/header.html @@ -28,6 +28,9 @@ + diff --git a/internal/web/templates/history.html b/internal/web/templates/history.html new file mode 100644 index 0000000..be2316e --- /dev/null +++ b/internal/web/templates/history.html @@ -0,0 +1,45 @@ +{{ define "history"}} + + + +
+ + + + + + + + + + + + {{ range .Hist }} + + + + + + + + + + + {{ end }} +
IDNameIPMacHardwareLast seenKnownState
{{ .Host }}{{ .Name }}{{ .IP }}{{ .Mac }}{{ .Hw }}{{ .Date }} + {{ if eq .Known 1 }} + Yes + {{ else }} + No + {{ end }} + + {{ if eq .State 1 }} + + {{ else }} + + {{ end }} +
+
+ +{{ template "footer" }} +{{ end }} \ No newline at end of file diff --git a/internal/web/templates/host.html b/internal/web/templates/host.html index d539b61..82158e8 100644 --- a/internal/web/templates/host.html +++ b/internal/web/templates/host.html @@ -67,7 +67,7 @@ Domain name {{ range .Themes }} - {{ . }} + {{ . }} {{ end }} diff --git a/internal/web/update.go b/internal/web/update.go index 55e1cfc..824b627 100644 --- a/internal/web/update.go +++ b/internal/web/update.go @@ -16,7 +16,7 @@ func updateHandler(w http.ResponseWriter, r *http.Request) { if idStr == "" { fmt.Fprintf(w, "No data!") } else { - var known uint16 + var known int id, _ := strconv.Atoi(idStr) known = 0 if knownStr == "on" { @@ -24,7 +24,7 @@ func updateHandler(w http.ResponseWriter, r *http.Request) { } for i, oneHost := range AllHosts { - if oneHost.ID == uint16(id) { + if oneHost.ID == id { AllHosts[i].Name = name AllHosts[i].Known = known db.Update(AppConfig.DbPath, AllHosts[i]) diff --git a/internal/web/webgui.go b/internal/web/webgui.go index 835fc3b..3f433ed 100644 --- a/internal/web/webgui.go +++ b/internal/web/webgui.go @@ -34,11 +34,12 @@ func Gui(configPath, nodePath string) { http.HandleFunc("/login/", loginHandler) // login.go http.HandleFunc("/", auth.Auth(indexHandler, &authConf)) - http.HandleFunc("/auth_conf/", auth.Auth(authConfHandler, &authConf)) // auth-conf.go - http.HandleFunc("/auth_save/", auth.Auth(saveAuthHandler, &authConf)) // auth-conf.go + 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