diff --git a/.github/workflows/binary-release.yml b/.github/workflows/binary-release.yml index b9484b7..875c6e5 100644 --- a/.github/workflows/binary-release.yml +++ b/.github/workflows/binary-release.yml @@ -23,5 +23,6 @@ jobs: distribution: goreleaser version: '~> v2' args: release --clean + workdir: backend env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/new-dev-docker.yml b/.github/workflows/new-dev-docker.yml index 74c7aa2..bd344c6 100644 --- a/.github/workflows/new-dev-docker.yml +++ b/.github/workflows/new-dev-docker.yml @@ -13,7 +13,7 @@ env: jobs: build: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout repository diff --git a/.version b/.version index 53be989..6f179ad 120000 --- a/.version +++ b/.version @@ -1 +1 @@ -internal/web/public/version \ No newline at end of file +backend/internal/web/public/version \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index c6aaabd..dd318f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. ## [v2.1.4] - 2025-09 ### Added +- Swagger API docs - Wake-on-LAN [#135](https://github.com/aceberg/WatchYourLAN/issues/135), [#196](https://github.com/aceberg/WatchYourLAN/issues/196) ### Fixed diff --git a/Dockerfile b/Dockerfile index 8e255b2..8ea3b33 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,10 +6,10 @@ COPY --from=xx / / WORKDIR /src -COPY go.mod go.sum ./ +COPY backend/go.mod backend/go.sum ./ RUN go mod download -COPY . . +COPY backend/ . ARG TARGETPLATFORM RUN CGO_ENABLED=0 xx-go build -ldflags='-w -s' -o /WatchYourLAN ./cmd/WatchYourLAN diff --git a/backend/.goreleaser.yaml b/backend/.goreleaser.yaml index 16b32c4..3e5c061 100644 --- a/backend/.goreleaser.yaml +++ b/backend/.goreleaser.yaml @@ -54,8 +54,6 @@ nfpms: archives: - files: - LICENSE - - README.md - - CHANGELOG.md - src: ./configs/watchyourlan.service dst: watchyourlan.service - src: ./configs/install.sh diff --git a/backend/Makefile b/backend/Makefile index 5ac458f..3cbf30d 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -19,4 +19,4 @@ lint: check: fmt lint swag: - swag init --parseInternal --dir cmd/WatchYourLAN \ No newline at end of file + swag init -g main.go --dir cmd/WatchYourLAN,internal/api,internal/models \ No newline at end of file diff --git a/backend/cmd/WatchYourLAN/main.go b/backend/cmd/WatchYourLAN/main.go index 166b1fa..edddab2 100644 --- a/backend/cmd/WatchYourLAN/main.go +++ b/backend/cmd/WatchYourLAN/main.go @@ -1,3 +1,11 @@ +// @title WatchYourLAN API +// @version 0.1 +// @description Lightweight network IP scanner written in Go +// @contact.url https://github.com/aceberg/WatchYourLAN +// @license.name MIT +// @license.url https://opensource.org/licenses/MIT +// @BasePath /api/ + package main import ( @@ -6,6 +14,9 @@ import ( // _ "net/http/pprof" + // Import Swagger docs + _ "github.com/aceberg/WatchYourLAN/docs" + "github.com/aceberg/WatchYourLAN/internal/conf" "github.com/aceberg/WatchYourLAN/internal/gdb" "github.com/aceberg/WatchYourLAN/internal/routines" diff --git a/backend/docs/docs.go b/backend/docs/docs.go new file mode 100644 index 0000000..3ae7880 --- /dev/null +++ b/backend/docs/docs.go @@ -0,0 +1,563 @@ +// Package docs Code generated by swaggo/swag. DO NOT EDIT +package docs + +import "github.com/swaggo/swag" + +const docTemplate = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{escape .Description}}", + "title": "{{.Title}}", + "contact": { + "url": "https://github.com/aceberg/WatchYourLAN" + }, + "license": { + "name": "MIT", + "url": "https://opensource.org/licenses/MIT" + }, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/all": { + "get": { + "description": "Retrieve all hosts from the database", + "produces": [ + "application/json" + ], + "tags": [ + "hosts" + ], + "summary": "Get all hosts", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Host" + } + } + } + } + } + }, + "/config": { + "get": { + "description": "Returns the current configuration used by the app", + "produces": [ + "application/json" + ], + "tags": [ + "system" + ], + "summary": "Get application configuration", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.Conf" + } + } + } + } + }, + "/edit/{id}/{name}/{known}": { + "get": { + "description": "Update a host's name and optionally toggle its \"known\" status", + "produces": [ + "application/json" + ], + "tags": [ + "hosts" + ], + "summary": "Edit host", + "parameters": [ + { + "type": "string", + "description": "Host ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "New name for the host", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Pass 'toggle' to flip the known/unknown status", + "name": "known", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } + }, + "/history": { + "get": { + "description": "Retrieve the complete history of all hosts. Not recommended, the output can be a lot", + "produces": [ + "application/json" + ], + "tags": [ + "history" + ], + "summary": "Get full history", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Host" + } + } + } + } + } + }, + "/history/{mac}": { + "get": { + "description": "Retrieve the latest history entries for a specific host by MAC address", + "produces": [ + "application/json" + ], + "tags": [ + "history" + ], + "summary": "Get history by MAC", + "parameters": [ + { + "type": "string", + "description": "MAC address of the host", + "name": "mac", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Number of history entries to return", + "name": "num", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Host" + } + } + } + } + } + }, + "/history/{mac}/{date}": { + "get": { + "description": "Retrieve history for a specific host on a given date\nThe date format is flexible and can be:\n- Year only: ` + "`" + `2025` + "`" + `\n- Year + month: ` + "`" + `2025-09` + "`" + `\n- Full date: ` + "`" + `2025-09-06` + "`" + `\n- Full timestamp: ` + "`" + `2025-09-06 00:58:26` + "`" + `", + "produces": [ + "application/json" + ], + "tags": [ + "history" + ], + "summary": "Get history by date", + "parameters": [ + { + "type": "string", + "description": "MAC address of the host", + "name": "mac", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Date filter (supports YYYY, YYYY-MM, YYYY-MM-DD, YYYY-MM-DD HH:mm:ss)", + "name": "date", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Host" + } + } + } + } + } + }, + "/host/del/{id}": { + "get": { + "description": "Remove a host from the database by its unique ID", + "produces": [ + "application/json" + ], + "tags": [ + "hosts" + ], + "summary": "Delete host", + "parameters": [ + { + "type": "string", + "description": "Host ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } + }, + "/host/{id}": { + "get": { + "description": "Retrieve detailed information about a host by its unique ID", + "produces": [ + "application/json" + ], + "tags": [ + "hosts" + ], + "summary": "Get host by ID", + "parameters": [ + { + "type": "string", + "description": "Host ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.Host" + } + } + } + } + }, + "/notify_test": { + "get": { + "description": "Trigger a test notification to verify notification settings", + "produces": [ + "application/json" + ], + "tags": [ + "system" + ], + "summary": "Send test notification", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } + }, + "/port/{addr}/{port}": { + "get": { + "description": "Check whether a given TCP port on an address is open or closed", + "produces": [ + "application/json" + ], + "tags": [ + "network" + ], + "summary": "Check port state", + "parameters": [ + { + "type": "string", + "description": "IP address or hostname", + "name": "addr", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Port number", + "name": "port", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "true if open, false if closed", + "schema": { + "type": "boolean" + } + } + } + } + }, + "/status/{iface}": { + "get": { + "description": "Retrieve summary statistics of hosts, optionally filtered by interface", + "produces": [ + "application/json" + ], + "tags": [ + "system" + ], + "summary": "Get network status", + "parameters": [ + { + "type": "string", + "description": "Interface name (omit for all interfaces)", + "name": "iface", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.Stat" + } + } + } + } + }, + "/version": { + "get": { + "description": "Returns the current running version of the application", + "produces": [ + "application/json" + ], + "tags": [ + "system" + ], + "summary": "Get application version", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } + }, + "/wol/{mac}": { + "get": { + "description": "Send a magic packet to wake up a host by its MAC address", + "produces": [ + "application/json" + ], + "tags": [ + "network" + ], + "summary": "Send Wake-on-LAN packet", + "parameters": [ + { + "type": "string", + "description": "MAC address of the host", + "name": "mac", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "true if sent successfully", + "schema": { + "type": "boolean" + } + } + } + } + } + }, + "definitions": { + "models.Conf": { + "type": "object", + "properties": { + "arpArgs": { + "type": "string" + }, + "arpStrs": { + "type": "array", + "items": { + "type": "string" + } + }, + "color": { + "type": "string" + }, + "confPath": { + "type": "string" + }, + "dbpath": { + "type": "string" + }, + "dirPath": { + "type": "string" + }, + "host": { + "type": "string" + }, + "ifaces": { + "type": "string" + }, + "influxAddr": { + "type": "string" + }, + "influxBucket": { + "type": "string" + }, + "influxEnable": { + "description": "InfluxDB", + "type": "boolean" + }, + "influxOrg": { + "type": "string" + }, + "influxSkipTLS": { + "type": "boolean" + }, + "influxToken": { + "type": "string" + }, + "logLevel": { + "type": "string" + }, + "nodePath": { + "type": "string" + }, + "pgconnect": { + "type": "string" + }, + "port": { + "type": "string" + }, + "prometheusEnable": { + "description": "Prometheus", + "type": "boolean" + }, + "shoutURL": { + "type": "string" + }, + "theme": { + "type": "string" + }, + "timeout": { + "type": "integer" + }, + "trimHist": { + "type": "integer" + }, + "useDB": { + "description": "PostgreSQL", + "type": "string" + }, + "version": { + "type": "string" + } + } + }, + "models.Host": { + "type": "object", + "properties": { + "date": { + "type": "string" + }, + "dns": { + "type": "string" + }, + "hw": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "iface": { + "type": "string" + }, + "ip": { + "type": "string" + }, + "known": { + "type": "integer" + }, + "mac": { + "type": "string" + }, + "name": { + "type": "string" + }, + "now": { + "type": "integer" + } + } + }, + "models.Stat": { + "type": "object", + "properties": { + "known": { + "type": "integer" + }, + "offline": { + "type": "integer" + }, + "online": { + "type": "integer" + }, + "total": { + "type": "integer" + }, + "unknown": { + "type": "integer" + } + } + } + } +}` + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = &swag.Spec{ + Version: "0.1", + Host: "", + BasePath: "/api/", + Schemes: []string{}, + Title: "WatchYourLAN API", + Description: "Lightweight network IP scanner written in Go", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, + LeftDelim: "{{", + RightDelim: "}}", +} + +func init() { + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) +} diff --git a/backend/docs/swagger.json b/backend/docs/swagger.json new file mode 100644 index 0000000..7f85eed --- /dev/null +++ b/backend/docs/swagger.json @@ -0,0 +1,538 @@ +{ + "swagger": "2.0", + "info": { + "description": "Lightweight network IP scanner written in Go", + "title": "WatchYourLAN API", + "contact": { + "url": "https://github.com/aceberg/WatchYourLAN" + }, + "license": { + "name": "MIT", + "url": "https://opensource.org/licenses/MIT" + }, + "version": "0.1" + }, + "basePath": "/api/", + "paths": { + "/all": { + "get": { + "description": "Retrieve all hosts from the database", + "produces": [ + "application/json" + ], + "tags": [ + "hosts" + ], + "summary": "Get all hosts", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Host" + } + } + } + } + } + }, + "/config": { + "get": { + "description": "Returns the current configuration used by the app", + "produces": [ + "application/json" + ], + "tags": [ + "system" + ], + "summary": "Get application configuration", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.Conf" + } + } + } + } + }, + "/edit/{id}/{name}/{known}": { + "get": { + "description": "Update a host's name and optionally toggle its \"known\" status", + "produces": [ + "application/json" + ], + "tags": [ + "hosts" + ], + "summary": "Edit host", + "parameters": [ + { + "type": "string", + "description": "Host ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "New name for the host", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Pass 'toggle' to flip the known/unknown status", + "name": "known", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } + }, + "/history": { + "get": { + "description": "Retrieve the complete history of all hosts. Not recommended, the output can be a lot", + "produces": [ + "application/json" + ], + "tags": [ + "history" + ], + "summary": "Get full history", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Host" + } + } + } + } + } + }, + "/history/{mac}": { + "get": { + "description": "Retrieve the latest history entries for a specific host by MAC address", + "produces": [ + "application/json" + ], + "tags": [ + "history" + ], + "summary": "Get history by MAC", + "parameters": [ + { + "type": "string", + "description": "MAC address of the host", + "name": "mac", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Number of history entries to return", + "name": "num", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Host" + } + } + } + } + } + }, + "/history/{mac}/{date}": { + "get": { + "description": "Retrieve history for a specific host on a given date\nThe date format is flexible and can be:\n- Year only: `2025`\n- Year + month: `2025-09`\n- Full date: `2025-09-06`\n- Full timestamp: `2025-09-06 00:58:26`", + "produces": [ + "application/json" + ], + "tags": [ + "history" + ], + "summary": "Get history by date", + "parameters": [ + { + "type": "string", + "description": "MAC address of the host", + "name": "mac", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Date filter (supports YYYY, YYYY-MM, YYYY-MM-DD, YYYY-MM-DD HH:mm:ss)", + "name": "date", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Host" + } + } + } + } + } + }, + "/host/del/{id}": { + "get": { + "description": "Remove a host from the database by its unique ID", + "produces": [ + "application/json" + ], + "tags": [ + "hosts" + ], + "summary": "Delete host", + "parameters": [ + { + "type": "string", + "description": "Host ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } + }, + "/host/{id}": { + "get": { + "description": "Retrieve detailed information about a host by its unique ID", + "produces": [ + "application/json" + ], + "tags": [ + "hosts" + ], + "summary": "Get host by ID", + "parameters": [ + { + "type": "string", + "description": "Host ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.Host" + } + } + } + } + }, + "/notify_test": { + "get": { + "description": "Trigger a test notification to verify notification settings", + "produces": [ + "application/json" + ], + "tags": [ + "system" + ], + "summary": "Send test notification", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } + }, + "/port/{addr}/{port}": { + "get": { + "description": "Check whether a given TCP port on an address is open or closed", + "produces": [ + "application/json" + ], + "tags": [ + "network" + ], + "summary": "Check port state", + "parameters": [ + { + "type": "string", + "description": "IP address or hostname", + "name": "addr", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Port number", + "name": "port", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "true if open, false if closed", + "schema": { + "type": "boolean" + } + } + } + } + }, + "/status/{iface}": { + "get": { + "description": "Retrieve summary statistics of hosts, optionally filtered by interface", + "produces": [ + "application/json" + ], + "tags": [ + "system" + ], + "summary": "Get network status", + "parameters": [ + { + "type": "string", + "description": "Interface name (omit for all interfaces)", + "name": "iface", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.Stat" + } + } + } + } + }, + "/version": { + "get": { + "description": "Returns the current running version of the application", + "produces": [ + "application/json" + ], + "tags": [ + "system" + ], + "summary": "Get application version", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } + }, + "/wol/{mac}": { + "get": { + "description": "Send a magic packet to wake up a host by its MAC address", + "produces": [ + "application/json" + ], + "tags": [ + "network" + ], + "summary": "Send Wake-on-LAN packet", + "parameters": [ + { + "type": "string", + "description": "MAC address of the host", + "name": "mac", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "true if sent successfully", + "schema": { + "type": "boolean" + } + } + } + } + } + }, + "definitions": { + "models.Conf": { + "type": "object", + "properties": { + "arpArgs": { + "type": "string" + }, + "arpStrs": { + "type": "array", + "items": { + "type": "string" + } + }, + "color": { + "type": "string" + }, + "confPath": { + "type": "string" + }, + "dbpath": { + "type": "string" + }, + "dirPath": { + "type": "string" + }, + "host": { + "type": "string" + }, + "ifaces": { + "type": "string" + }, + "influxAddr": { + "type": "string" + }, + "influxBucket": { + "type": "string" + }, + "influxEnable": { + "description": "InfluxDB", + "type": "boolean" + }, + "influxOrg": { + "type": "string" + }, + "influxSkipTLS": { + "type": "boolean" + }, + "influxToken": { + "type": "string" + }, + "logLevel": { + "type": "string" + }, + "nodePath": { + "type": "string" + }, + "pgconnect": { + "type": "string" + }, + "port": { + "type": "string" + }, + "prometheusEnable": { + "description": "Prometheus", + "type": "boolean" + }, + "shoutURL": { + "type": "string" + }, + "theme": { + "type": "string" + }, + "timeout": { + "type": "integer" + }, + "trimHist": { + "type": "integer" + }, + "useDB": { + "description": "PostgreSQL", + "type": "string" + }, + "version": { + "type": "string" + } + } + }, + "models.Host": { + "type": "object", + "properties": { + "date": { + "type": "string" + }, + "dns": { + "type": "string" + }, + "hw": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "iface": { + "type": "string" + }, + "ip": { + "type": "string" + }, + "known": { + "type": "integer" + }, + "mac": { + "type": "string" + }, + "name": { + "type": "string" + }, + "now": { + "type": "integer" + } + } + }, + "models.Stat": { + "type": "object", + "properties": { + "known": { + "type": "integer" + }, + "offline": { + "type": "integer" + }, + "online": { + "type": "integer" + }, + "total": { + "type": "integer" + }, + "unknown": { + "type": "integer" + } + } + } + } +} \ No newline at end of file diff --git a/backend/docs/swagger.yaml b/backend/docs/swagger.yaml new file mode 100644 index 0000000..5fe97a6 --- /dev/null +++ b/backend/docs/swagger.yaml @@ -0,0 +1,363 @@ +basePath: /api/ +definitions: + models.Conf: + properties: + arpArgs: + type: string + arpStrs: + items: + type: string + type: array + color: + type: string + confPath: + type: string + dbpath: + type: string + dirPath: + type: string + host: + type: string + ifaces: + type: string + influxAddr: + type: string + influxBucket: + type: string + influxEnable: + description: InfluxDB + type: boolean + influxOrg: + type: string + influxSkipTLS: + type: boolean + influxToken: + type: string + logLevel: + type: string + nodePath: + type: string + pgconnect: + type: string + port: + type: string + prometheusEnable: + description: Prometheus + type: boolean + shoutURL: + type: string + theme: + type: string + timeout: + type: integer + trimHist: + type: integer + useDB: + description: PostgreSQL + type: string + version: + type: string + type: object + models.Host: + properties: + date: + type: string + dns: + type: string + hw: + type: string + id: + type: integer + iface: + type: string + ip: + type: string + known: + type: integer + mac: + type: string + name: + type: string + now: + type: integer + type: object + models.Stat: + properties: + known: + type: integer + offline: + type: integer + online: + type: integer + total: + type: integer + unknown: + type: integer + type: object +info: + contact: + url: https://github.com/aceberg/WatchYourLAN + description: Lightweight network IP scanner written in Go + license: + name: MIT + url: https://opensource.org/licenses/MIT + title: WatchYourLAN API + version: "0.1" +paths: + /all: + get: + description: Retrieve all hosts from the database + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/models.Host' + type: array + summary: Get all hosts + tags: + - hosts + /config: + get: + description: Returns the current configuration used by the app + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.Conf' + summary: Get application configuration + tags: + - system + /edit/{id}/{name}/{known}: + get: + description: Update a host's name and optionally toggle its "known" status + parameters: + - description: Host ID + in: path + name: id + required: true + type: string + - description: New name for the host + in: path + name: name + required: true + type: string + - description: Pass 'toggle' to flip the known/unknown status + in: path + name: known + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + summary: Edit host + tags: + - hosts + /history: + get: + description: Retrieve the complete history of all hosts. Not recommended, the + output can be a lot + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/models.Host' + type: array + summary: Get full history + tags: + - history + /history/{mac}: + get: + description: Retrieve the latest history entries for a specific host by MAC + address + parameters: + - description: MAC address of the host + in: path + name: mac + required: true + type: string + - description: Number of history entries to return + in: query + name: num + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/models.Host' + type: array + summary: Get history by MAC + tags: + - history + /history/{mac}/{date}: + get: + description: |- + Retrieve history for a specific host on a given date + The date format is flexible and can be: + - Year only: `2025` + - Year + month: `2025-09` + - Full date: `2025-09-06` + - Full timestamp: `2025-09-06 00:58:26` + parameters: + - description: MAC address of the host + in: path + name: mac + required: true + type: string + - description: Date filter (supports YYYY, YYYY-MM, YYYY-MM-DD, YYYY-MM-DD HH:mm:ss) + in: path + name: date + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/models.Host' + type: array + summary: Get history by date + tags: + - history + /host/{id}: + get: + description: Retrieve detailed information about a host by its unique ID + parameters: + - description: Host ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.Host' + summary: Get host by ID + tags: + - hosts + /host/del/{id}: + get: + description: Remove a host from the database by its unique ID + parameters: + - description: Host ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + summary: Delete host + tags: + - hosts + /notify_test: + get: + description: Trigger a test notification to verify notification settings + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + summary: Send test notification + tags: + - system + /port/{addr}/{port}: + get: + description: Check whether a given TCP port on an address is open or closed + parameters: + - description: IP address or hostname + in: path + name: addr + required: true + type: string + - description: Port number + in: path + name: port + required: true + type: string + produces: + - application/json + responses: + "200": + description: true if open, false if closed + schema: + type: boolean + summary: Check port state + tags: + - network + /status/{iface}: + get: + description: Retrieve summary statistics of hosts, optionally filtered by interface + parameters: + - description: Interface name (omit for all interfaces) + in: path + name: iface + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.Stat' + summary: Get network status + tags: + - system + /version: + get: + description: Returns the current running version of the application + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + summary: Get application version + tags: + - system + /wol/{mac}: + get: + description: Send a magic packet to wake up a host by its MAC address + parameters: + - description: MAC address of the host + in: path + name: mac + required: true + type: string + produces: + - application/json + responses: + "200": + description: true if sent successfully + schema: + type: boolean + summary: Send Wake-on-LAN packet + tags: + - network +swagger: "2.0" diff --git a/backend/go.mod b/backend/go.mod index cc41fcd..c3b6053 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -10,11 +10,17 @@ require ( github.com/nicholas-fedor/shoutrrr v0.8.18 github.com/prometheus/client_golang v1.23.1 github.com/spf13/viper v1.20.1 + github.com/swaggo/files v1.0.1 + github.com/swaggo/gin-swagger v1.6.1 + github.com/swaggo/swag v1.16.6 gorm.io/driver/postgres v1.6.0 gorm.io/gorm v1.30.3 ) require ( + github.com/KyleBanks/depth v1.2.1 // indirect + github.com/PuerkitoBio/purell v1.1.1 // indirect + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bytedance/sonic v1.11.6 // indirect @@ -27,6 +33,10 @@ require ( github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.19.6 // indirect + github.com/go-openapi/spec v0.20.4 // indirect + github.com/go-openapi/swag v0.19.15 // 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 @@ -41,9 +51,11 @@ require ( github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect + github.com/josharian/intern v1.0.0 // 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/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -67,10 +79,12 @@ require ( golang.org/x/arch v0.8.0 // indirect golang.org/x/crypto v0.41.0 // indirect golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect + golang.org/x/mod v0.27.0 // indirect golang.org/x/net v0.43.0 // indirect golang.org/x/sync v0.16.0 // indirect golang.org/x/sys v0.35.0 // indirect golang.org/x/text v0.28.0 // indirect + golang.org/x/tools v0.36.0 // indirect google.golang.org/protobuf v1.36.8 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/backend/go.sum b/backend/go.sum index 5b03f71..1bc50c5 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -1,5 +1,11 @@ +github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= +github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= github.com/aceberg/gorm-sqlite v1.6.0 h1:I1c4uqC82Uc7JVt2+cjushdvQogN/C1I3XOiyn3yoUI= github.com/aceberg/gorm-sqlite v1.6.0/go.mod h1:3g//0BOzaOv9dO8G9j7JZOJA6qIURpPaht9oDY7LKpA= @@ -18,6 +24,7 @@ github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/ 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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -31,12 +38,24 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= 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/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= +github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk= 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.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= +github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= 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= @@ -78,6 +97,8 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 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/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= @@ -87,8 +108,11 @@ github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02 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.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= @@ -97,6 +121,11 @@ 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/linde12/gowol v0.0.0-20180926075039-797e4d01634c h1:QRJTb9zWXQL+yUajUqbp+VLtN+DQaYRloOxNwylsuVc= github.com/linde12/gowol v0.0.0-20180926075039-797e4d01634c/go.mod h1:YeHfx3xIWda3noSterlj6d3+PdRRCTRox269+zhLmbM= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -112,6 +141,7 @@ github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdh github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/nicholas-fedor/shoutrrr v0.8.18 h1:xYVtx2ipcEoLJFWViRpndC8zuTSRbhPEehsTkEBO4NQ= github.com/nicholas-fedor/shoutrrr v0.8.18/go.mod h1:RYx646RI7DazdYQo49IWCuMeqq+D5zC3//F6I+qhCgY= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/oapi-codegen/runtime v1.0.0 h1:P4rqFX5fMFWqRzY9M/3YF9+aPSPPB06IzP2P7oOxrWo= github.com/oapi-codegen/runtime v1.0.0/go.mod h1:LmCUMQuPB4M/nLXilQXhHw+BLZdDb18B34OO356yJ/A= github.com/onsi/ginkgo/v2 v2.25.2 h1:hepmgwx1D+llZleKQDMEvy8vIlCxMGt7W5ZxDjIEhsw= @@ -151,6 +181,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ 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/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 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= @@ -159,10 +190,17 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= +github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg= +github.com/swaggo/gin-swagger v1.6.1 h1:Ri06G4gc9N4t4k8hekMigJ9zKTFSlqj/9paAQCQs7cY= +github.com/swaggo/gin-swagger v1.6.1/go.mod h1:LQ+hJStHakCWRiK/YNYtJOu4mR2FP+pxLnILT/qNiTw= +github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI= +github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg= 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= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -172,32 +210,64 @@ go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= 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.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM= golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= -golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= -golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= +golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/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= gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4= diff --git a/backend/internal/api/api-history.go b/backend/internal/api/api-history.go new file mode 100644 index 0000000..6bfc736 --- /dev/null +++ b/backend/internal/api/api-history.go @@ -0,0 +1,60 @@ +package api + +import ( + "net/http" + "strconv" + + "github.com/gin-gonic/gin" + + "github.com/aceberg/WatchYourLAN/internal/gdb" +) + +// getHistory godoc +// @Summary Get full history +// @Description Retrieve the complete history of all hosts. Not recommended, the output can be a lot +// @Tags history +// @Produce json +// @Success 200 {array} models.Host +// @Router /history [get] +func getHistory(c *gin.Context) { + hosts, _ := gdb.Select("history") + c.IndentedJSON(http.StatusOK, hosts) +} + +// getHistoryByMAC godoc +// @Summary Get history by MAC +// @Description Retrieve the latest history entries for a specific host by MAC address +// @Tags history +// @Produce json +// @Param mac path string true "MAC address of the host" +// @Param num query int true "Number of history entries to return" +// @Success 200 {array} models.Host +// @Router /history/{mac} [get] +func getHistoryByMAC(c *gin.Context) { + mac := c.Param("mac") + numStr := c.Query("num") + num, _ := strconv.Atoi(numStr) + hosts := gdb.SelectLatest(mac, num) + c.IndentedJSON(http.StatusOK, hosts) +} + +// getHistoryByDate godoc +// @Summary Get history by date +// @Description Retrieve history for a specific host on a given date +// @Description The date format is flexible and can be: +// @Description - Year only: `2025` +// @Description - Year + month: `2025-09` +// @Description - Full date: `2025-09-06` +// @Description - Full timestamp: `2025-09-06 00:58:26` +// @Tags history +// @Produce json +// @Param mac path string true "MAC address of the host" +// @Param date path string true "Date filter (supports YYYY, YYYY-MM, YYYY-MM-DD, YYYY-MM-DD HH:mm:ss)" +// @Success 200 {array} models.Host +// @Router /history/{mac}/{date} [get] +func getHistoryByDate(c *gin.Context) { + mac := c.Param("mac") + date := c.Param("date") + hosts := gdb.SelectByDate(mac, date) + c.IndentedJSON(http.StatusOK, hosts) +} diff --git a/backend/internal/api/api-hosts.go b/backend/internal/api/api-hosts.go new file mode 100644 index 0000000..28a1e36 --- /dev/null +++ b/backend/internal/api/api-hosts.go @@ -0,0 +1,83 @@ +package api + +import ( + "log/slog" + "net/http" + + "github.com/gin-gonic/gin" + + "github.com/aceberg/WatchYourLAN/internal/check" + "github.com/aceberg/WatchYourLAN/internal/gdb" +) + +// getAllHosts godoc +// @Summary Get all hosts +// @Description Retrieve all hosts from the database +// @Tags hosts +// @Produce json +// @Success 200 {array} models.Host +// @Router /all [get] +func getAllHosts(c *gin.Context) { + allHosts, _ := gdb.Select("now") + c.IndentedJSON(http.StatusOK, allHosts) +} + +// getHost godoc +// @Summary Get host by ID +// @Description Retrieve detailed information about a host by its unique ID +// @Tags hosts +// @Produce json +// @Param id path string true "Host ID" +// @Success 200 {object} models.Host +// @Router /host/{id} [get] +func getHost(c *gin.Context) { + idStr := c.Param("id") + host := getHostByID(idStr) // functions.go + _, host.DNS = check.DNS(host) + c.IndentedJSON(http.StatusOK, host) +} + +// delHost godoc +// @Summary Delete host +// @Description Remove a host from the database by its unique ID +// @Tags hosts +// @Produce json +// @Param id path string true "Host ID" +// @Success 200 {string} string "OK" +// @Router /host/del/{id} [get] +func delHost(c *gin.Context) { + idStr := c.Param("id") + host := getHostByID(idStr) // functions.go + gdb.Delete("now", host.ID) + slog.Info("Deleting from DB", "host", host) + c.IndentedJSON(http.StatusOK, "OK") +} + +// editHost godoc +// @Summary Edit host +// @Description Update a host's name and optionally toggle its "known" status +// @Tags hosts +// @Produce json +// @Param id path string true "Host ID" +// @Param name path string true "New name for the host" +// @Param known path string false "Pass 'toggle' to flip the known/unknown status" +// @Success 200 {string} string "OK" +// @Router /edit/{id}/{name}/{known} [get] +func editHost(c *gin.Context) { + + idStr := c.Param("id") + name := c.Param("name") + toggleKnown := c.Param("known") + + host := getHostByID(idStr) // functions.go + + host.Name = name + + if toggleKnown == "/toggle" { + host.Known = 1 - host.Known + } + + gdb.Update("now", host) + + c.IndentedJSON(http.StatusOK, "OK") +} diff --git a/backend/internal/api/api-network.go b/backend/internal/api/api-network.go new file mode 100644 index 0000000..abef5cb --- /dev/null +++ b/backend/internal/api/api-network.go @@ -0,0 +1,51 @@ +package api + +import ( + "log/slog" + "net/http" + + "github.com/gin-gonic/gin" + "github.com/linde12/gowol" + + "github.com/aceberg/WatchYourLAN/internal/check" + "github.com/aceberg/WatchYourLAN/internal/portscan" +) + +// getPortState godoc +// @Summary Check port state +// @Description Check whether a given TCP port on an address is open or closed +// @Tags network +// @Produce json +// @Param addr path string true "IP address or hostname" +// @Param port path string true "Port number" +// @Success 200 {boolean} bool "true if open, false if closed" +// @Router /port/{addr}/{port} [get] +func getPortState(c *gin.Context) { + addr := c.Param("addr") + port := c.Param("port") + state := portscan.IsOpen(addr, port) + c.IndentedJSON(http.StatusOK, state) +} + +// sendWOL godoc +// @Summary Send Wake-on-LAN packet +// @Description Send a magic packet to wake up a host by its MAC address +// @Tags network +// @Produce json +// @Param mac path string true "MAC address of the host" +// @Success 200 {boolean} bool "true if sent successfully" +// @Router /wol/{mac} [get] +func sendWOL(c *gin.Context) { + + mac := c.Param("mac") + + packet, err := gowol.NewMagicPacket(mac) + + if !check.IfError(err) { + err = packet.Send("255.255.255.255") + + slog.Info("Wake-on-LAN: " + mac) + } + + c.IndentedJSON(http.StatusOK, !check.IfError(err)) +} diff --git a/backend/internal/api/api-system.go b/backend/internal/api/api-system.go new file mode 100644 index 0000000..581bb8e --- /dev/null +++ b/backend/internal/api/api-system.go @@ -0,0 +1,91 @@ +package api + +import ( + "net/http" + + "github.com/gin-gonic/gin" + + "github.com/aceberg/WatchYourLAN/internal/conf" + "github.com/aceberg/WatchYourLAN/internal/gdb" + "github.com/aceberg/WatchYourLAN/internal/models" + "github.com/aceberg/WatchYourLAN/internal/notify" +) + +// getVersion godoc +// @Summary Get application version +// @Description Returns the current running version of the application +// @Tags system +// @Produce json +// @Success 200 {string} string +// @Router /version [get] +func getVersion(c *gin.Context) { + c.IndentedJSON(http.StatusOK, conf.AppConfig.Version) +} + +// getConfig godoc +// @Summary Get application configuration +// @Description Returns the current configuration used by the app +// @Tags system +// @Produce json +// @Success 200 {object} models.Conf +// @Router /config [get] +func getConfig(c *gin.Context) { + c.IndentedJSON(http.StatusOK, conf.AppConfig) +} + +// notifyTest godoc +// @Summary Send test notification +// @Description Trigger a test notification to verify notification settings +// @Tags system +// @Produce json +// @Success 200 {string} string "OK" +// @Router /notify_test [get] +func notifyTest(c *gin.Context) { + notify.Test() + c.Status(http.StatusOK) +} + +// getStatus godoc +// @Summary Get network status +// @Description Retrieve summary statistics of hosts, optionally filtered by interface +// @Tags system +// @Produce json +// @Param iface path string false "Interface name (omit for all interfaces)" +// @Success 200 {object} models.Stat +// @Router /status/{iface} [get] +func getStatus(c *gin.Context) { + var status models.Stat + var searchHosts []models.Host + + allHosts, _ := gdb.Select("now") + + iface := c.Param("iface") + iface = iface[1:] + + if iface != "" && iface != "undefined" { + for _, host := range allHosts { + if iface == host.Iface { + searchHosts = append(searchHosts, host) + } + } + } else { + searchHosts = allHosts + } + + for _, host := range searchHosts { + status.Total = status.Total + 1 + + if host.Known > 0 { + status.Known = status.Known + 1 + } else { + status.Unknown = status.Unknown + 1 + } + if host.Now > 0 { + status.Online = status.Online + 1 + } else { + status.Offline = status.Offline + 1 + } + } + + c.IndentedJSON(http.StatusOK, status) +} diff --git a/backend/internal/api/api.go b/backend/internal/api/api.go deleted file mode 100644 index 06e709d..0000000 --- a/backend/internal/api/api.go +++ /dev/null @@ -1,170 +0,0 @@ -package api - -import ( - "log/slog" - "net/http" - "strconv" - - "github.com/gin-gonic/gin" - "github.com/linde12/gowol" - - "github.com/aceberg/WatchYourLAN/internal/check" - "github.com/aceberg/WatchYourLAN/internal/conf" - "github.com/aceberg/WatchYourLAN/internal/gdb" - "github.com/aceberg/WatchYourLAN/internal/models" - "github.com/aceberg/WatchYourLAN/internal/notify" - "github.com/aceberg/WatchYourLAN/internal/portscan" -) - -func getAllHosts(c *gin.Context) { - - allHosts, _ := gdb.Select("now") - - c.IndentedJSON(http.StatusOK, allHosts) -} - -func getVersion(c *gin.Context) { - - c.IndentedJSON(http.StatusOK, conf.AppConfig.Version) -} - -func getConfig(c *gin.Context) { - - c.IndentedJSON(http.StatusOK, conf.AppConfig) -} - -func getHistory(c *gin.Context) { - - hosts, _ := gdb.Select("history") - - c.IndentedJSON(http.StatusOK, hosts) -} - -func getHistoryByMAC(c *gin.Context) { - - mac := c.Param("mac") - numStr := c.Query("num") - num, _ := strconv.Atoi(numStr) - - hosts := gdb.SelectLatest(mac, num) - - 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") - - host := getHostByID(idStr) // functions.go - _, host.DNS = check.DNS(host) - - c.IndentedJSON(http.StatusOK, host) -} - -func delHost(c *gin.Context) { - - idStr := c.Param("id") - host := getHostByID(idStr) // functions.go - gdb.Delete("now", host.ID) - - slog.Info("Deleting from DB", "host", host) - - c.IndentedJSON(http.StatusOK, "OK") -} - -func getPortState(c *gin.Context) { - - addr := c.Param("addr") - port := c.Param("port") - state := portscan.IsOpen(addr, port) - - c.IndentedJSON(http.StatusOK, state) -} - -func editHost(c *gin.Context) { - - idStr := c.Param("id") - name := c.Param("name") - toggleKnown := c.Param("known") - - host := getHostByID(idStr) // functions.go - - host.Name = name - - if toggleKnown == "/toggle" { - host.Known = 1 - host.Known - } - - gdb.Update("now", host) - - c.IndentedJSON(http.StatusOK, "OK") -} - -func notifyTest(c *gin.Context) { - - notify.Test() - - c.Status(http.StatusOK) -} - -func getStatus(c *gin.Context) { - var status models.Stat - var searchHosts []models.Host - - allHosts, _ := gdb.Select("now") - - iface := c.Param("iface") - iface = iface[1:] - - if iface != "" { - for _, host := range allHosts { - if iface == host.Iface { - searchHosts = append(searchHosts, host) - } - } - } else { - searchHosts = allHosts - } - - for _, host := range searchHosts { - status.Total = status.Total + 1 - - if host.Known > 0 { - status.Known = status.Known + 1 - } else { - status.Unknown = status.Unknown + 1 - } - if host.Now > 0 { - status.Online = status.Online + 1 - } else { - status.Offline = status.Offline + 1 - } - } - - c.IndentedJSON(http.StatusOK, status) -} - -func sendWOL(c *gin.Context) { - - mac := c.Param("mac") - - packet, err := gowol.NewMagicPacket(mac) - - if !check.IfError(err) { - err = packet.Send("255.255.255.255") - - slog.Info("Wake-on-LAN: " + mac) - } - - c.IndentedJSON(http.StatusOK, !check.IfError(err)) -} diff --git a/backend/internal/api/routes.go b/backend/internal/api/routes.go index 1e1381a..6baede5 100644 --- a/backend/internal/api/routes.go +++ b/backend/internal/api/routes.go @@ -2,27 +2,38 @@ package api import ( "github.com/gin-gonic/gin" + + swaggerFiles "github.com/swaggo/files" + ginSwagger "github.com/swaggo/gin-swagger" ) // Routes - start API routes func Routes(router *gin.Engine) { - 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.GET("/api/wol/:mac", sendWOL) // api.go + r0 := router.Group("/api") + { + r0.GET("/all", getAllHosts) // api-hosts.go + 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 - router.POST("/api/config/", saveConfigHandler) // config.go - router.POST("/api/config_settings/", saveSettingsHandler) // config.go - router.POST("/api/config_influx/", saveInfluxHandler) // config.go - router.POST("/api/config_prometheus/", savePrometheusHandler) // config.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("/history", getHistory) // api-history.go + r0.GET("/history/:mac", getHistoryByMAC) // api-history.go + r0.GET("/history/:mac/:date", getHistoryByDate) // api-history.go + + r0.GET("/port/:addr/:port", getPortState) // api-network.go + r0.GET("/wol/:mac", sendWOL) // api-network.go + + r0.POST("/config/", saveConfigHandler) // config.go + r0.POST("/config_settings/", saveSettingsHandler) // config.go + r0.POST("/config_influx/", saveInfluxHandler) // config.go + r0.POST("/config_prometheus/", savePrometheusHandler) // config.go + } + + router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) } diff --git a/backend/internal/web/webgui.go b/backend/internal/web/webgui.go index 7f14c5a..2fc6c07 100644 --- a/backend/internal/web/webgui.go +++ b/backend/internal/web/webgui.go @@ -42,6 +42,7 @@ func Gui() { "\n Default DB: " + conf.AppConfig.UseDB + "\n Log level: " + conf.AppConfig.LogLevel + "\n Web GUI: http://" + address + + "\n Swagger UI: http://" + address + "/swagger/index.html" + "\n=================================== " + colorReset) gin.SetMode(gin.ReleaseMode)