From 24ee0acb456f0a88db8527203444b9c02536bdb1 Mon Sep 17 00:00:00 2001 From: aceberg <1502200+aceberg@users.noreply.github.com> Date: Sun, 7 Sep 2025 03:33:42 +0700 Subject: [PATCH] Add host from API, trigger rescan --- CHANGELOG.md | 4 +- backend/docs/docs.go | 67 +++++++++++++++++++ backend/docs/swagger.json | 67 +++++++++++++++++++ backend/docs/swagger.yaml | 46 +++++++++++++ backend/internal/api/api-hosts.go | 37 ++++++++++ backend/internal/api/api-system.go | 13 ++++ backend/internal/api/routes.go | 2 + backend/internal/gdb/select.go | 4 +- backend/internal/notify/shout.go | 2 +- backend/internal/web/public/assets/Config.js | 2 +- .../internal/web/public/assets/HostPage.js | 2 +- .../internal/web/public/assets/MacHistory.js | 4 +- backend/internal/web/public/assets/index.js | 2 +- frontend/src/components/Body/CardHead.tsx | 2 +- frontend/src/components/Body/TableHead.tsx | 2 +- frontend/src/components/Body/TableRow.tsx | 4 +- frontend/src/components/Config/About.tsx | 2 +- frontend/src/components/Config/Scan.tsx | 2 +- frontend/src/components/HostPage/HostCard.tsx | 8 +-- frontend/src/pages/Body.tsx | 7 +- 20 files changed, 255 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e29f20..cafaea8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,12 +4,10 @@ All notable changes to this project will be documented in this file. ## [v2.1.4] - 2025-09 ### Added - Swagger API docs +- Add host from API - Delete selected hosts - Wake-on-LAN [#135](https://github.com/aceberg/WatchYourLAN/issues/135), [#196](https://github.com/aceberg/WatchYourLAN/issues/196) -### Fixed -- Names not fully saved while editing with GUI - ## [v2.1.3] - 2025-07-26 ### Fixed - Memory leak bug [#149](https://github.com/aceberg/WatchYourLAN/issues/149) diff --git a/backend/docs/docs.go b/backend/docs/docs.go index 3ae7880..a89c311 100644 --- a/backend/docs/docs.go +++ b/backend/docs/docs.go @@ -207,6 +207,53 @@ const docTemplate = `{ } } }, + "/host/add/{mac}": { + "get": { + "description": "Add host by MAC, with optional Name, IP, Hardware\nReturns ` + "`" + `models.Host` + "`" + ` with this MAC form DB, either just added or existing", + "produces": [ + "application/json" + ], + "tags": [ + "hosts" + ], + "summary": "Add host manually", + "parameters": [ + { + "type": "string", + "description": "Host MAC", + "name": "mac", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Name", + "name": "name", + "in": "query" + }, + { + "type": "string", + "description": "IP", + "name": "ip", + "in": "query" + }, + { + "type": "string", + "description": "Hardware", + "name": "hw", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.Host" + } + } + } + } + }, "/host/del/{id}": { "get": { "description": "Remove a host from the database by its unique ID", @@ -321,6 +368,26 @@ const docTemplate = `{ } } }, + "/rescan": { + "get": { + "description": "Manually trigger rescan", + "produces": [ + "application/json" + ], + "tags": [ + "system" + ], + "summary": "Rescan all interfaces now", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } + }, "/status/{iface}": { "get": { "description": "Retrieve summary statistics of hosts, optionally filtered by interface", diff --git a/backend/docs/swagger.json b/backend/docs/swagger.json index 7f85eed..21f36b4 100644 --- a/backend/docs/swagger.json +++ b/backend/docs/swagger.json @@ -200,6 +200,53 @@ } } }, + "/host/add/{mac}": { + "get": { + "description": "Add host by MAC, with optional Name, IP, Hardware\nReturns `models.Host` with this MAC form DB, either just added or existing", + "produces": [ + "application/json" + ], + "tags": [ + "hosts" + ], + "summary": "Add host manually", + "parameters": [ + { + "type": "string", + "description": "Host MAC", + "name": "mac", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Name", + "name": "name", + "in": "query" + }, + { + "type": "string", + "description": "IP", + "name": "ip", + "in": "query" + }, + { + "type": "string", + "description": "Hardware", + "name": "hw", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.Host" + } + } + } + } + }, "/host/del/{id}": { "get": { "description": "Remove a host from the database by its unique ID", @@ -314,6 +361,26 @@ } } }, + "/rescan": { + "get": { + "description": "Manually trigger rescan", + "produces": [ + "application/json" + ], + "tags": [ + "system" + ], + "summary": "Rescan all interfaces now", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } + }, "/status/{iface}": { "get": { "description": "Retrieve summary statistics of hosts, optionally filtered by interface", diff --git a/backend/docs/swagger.yaml b/backend/docs/swagger.yaml index 5fe97a6..e24c518 100644 --- a/backend/docs/swagger.yaml +++ b/backend/docs/swagger.yaml @@ -254,6 +254,39 @@ paths: summary: Get host by ID tags: - hosts + /host/add/{mac}: + get: + description: |- + Add host by MAC, with optional Name, IP, Hardware + Returns `models.Host` with this MAC form DB, either just added or existing + parameters: + - description: Host MAC + in: path + name: mac + required: true + type: string + - description: Name + in: query + name: name + type: string + - description: IP + in: query + name: ip + type: string + - description: Hardware + in: query + name: hw + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.Host' + summary: Add host manually + tags: + - hosts /host/del/{id}: get: description: Remove a host from the database by its unique ID @@ -310,6 +343,19 @@ paths: summary: Check port state tags: - network + /rescan: + get: + description: Manually trigger rescan + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + summary: Rescan all interfaces now + tags: + - system /status/{iface}: get: description: Retrieve summary statistics of hosts, optionally filtered by interface diff --git a/backend/internal/api/api-hosts.go b/backend/internal/api/api-hosts.go index 28a1e36..e692439 100644 --- a/backend/internal/api/api-hosts.go +++ b/backend/internal/api/api-hosts.go @@ -8,6 +8,7 @@ import ( "github.com/aceberg/WatchYourLAN/internal/check" "github.com/aceberg/WatchYourLAN/internal/gdb" + "github.com/aceberg/WatchYourLAN/internal/models" ) // getAllHosts godoc @@ -53,6 +54,42 @@ func delHost(c *gin.Context) { c.IndentedJSON(http.StatusOK, "OK") } +// addHost godoc +// @Summary Add host manually +// @Description Add host by MAC, with optional Name, IP, Hardware +// @Description Returns `models.Host` with this MAC form DB, either just added or existing +// @Tags hosts +// @Produce json +// @Param mac path string true "Host MAC" +// @Param name query string false "Name" +// @Param ip query string false "IP" +// @Param hw query string false "Hardware" +// @Success 200 {object} models.Host +// @Router /host/add/{mac} [get] +func addHost(c *gin.Context) { + + mac := c.Param("mac") + hosts := gdb.SelectByMAC("now", mac) + + if len(hosts) > 0 { + slog.Warn("Host with this MAC already exists", "host", hosts[0]) + } else { + var host models.Host + + host.Mac = mac + host.Name = c.Query("name") + host.IP = c.Query("ip") + host.Hw = c.Query("hw") + + gdb.Update("now", host) + hosts = gdb.SelectByMAC("now", mac) + + slog.Info("Added host to DB", "host", hosts[0]) + } + + c.IndentedJSON(http.StatusOK, hosts[0]) +} + // editHost godoc // @Summary Edit host // @Description Update a host's name and optionally toggle its "known" status diff --git a/backend/internal/api/api-system.go b/backend/internal/api/api-system.go index 581bb8e..780201a 100644 --- a/backend/internal/api/api-system.go +++ b/backend/internal/api/api-system.go @@ -9,6 +9,7 @@ import ( "github.com/aceberg/WatchYourLAN/internal/gdb" "github.com/aceberg/WatchYourLAN/internal/models" "github.com/aceberg/WatchYourLAN/internal/notify" + "github.com/aceberg/WatchYourLAN/internal/routines" ) // getVersion godoc @@ -22,6 +23,18 @@ func getVersion(c *gin.Context) { c.IndentedJSON(http.StatusOK, conf.AppConfig.Version) } +// triggerRescan godoc +// @Summary Rescan all interfaces now +// @Description Manually trigger rescan +// @Tags system +// @Produce json +// @Success 200 {string} string "OK" +// @Router /rescan [get] +func triggerRescan(c *gin.Context) { + routines.ScanRestart() + c.Status(http.StatusOK) +} + // getConfig godoc // @Summary Get application configuration // @Description Returns the current configuration used by the app diff --git a/backend/internal/api/routes.go b/backend/internal/api/routes.go index 6baede5..f27223a 100644 --- a/backend/internal/api/routes.go +++ b/backend/internal/api/routes.go @@ -16,11 +16,13 @@ func Routes(router *gin.Engine) { r0.GET("/edit/:id/:name/*known", editHost) // api-hosts.go r0.GET("/host/:id", getHost) // api-hosts.go r0.GET("/host/del/:id", delHost) // api-hosts.go + r0.GET("/host/add/:mac", addHost) // api-hosts.go r0.GET("/config", getConfig) // api-system.go r0.GET("/notify_test", notifyTest) // api-system.go r0.GET("/status/*iface", getStatus) // api-system.go r0.GET("/version", getVersion) // api-system.go + r0.GET("/rescan", triggerRescan) // api-system.go r0.GET("/history", getHistory) // api-history.go r0.GET("/history/:mac", getHistoryByMAC) // api-history.go diff --git a/backend/internal/gdb/select.go b/backend/internal/gdb/select.go index a4885b1..352fdb6 100644 --- a/backend/internal/gdb/select.go +++ b/backend/internal/gdb/select.go @@ -24,9 +24,9 @@ func SelectByID(id int) (host models.Host) { } // SelectByMAC - get all hosts by MAC -func SelectByMAC(mac string) (hosts []models.Host) { +func SelectByMAC(table, mac string) (hosts []models.Host) { - tab := db.Table("history") + tab := db.Table(table) tab.Where("\"MAC\" = ?", mac).Find(&hosts) return hosts diff --git a/backend/internal/notify/shout.go b/backend/internal/notify/shout.go index 1545028..a464a72 100644 --- a/backend/internal/notify/shout.go +++ b/backend/internal/notify/shout.go @@ -17,7 +17,6 @@ func Unknown(host models.Host) { msg := fmt.Sprintf("Unknown host found. Name: '%s', IP: '%s', MAC: '%s', Hw: '%s', Iface: '%s'", host.DNS, host.IP, host.Mac, host.Hw, host.Iface) slog.Warn(msg) - shout(msg) } @@ -25,6 +24,7 @@ func Unknown(host models.Host) { func Test() { msg := "test notification" + slog.Info("Sending " + msg) shout(msg) } diff --git a/backend/internal/web/public/assets/Config.js b/backend/internal/web/public/assets/Config.js index 44a4304..894ad64 100644 --- a/backend/internal/web/public/assets/Config.js +++ b/backend/internal/web/public/assets/Config.js @@ -1 +1 @@ -import{c as J,o as K,a as X,t,i as r,b,s as H,d as W,e as c,S as V,f as e,F as M,g as Z,h as tt,j as Q}from"./index.js";var et=t('
| Swagger API docs | /swagger/index.html |
| Local node-bootstrap URL | local themes and fonts (optional). If empty, the app will pull everything from cdn |
| 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) |
| 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');function rt(){const[i,d]=J(""),[s,a]=J("");return K(async()=>{const l=await X();d(l),a("https://github.com/aceberg/WatchYourLAN/releases/tag/"+l)}),(()=>{var l=et(),n=l.firstChild,o=n.firstChild,p=o.nextSibling;return r(p,i),b(()=>H(p,"href",s())),l})()}var lt=t(" |