From e6f3b150abd9cbedb0c8ddce2b0a01abbf226717 Mon Sep 17 00:00:00 2001 From: aceberg <1502200+aceberg@users.noreply.github.com> Date: Fri, 25 Jul 2025 02:11:12 +0700 Subject: [PATCH] History GUI upd --- CHANGELOG.md | 3 + frontend/package.json | 2 +- frontend/src/components/Config/About.tsx | 5 +- frontend/src/components/Config/Scan.tsx | 11 ---- frontend/src/components/HostPage/HistCard.tsx | 35 ++++++++---- frontend/src/components/MacHistory.tsx | 4 +- frontend/src/functions/api.ts | 7 +++ frontend/src/functions/exports.ts | 2 - frontend/src/functions/history.ts | 11 +++- frontend/src/pages/History.tsx | 2 +- internal/api/api.go | 49 +++++++++------- internal/api/routes.go | 22 +++---- internal/gdb/edit.go | 27 --------- internal/gdb/select.go | 57 +++++++++++++++++++ internal/web/public/assets/Config.js | 2 +- internal/web/public/assets/HistShow.js | 4 -- internal/web/public/assets/History.js | 2 +- internal/web/public/assets/HostPage.js | 2 +- internal/web/public/assets/MacHistory.js | 4 ++ internal/web/public/assets/index.js | 4 +- 20 files changed, 157 insertions(+), 98 deletions(-) create mode 100644 internal/gdb/select.go delete mode 100644 internal/web/public/assets/HistShow.js create mode 100644 internal/web/public/assets/MacHistory.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 9449ffd..4d1124a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ All notable changes to this project will be documented in this file. - Moved to `github.com/nicholas-fedor/shoutrrr` [#197](https://github.com/aceberg/WatchYourLAN/pull/197) - Removed `HIST_IN_DB` config option. Now history is always stored in `DB` +### Fixed +- Memory leak bug [#149](https://github.com/aceberg/WatchYourLAN/pull/149) + ## [v2.1.2] - 2025-03-30 ### Fixed - Edit names bug diff --git a/frontend/package.json b/frontend/package.json index 0e1df22..bf039f1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "watchyourlan", "private": true, - "version": "2.1.2", + "version": "2.1.3", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/components/Config/About.tsx b/frontend/src/components/Config/About.tsx index 965fe4e..2371159 100644 --- a/frontend/src/components/Config/About.tsx +++ b/frontend/src/components/Config/About.tsx @@ -19,16 +19,15 @@ function About() {

● After changing Host or Port the app must be restarted

-

Shoutrrr URL provides notifications to Discord, Email, Gotify, Telegram and other services. Link to documentation

+

Shoutrrr URL provides notifications to Discord, Email, Gotify, Telegram and other services. Link to documentation

Interfaces - one or more, space separated

Timeout (seconds) - time between scans

Args for arp-scan - pass your own arguments to arp-scan. Enable debug log level to see resulting command. (Example: -r 1). See docs for more.

Arp Strings - can setup scans for vlans, docker0 and etcetera. See docs for more.

Trim History - remove history after (hours)

-

Store History in DB - if off, the History will be stored only in memory and will be lost on app restart. Though, it will keep the app DB smaller and InfluxDB is recommended for long term History storage

+

Store History in DB - DEPRECATED. Now History is always stored in DB. Use Trim History to reduce DB size

PG Connect URL - address to connect to PostgreSQL DB. (Example: postgres://username:password@192.168.0.1:5432/dbname?sslmode=disable). Full list of URL parameters here

● If you find this app useful, please, donate

-

● Commission you own app (Golang, React/SolidJS). Contact here

) diff --git a/frontend/src/components/Config/Scan.tsx b/frontend/src/components/Config/Scan.tsx index 4496683..a97946c 100644 --- a/frontend/src/components/Config/Scan.tsx +++ b/frontend/src/components/Config/Scan.tsx @@ -48,17 +48,6 @@ function Scan() { Trim History (hours) - - Store History in DB - -
- {appConfig().HistInDB - ? - : - } -
- - Use DB handleDate(e.currentTarget.value)} + /> +
- {_props.mac !== "" - ? + {_props.mac !== "" && today() !== "" + ? : <>Loading... }
diff --git a/frontend/src/components/MacHistory.tsx b/frontend/src/components/MacHistory.tsx index 66a533d..0d2c45b 100644 --- a/frontend/src/components/MacHistory.tsx +++ b/frontend/src/components/MacHistory.tsx @@ -9,11 +9,11 @@ function MacHistory(_props: any) { let interval: number; onMount(async () => { - const newHistory = await getHistoryForMac(_props.mac); + const newHistory = await getHistoryForMac(_props.mac, _props.date); setHist(newHistory); interval = setInterval(async () => { // console.log("Upd Hist", new Date()); - const newHistory = await getHistoryForMac(_props.mac); + const newHistory = await getHistoryForMac(_props.mac, _props.date); setHist(newHistory); }, 60000); // 60000 ms = 1 minute }); diff --git a/frontend/src/functions/api.ts b/frontend/src/functions/api.ts index 94858c0..c4a568c 100644 --- a/frontend/src/functions/api.ts +++ b/frontend/src/functions/api.ts @@ -65,5 +65,12 @@ export const apiGetHistory = async (mac:string) => { const url = apiPath+'/api/history/'+mac; const hosts = await (await fetch(url)).json(); + return hosts; +}; + +export const apiGetHistoryByDate = async (mac:string, date: string) => { + const url = apiPath+'/api/history/'+mac+'/'+date; + const hosts = await (await fetch(url)).json(); + return hosts; }; \ No newline at end of file diff --git a/frontend/src/functions/exports.ts b/frontend/src/functions/exports.ts index d29c111..8acf391 100644 --- a/frontend/src/functions/exports.ts +++ b/frontend/src/functions/exports.ts @@ -27,7 +27,6 @@ export interface Conf { ArpArgs: string; ArpStrs: string[]; TrimHist: number; - HistInDB: boolean; ShoutURL: string; UseDB: string; PGConnect: string; @@ -68,7 +67,6 @@ export const emptyConf:Conf = { ArpArgs: "", ArpStrs: [], TrimHist: 48, - HistInDB: false, ShoutURL: "", UseDB: "", PGConnect: "", diff --git a/frontend/src/functions/history.ts b/frontend/src/functions/history.ts index e13be3e..45141f5 100644 --- a/frontend/src/functions/history.ts +++ b/frontend/src/functions/history.ts @@ -1,9 +1,14 @@ -import { apiGetHistory } from "./api"; +import { apiGetHistory, apiGetHistoryByDate } from "./api"; import { Host } from "./exports"; -export async function getHistoryForMac(mac: string) { +export async function getHistoryForMac(mac: string, date: string) { let h:Host[] = []; - h = await apiGetHistory(mac); + if (date === "") { + h = await apiGetHistory(mac); + } else { + h = await apiGetHistoryByDate(mac, date); + } + if (h != null) { h.sort((a:Host, b:Host) => (a.Date < b.Date ? 1 : -1)); return h; diff --git a/frontend/src/pages/History.tsx b/frontend/src/pages/History.tsx index 5edb1f1..1fb1de8 100644 --- a/frontend/src/pages/History.tsx +++ b/frontend/src/pages/History.tsx @@ -42,7 +42,7 @@ function History() { {host.IP} - + } diff --git a/internal/api/api.go b/internal/api/api.go index 12eaf68..dcb575f 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -14,39 +14,50 @@ import ( "github.com/aceberg/WatchYourLAN/internal/portscan" ) -func apiAll(c *gin.Context) { +func getAllHosts(c *gin.Context) { allHosts := gdb.Select("now") c.IndentedJSON(http.StatusOK, allHosts) } -func apiVersion(c *gin.Context) { +func getVersion(c *gin.Context) { c.IndentedJSON(http.StatusOK, conf.AppConfig.Version) } -func apiGetConfig(c *gin.Context) { +func getConfig(c *gin.Context) { c.IndentedJSON(http.StatusOK, conf.AppConfig) } -func apiHistory(c *gin.Context) { - var hosts []models.Host +func getHistory(c *gin.Context) { - mac := c.Param("mac") - - if mac != "/" { - mac = mac[1:] - hosts = gdb.SelectByMAC(mac) - } else { - hosts = gdb.Select("history") - } + hosts := gdb.Select("history") c.IndentedJSON(http.StatusOK, hosts) } -func apiHost(c *gin.Context) { +func getHistoryByMAC(c *gin.Context) { + + mac := c.Param("mac") + + hosts := gdb.SelectLatest(mac, 200) + + c.IndentedJSON(http.StatusOK, hosts) +} + +func getHistoryByDate(c *gin.Context) { + + mac := c.Param("mac") + date := c.Param("date") + + hosts := gdb.SelectByDate(mac, date) + + c.IndentedJSON(http.StatusOK, hosts) +} + +func getHost(c *gin.Context) { idStr := c.Param("id") @@ -56,7 +67,7 @@ func apiHost(c *gin.Context) { c.IndentedJSON(http.StatusOK, host) } -func apiHostDel(c *gin.Context) { +func delHost(c *gin.Context) { idStr := c.Param("id") host := getHostByID(idStr) // functions.go @@ -67,7 +78,7 @@ func apiHostDel(c *gin.Context) { c.IndentedJSON(http.StatusOK, "OK") } -func apiPort(c *gin.Context) { +func getPortState(c *gin.Context) { addr := c.Param("addr") port := c.Param("port") @@ -76,7 +87,7 @@ func apiPort(c *gin.Context) { c.IndentedJSON(http.StatusOK, state) } -func apiEdit(c *gin.Context) { +func editHost(c *gin.Context) { idStr := c.Param("id") name := c.Param("name") @@ -95,14 +106,14 @@ func apiEdit(c *gin.Context) { c.IndentedJSON(http.StatusOK, "OK") } -func apiNotifyTest(c *gin.Context) { +func notifyTest(c *gin.Context) { notify.Test() c.Status(http.StatusOK) } -func apiStatus(c *gin.Context) { +func getStatus(c *gin.Context) { var status models.Stat var searchHosts []models.Host diff --git a/internal/api/routes.go b/internal/api/routes.go index dff279f..29551f9 100644 --- a/internal/api/routes.go +++ b/internal/api/routes.go @@ -7,16 +7,18 @@ import ( // Routes - start API routes func Routes(router *gin.Engine) { - router.GET("/api/all", apiAll) // api.go - router.GET("/api/config", apiGetConfig) // api.go - router.GET("/api/edit/:id/:name/*known", apiEdit) // api.go - router.GET("/api/history/*mac", apiHistory) // api.go - router.GET("/api/host/:id", apiHost) // api.go - router.GET("/api/host/del/:id", apiHostDel) // api.go - router.GET("/api/notify_test", apiNotifyTest) // api.go - router.GET("/api/port/:addr/:port", apiPort) // api.go - router.GET("/api/status/*iface", apiStatus) // api.go - router.GET("/api/version", apiVersion) // api.go + router.GET("/api/all", getAllHosts) // api.go + router.GET("/api/config", getConfig) // api.go + router.GET("/api/edit/:id/:name/*known", editHost) // api.go + router.GET("/api/history", getHistory) // api.go + router.GET("/api/history/:mac", getHistoryByMAC) // api.go + router.GET("/api/history/:mac/:date", getHistoryByDate) // api.go + router.GET("/api/host/:id", getHost) // api.go + router.GET("/api/host/del/:id", delHost) // api.go + router.GET("/api/notify_test", notifyTest) // api.go + router.GET("/api/port/:addr/:port", getPortState) // api.go + router.GET("/api/status/*iface", getStatus) // api.go + router.GET("/api/version", getVersion) // api.go router.POST("/api/config/", saveConfigHandler) // config.go router.POST("/api/config_settings/", saveSettingsHandler) // config.go diff --git a/internal/gdb/edit.go b/internal/gdb/edit.go index 3f11a8d..f964438 100644 --- a/internal/gdb/edit.go +++ b/internal/gdb/edit.go @@ -5,33 +5,6 @@ import ( "github.com/aceberg/WatchYourLAN/internal/models" ) -// Select - get all hosts -func Select(table string) (dbHosts []models.Host) { - - tab := db.Table(table) - tab.Find(&dbHosts) - - return dbHosts -} - -// SelectByID - get host by ID -func SelectByID(id int) (host models.Host) { - - tab := db.Table("now") - tab.First(&host, id) - - return host -} - -// SelectByMAC - get all hosts by MAC -func SelectByMAC(mac string) (hosts []models.Host) { - - tab := db.Table("history") - tab.Where("MAC = ?", mac).Find(&hosts) - - return hosts -} - // Update - update or create host func Update(table string, oneHost models.Host) { diff --git a/internal/gdb/select.go b/internal/gdb/select.go new file mode 100644 index 0000000..38a158e --- /dev/null +++ b/internal/gdb/select.go @@ -0,0 +1,57 @@ +package gdb + +import ( + "github.com/aceberg/WatchYourLAN/internal/models" +) + +// Select - get all hosts +func Select(table string) (dbHosts []models.Host) { + + tab := db.Table(table) + tab.Find(&dbHosts) + + return dbHosts +} + +// SelectByID - get host by ID +func SelectByID(id int) (host models.Host) { + + tab := db.Table("now") + tab.First(&host, id) + + return host +} + +// SelectByMAC - get all hosts by MAC +func SelectByMAC(mac string) (hosts []models.Host) { + + tab := db.Table("history") + tab.Where("MAC = ?", mac).Find(&hosts) + + return hosts +} + +// SelectByDate - get all hosts by MAC +func SelectByDate(mac, date string) (hosts []models.Host) { + + tab := db.Table("history") + tab. + Where("MAC = ?", mac). + Where("DATE LIKE ?", date+"%"). + Find(&hosts) + + return hosts +} + +// SelectLatest - get latest hosts by MAC +func SelectLatest(mac string, number int) (hosts []models.Host) { + + tab := db.Table("history") + tab. + Where("MAC = ?", mac). + Order("DATE DESC"). + Limit(number). + Find(&hosts) + + return hosts +} diff --git a/internal/web/public/assets/Config.js b/internal/web/public/assets/Config.js index 243c9d1..947daf7 100644 --- a/internal/web/public/assets/Config.js +++ b/internal/web/public/assets/Config.js @@ -1 +1 @@ -import{c as Q,o as et,a as lt,t,i as l,b as c,s as G,d as Y,e as d,S as F,f as e,F as J,g as rt,h as it,j as W}from"./index.js";var st=t('
About ()

● After changing Host or Port the app must be restarted

Shoutrrr URL provides notifications to Discord, Email, Gotify, Telegram and other services. Link to documentation

Interfaces - one or more, space separated

Timeout (seconds) - time between scans

Args for arp-scan - pass your own arguments to arp-scan. Enable debug log level to see resulting command. (Example: -r 1). See docs for more.

Arp Strings - can setup scans for vlans, docker0 and etcetera. See docs for more.

Trim History - remove history after (hours)

Store History in DB - if off, the History will be stored only in memory and will be lost on app restart. Though, it will keep the app DB smaller and InfluxDB is recommended for long term History storage

PG Connect URL - address to connect to PostgreSQL DB. (Example: postgres://username:password@192.168.0.1:5432/dbname?sslmode=disable). Full list of URL parameters here

● If you find this app useful, please, donate

● Commission you own app (Golang, React/SolidJS). Contact here');function nt(){const[i,n]=Q(""),[a,s]=Q("");return et(async()=>{const r=await lt();n(r),s("https://github.com/aceberg/WatchYourLAN/releases/tag/"+r)}),(()=>{var r=st(),o=r.firstChild,p=o.firstChild,b=p.nextSibling;return l(b,i),c(()=>G(b,"href",a())),r})()}var at=t("

Host
Port
Theme
Color mode
Local node-bootstrap URL
Shoutrrr URL
'),ct=t("