From 52972b2de2eef6db08ec67e1540b15760bce58e5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 May 2026 03:08:49 +0000 Subject: [PATCH 01/59] chore(deps): bump @angular-eslint/schematics in /webapp/frontend Bumps [@angular-eslint/schematics](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/schematics) from 21.3.1 to 21.4.0. - [Release notes](https://github.com/angular-eslint/angular-eslint/releases) - [Changelog](https://github.com/angular-eslint/angular-eslint/blob/main/packages/schematics/CHANGELOG.md) - [Commits](https://github.com/angular-eslint/angular-eslint/commits/v21.4.0/packages/schematics) --- updated-dependencies: - dependency-name: "@angular-eslint/schematics" dependency-version: 21.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- webapp/frontend/package-lock.json | 61 ++++++++++++++++++++----------- webapp/frontend/package.json | 2 +- 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/webapp/frontend/package-lock.json b/webapp/frontend/package-lock.json index 7ded9984c..4047bc988 100644 --- a/webapp/frontend/package-lock.json +++ b/webapp/frontend/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.1", "license": "https://themeforest.net/licenses/standard", "dependencies": { - "@angular-eslint/schematics": "^21.3.1", + "@angular-eslint/schematics": "^21.4.0", "@angular/animations": "^21.2.12", "@angular/cdk": "^21.2.10", "@angular/common": "^21.2.12", @@ -414,16 +414,17 @@ "version": "21.3.1", "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-21.3.1.tgz", "integrity": "sha512-jjbnJPUXQeQBJ8RM+ahlbt4GH2emVN8JvG3AhFbPci1FrqXi9cOOfkbwLmvpoyTli4LF8gy7g4ctFqnlRgqryw==", + "dev": true, "license": "MIT" }, "node_modules/@angular-eslint/eslint-plugin": { - "version": "21.3.1", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-21.3.1.tgz", - "integrity": "sha512-08NNTxwawRLTWPLl8dg1BnXMwimx93y4wMEwx2aWQpJbIt4pmNvwJzd+NgoD/Ag2VdLS/gOMadhJH5fgaYKsPQ==", + "version": "21.4.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-21.4.0.tgz", + "integrity": "sha512-mow2DMj+xBvGl5t7jzC34R8YfbHbaGNyCNFzpovtl9qc0JbuqLyg6htmt8xb05f8ZjATOr4nz0ESt6HV4c51hw==", "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "21.3.1", - "@angular-eslint/utils": "21.3.1", + "@angular-eslint/bundled-angular-compiler": "21.4.0", + "@angular-eslint/utils": "21.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { @@ -433,34 +434,46 @@ } }, "node_modules/@angular-eslint/eslint-plugin-template": { - "version": "21.3.1", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-21.3.1.tgz", - "integrity": "sha512-ndPWJodkcEOu2PVUxlUwyz4D2u3r9KO7veWmStVNOLeNrICJA+nQvrz2BWCu0l48rO0K5ezsy0JFcQDVwE/5mw==", + "version": "21.4.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-21.4.0.tgz", + "integrity": "sha512-sJEHx2WYnvOgPpzP1eHnUdRS06zgKmRxbiIR0JiCcaSen5iv1HlsMieXy//FS9TtNW+abHOy4UtDuGuSPflPFA==", "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "21.3.1", - "@angular-eslint/utils": "21.3.1", + "@angular-eslint/bundled-angular-compiler": "21.4.0", + "@angular-eslint/utils": "21.4.0", "aria-query": "5.3.2", "axobject-query": "4.1.0" }, "peerDependencies": { - "@angular-eslint/template-parser": "21.3.1", + "@angular-eslint/template-parser": "21.4.0", "@typescript-eslint/types": "^7.11.0 || ^8.0.0", "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": "*" } }, + "node_modules/@angular-eslint/eslint-plugin-template/node_modules/@angular-eslint/bundled-angular-compiler": { + "version": "21.4.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-21.4.0.tgz", + "integrity": "sha512-/3H4BPbQ1BHJkkrUsfusZtmHc+qiFWBBZ9UDPWah4xZMjflexOK9U4GYeH7nMjcuyqFnIlMMeJJNwNLGt/hmdg==", + "license": "MIT" + }, + "node_modules/@angular-eslint/eslint-plugin/node_modules/@angular-eslint/bundled-angular-compiler": { + "version": "21.4.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-21.4.0.tgz", + "integrity": "sha512-/3H4BPbQ1BHJkkrUsfusZtmHc+qiFWBBZ9UDPWah4xZMjflexOK9U4GYeH7nMjcuyqFnIlMMeJJNwNLGt/hmdg==", + "license": "MIT" + }, "node_modules/@angular-eslint/schematics": { - "version": "21.3.1", - "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-21.3.1.tgz", - "integrity": "sha512-1U2u4ZsZvwT30aXRLsIJf6tULIiioo9BtASNsldpYecU3/m/1+F61lCYG79qt7YWbif9KABPYZlFTJUFGN8HWA==", + "version": "21.4.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-21.4.0.tgz", + "integrity": "sha512-crD6Hfxs7x5bN9FCqTZI7uVSiGvprfCS3MCPOpyIQl87bRr/9aNhnicJ3ROUHv+2A713BgPHIgiCII/bxzrfPw==", "license": "MIT", "dependencies": { "@angular-devkit/core": ">= 21.0.0 < 22.0.0", "@angular-devkit/schematics": ">= 21.0.0 < 22.0.0", - "@angular-eslint/eslint-plugin": "21.3.1", - "@angular-eslint/eslint-plugin-template": "21.3.1", + "@angular-eslint/eslint-plugin": "21.4.0", + "@angular-eslint/eslint-plugin-template": "21.4.0", "ignore": "7.0.5", "semver": "7.7.4", "strip-json-comments": "3.1.1" @@ -523,12 +536,12 @@ } }, "node_modules/@angular-eslint/utils": { - "version": "21.3.1", - "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-21.3.1.tgz", - "integrity": "sha512-Q3SGA1/36phZhmsp1mYrKzp/jcmqofRr861MYn46FaWIKSYXBYRzl+H3FIJKBu5CE36Bggu6hbNpwGPuUp+MCg==", + "version": "21.4.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-21.4.0.tgz", + "integrity": "sha512-7pi+Ga7QmdH5Ig/diau6fR5L4yubgKr9TOjdCg7OeuE/zo0O3osTCNT6JOodzS/iQM1kSCJFDoIBKFeUOttiNw==", "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "21.3.1" + "@angular-eslint/bundled-angular-compiler": "21.4.0" }, "peerDependencies": { "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", @@ -536,6 +549,12 @@ "typescript": "*" } }, + "node_modules/@angular-eslint/utils/node_modules/@angular-eslint/bundled-angular-compiler": { + "version": "21.4.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-21.4.0.tgz", + "integrity": "sha512-/3H4BPbQ1BHJkkrUsfusZtmHc+qiFWBBZ9UDPWah4xZMjflexOK9U4GYeH7nMjcuyqFnIlMMeJJNwNLGt/hmdg==", + "license": "MIT" + }, "node_modules/@angular/animations": { "version": "21.2.12", "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-21.2.12.tgz", diff --git a/webapp/frontend/package.json b/webapp/frontend/package.json index aedc88e97..25c279a5c 100644 --- a/webapp/frontend/package.json +++ b/webapp/frontend/package.json @@ -21,7 +21,7 @@ }, "private": true, "dependencies": { - "@angular-eslint/schematics": "^21.3.1", + "@angular-eslint/schematics": "^21.4.0", "@angular/animations": "^21.2.12", "@angular/cdk": "^21.2.10", "@angular/common": "^21.2.12", From c461109d429f99bf8a6ab362e3fac3d7764957c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 May 2026 03:08:55 +0000 Subject: [PATCH 02/59] chore(deps): bump ng-apexcharts from 2.3.0 to 2.4.0 in /webapp/frontend Bumps [ng-apexcharts](https://github.com/apexcharts/ng-apexcharts) from 2.3.0 to 2.4.0. - [Release notes](https://github.com/apexcharts/ng-apexcharts/releases) - [Changelog](https://github.com/apexcharts/ng-apexcharts/blob/master/CHANGELOG.md) - [Commits](https://github.com/apexcharts/ng-apexcharts/commits/v2.4.0) --- updated-dependencies: - dependency-name: ng-apexcharts dependency-version: 2.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- webapp/frontend/package-lock.json | 8 ++++---- webapp/frontend/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/webapp/frontend/package-lock.json b/webapp/frontend/package-lock.json index 7ded9984c..ed2b70d48 100644 --- a/webapp/frontend/package-lock.json +++ b/webapp/frontend/package-lock.json @@ -27,7 +27,7 @@ "humanize-duration": "^3.27.3", "lodash": "^4.18.1", "marked": "^17.0.1", - "ng-apexcharts": "^2.3.0", + "ng-apexcharts": "^2.4.0", "perfect-scrollbar": "^1.5.5", "rrule": "^2.7.1", "rxjs": "^7.5.7", @@ -9425,9 +9425,9 @@ } }, "node_modules/ng-apexcharts": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/ng-apexcharts/-/ng-apexcharts-2.3.0.tgz", - "integrity": "sha512-7OGyZB/olPp1UE56Nqd20iAZb/A9sxW4rvdY2Hwy92z6U44C/+hGTbZcGgBv8wds2kRrm6uy4wJovMUw+knYGw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ng-apexcharts/-/ng-apexcharts-2.4.0.tgz", + "integrity": "sha512-bNXIOQrux9ZXnpm4kxbW8M6AA4ENAEOpR31ZoaeBkc0SrfOMw1nxRN5ORkStOQFVQF5Wf5qivAA9KIgU7Ol26A==", "dependencies": { "tslib": "^2.8.1" }, diff --git a/webapp/frontend/package.json b/webapp/frontend/package.json index aedc88e97..e77501407 100644 --- a/webapp/frontend/package.json +++ b/webapp/frontend/package.json @@ -39,7 +39,7 @@ "humanize-duration": "^3.27.3", "lodash": "^4.18.1", "marked": "^17.0.1", - "ng-apexcharts": "^2.3.0", + "ng-apexcharts": "^2.4.0", "perfect-scrollbar": "^1.5.5", "rrule": "^2.7.1", "rxjs": "^7.5.7", From d3cb84a7184f534dc75f73c5f0ea41c05b6eb3bd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 May 2026 03:09:02 +0000 Subject: [PATCH 03/59] chore(deps): bump eslint-plugin-prettier in /webapp/frontend Bumps [eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier) from 4.2.5 to 5.5.5. - [Release notes](https://github.com/prettier/eslint-plugin-prettier/releases) - [Changelog](https://github.com/prettier/eslint-plugin-prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/eslint-plugin-prettier/compare/v4.2.5...v5.5.5) --- updated-dependencies: - dependency-name: eslint-plugin-prettier dependency-version: 5.5.5 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- webapp/frontend/package-lock.json | 66 ++++++++++++++++++++++++------- webapp/frontend/package.json | 2 +- 2 files changed, 53 insertions(+), 15 deletions(-) diff --git a/webapp/frontend/package-lock.json b/webapp/frontend/package-lock.json index 7ded9984c..c3a627053 100644 --- a/webapp/frontend/package-lock.json +++ b/webapp/frontend/package-lock.json @@ -54,7 +54,7 @@ "apexcharts": "~5.11.0", "eslint": "^8.57.1", "eslint-config-prettier": "^8.10.2", - "eslint-plugin-prettier": "^4.2.5", + "eslint-plugin-prettier": "^5.5.5", "jasmine-core": "^4.5.0", "jasmine-spec-reporter": "^7.0.0", "karma": "^6.4.1", @@ -3905,6 +3905,19 @@ "license": "MIT", "optional": true }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.0.0-rc.4", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.4.tgz", @@ -6708,22 +6721,31 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.5.tgz", - "integrity": "sha512-9Ni+xgemM2IWLq6aXEpP2+V/V30GeA/46Ar629vcMqVPodFFWC9skHu/D1phvuqtS8bJCFnNf01/qcmqYEwNfg==", + "version": "5.5.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.5.tgz", + "integrity": "sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==", "dev": true, "license": "MIT", "dependencies": { - "prettier-linter-helpers": "^1.0.0" + "prettier-linter-helpers": "^1.0.1", + "synckit": "^0.11.12" }, "engines": { - "node": ">=12.0.0" + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" }, "peerDependencies": { - "eslint": ">=7.28.0", - "prettier": ">=2.0.0" + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" }, "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, "eslint-config-prettier": { "optional": true } @@ -7210,9 +7232,9 @@ "license": "MIT" }, "node_modules/fast-diff": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz", - "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", "dev": true, "license": "Apache-2.0" }, @@ -10376,9 +10398,9 @@ } }, "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz", + "integrity": "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==", "dev": true, "license": "MIT", "dependencies": { @@ -11557,6 +11579,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/synckit": { + "version": "0.11.12", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", + "integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.2.9" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, "node_modules/tailwindcss": { "version": "3.4.19", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", diff --git a/webapp/frontend/package.json b/webapp/frontend/package.json index aedc88e97..91b6215f3 100644 --- a/webapp/frontend/package.json +++ b/webapp/frontend/package.json @@ -66,7 +66,7 @@ "apexcharts": "~5.11.0", "eslint": "^8.57.1", "eslint-config-prettier": "^8.10.2", - "eslint-plugin-prettier": "^4.2.5", + "eslint-plugin-prettier": "^5.5.5", "jasmine-core": "^4.5.0", "jasmine-spec-reporter": "^7.0.0", "karma": "^6.4.1", From 999fe7270f9804a213bd43a43ed10dad62136077 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Jun 2026 02:18:49 +0000 Subject: [PATCH 04/59] chore(deps): bump github.com/go-gormigrate/gormigrate/v2 Bumps [github.com/go-gormigrate/gormigrate/v2](https://github.com/go-gormigrate/gormigrate) from 2.1.5 to 2.1.6. - [Release notes](https://github.com/go-gormigrate/gormigrate/releases) - [Changelog](https://github.com/go-gormigrate/gormigrate/blob/master/CHANGELOG.md) - [Commits](https://github.com/go-gormigrate/gormigrate/compare/v2.1.5...v2.1.6) --- updated-dependencies: - dependency-name: github.com/go-gormigrate/gormigrate/v2 dependency-version: 2.1.6 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 3e91ba418..4a55c5a08 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/fatih/color v1.19.0 github.com/gin-gonic/gin v1.12.0 github.com/glebarez/sqlite v1.11.0 - github.com/go-gormigrate/gormigrate/v2 v2.1.5 + github.com/go-gormigrate/gormigrate/v2 v2.1.6 github.com/go-pdf/fpdf v0.9.0 github.com/go-viper/mapstructure/v2 v2.5.0 github.com/golang-jwt/jwt/v5 v5.3.1 diff --git a/go.sum b/go.sum index df94f170d..479a09fac 100644 --- a/go.sum +++ b/go.sum @@ -45,8 +45,8 @@ github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9g github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k= github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw= github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ= -github.com/go-gormigrate/gormigrate/v2 v2.1.5 h1:1OyorA5LtdQw12cyJDEHuTrEV3GiXiIhS4/QTTa/SM8= -github.com/go-gormigrate/gormigrate/v2 v2.1.5/go.mod h1:mj9ekk/7CPF3VjopaFvWKN2v7fN3D9d3eEOAXRhi/+M= +github.com/go-gormigrate/gormigrate/v2 v2.1.6 h1:VtX+l1Stj2v5RGubVQk0LS/8EPGXR+ldcOyCmlmKoyg= +github.com/go-gormigrate/gormigrate/v2 v2.1.6/go.mod h1:PZpedQc4tWaxn6kvXicwhinh3L0seLpMc5ReKRX5id4= 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-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= From 2bd67aa8a44fad50e25e2196a84dc1da84cd6e75 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jun 2026 02:34:11 +0000 Subject: [PATCH 05/59] chore(deps): bump github.com/nicholas-fedor/shoutrrr Bumps [github.com/nicholas-fedor/shoutrrr](https://github.com/nicholas-fedor/shoutrrr) from 0.14.3 to 0.16.0. - [Release notes](https://github.com/nicholas-fedor/shoutrrr/releases) - [Changelog](https://github.com/nicholas-fedor/shoutrrr/blob/main/CHANGELOG.md) - [Commits](https://github.com/nicholas-fedor/shoutrrr/compare/v0.14.3...v0.16.0) --- updated-dependencies: - dependency-name: github.com/nicholas-fedor/shoutrrr dependency-version: 0.16.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 20 +++++++++--------- go.sum | 65 +++++++++++++++++++++++++++++----------------------------- 2 files changed, 42 insertions(+), 43 deletions(-) diff --git a/go.mod b/go.mod index 3e91ba418..dab16f62a 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/influxdata/influxdb-client-go/v2 v2.14.0 github.com/jaypipes/ghw v0.24.0 github.com/mitchellh/mapstructure v1.5.0 - github.com/nicholas-fedor/shoutrrr v0.14.3 + github.com/nicholas-fedor/shoutrrr v0.16.0 github.com/prometheus/client_golang v1.23.2 github.com/prometheus/client_model v0.6.2 github.com/robfig/cron/v3 v3.0.1 @@ -43,7 +43,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/eclipse/paho.golang v0.23.0 // indirect - github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/fsnotify/fsnotify v1.10.1 // indirect github.com/gabriel-vasile/mimetype v1.4.12 // indirect github.com/gin-contrib/sse v1.1.0 // indirect github.com/glebarez/go-sqlite v1.21.2 // indirect @@ -62,14 +62,14 @@ require ( github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/kvz/logstreamer v0.0.0-20201023134116-02d20f4338f5 // indirect github.com/leodido/go-urn v1.4.0 // indirect - github.com/mattn/go-colorable v0.1.14 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-colorable v0.1.15 // indirect + github.com/mattn/go-isatty v0.0.22 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/oapi-codegen/runtime v1.0.0 // indirect - github.com/pelletier/go-toml/v2 v2.3.0 // indirect + github.com/pelletier/go-toml/v2 v2.3.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/common v0.66.1 // indirect github.com/prometheus/procfs v0.16.1 // indirect @@ -90,11 +90,11 @@ require ( go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/arch v0.22.0 // indirect - golang.org/x/crypto v0.49.0 // indirect - golang.org/x/net v0.52.0 // indirect - golang.org/x/sys v0.42.0 // indirect - golang.org/x/term v0.41.0 // indirect - golang.org/x/text v0.35.0 // indirect + golang.org/x/crypto v0.51.0 // indirect + golang.org/x/net v0.55.0 // indirect + golang.org/x/sys v0.45.0 // indirect + golang.org/x/term v0.43.0 // indirect + golang.org/x/text v0.37.0 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index df94f170d..dfecc6f59 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -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/Masterminds/semver/v3 v3.5.0 h1:kQceYJfbupGfZOKZQg0kou0DgAKhzDg2NZPAwZ/2OOE= +github.com/Masterminds/semver/v3 v3.5.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= github.com/analogj/go-util v0.0.0-20190301173314-5295e364eb14 h1:wsrSjiqQtseStRIoLLxS4C5IEtXkazZVEPDHq8jW7r8= github.com/analogj/go-util v0.0.0-20190301173314-5295e364eb14/go.mod h1:lJQVqFKMV5/oDGYR2bra2OljcF3CvolAoyDRyOA4k4E= @@ -33,8 +33,8 @@ github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w= github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= -github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho= +github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= @@ -76,8 +76,8 @@ github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+Licev github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20260302011040-a15ffb7f9dcc h1:VBbFa1lDYWEeV5FZKUiYKYT0VxCp9twUmmaq9eb8sXw= -github.com/google/pprof v0.0.0-20260302011040-a15ffb7f9dcc/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= +github.com/google/pprof v0.0.0-20260507013755-92041b743c96 h1:YDDnaZ9afWajDboPMt9Vikqca/yWAX7KAxVzb4lJU1M= +github.com/google/pprof v0.0.0-20260507013755-92041b743c96/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= @@ -114,10 +114,10 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 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/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= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy0/jY= +github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4= +github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -129,16 +129,16 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/nicholas-fedor/shoutrrr v0.14.3 h1:aBX2iw9a7jl5wfHd3bi9LnS5ucoYIy6KcLH9XVF+gig= -github.com/nicholas-fedor/shoutrrr v0.14.3/go.mod h1:U7IywBkLpBV7rgn8iLbQ9/LklJG1gm24bFv5cXXsDKs= +github.com/nicholas-fedor/shoutrrr v0.16.0 h1:oUKfFZDM7CoENuZMsE/8sK/KbED92/urTWR0wv2BAq8= +github.com/nicholas-fedor/shoutrrr v0.16.0/go.mod h1:l0IveCVC6V2uyjxCeK22JTRlzoiQp2c0Dam/HE6OLBk= 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.28.1 h1:S4hj+HbZp40fNKuLUQOYLDgZLwNUVn19N3Atb98NCyI= -github.com/onsi/ginkgo/v2 v2.28.1/go.mod h1:CLtbVInNckU3/+gC8LzkGUb9oF+e8W8TdUsxPwvdOgE= -github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28= -github.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg= -github.com/pelletier/go-toml/v2 v2.3.0 h1:k59bC/lIZREW0/iVaQR8nDHxVq8OVlIzYCOJf421CaM= -github.com/pelletier/go-toml/v2 v2.3.0/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/onsi/ginkgo/v2 v2.29.0 h1:rfh+ZFjgJhYWRoIqVf3Uwx/W20yLrcrE2h2GmYVRaag= +github.com/onsi/ginkgo/v2 v2.29.0/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44= +github.com/onsi/gomega v1.41.0 h1:OwKp4pXNgVxf6sCplzYo794OFNuoL2q2SBMU5NSWOjA= +github.com/onsi/gomega v1.41.0/go.mod h1:M/Uqpu/8qTjtzCLUA2zJHX9Iilrau25x1PdoSRbWh5A= +github.com/pelletier/go-toml/v2 v2.3.1 h1:MYEvvGnQjeNkRF1qUuGolNtNExTDwct51yp7olPtrEc= +github.com/pelletier/go-toml/v2 v2.3.1/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= @@ -221,16 +221,16 @@ golang.org/x/arch v0.22.0 h1:c/Zle32i5ttqRXjdLyyHZESLD/bB90DCU1g9l/0YBDI= golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= -golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= +golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI= +golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= -golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= +golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4= +golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= -golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= +golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= @@ -241,21 +241,20 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= -golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= -golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= 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.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= -golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= 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.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= -golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= +golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8= +golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 0de62db9f72f7246ee281d177c0a100966bb9d20 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jun 2026 02:34:56 +0000 Subject: [PATCH 06/59] chore(deps): bump the dev-dependencies group across 1 directory with 3 updates Bumps the dev-dependencies group with 3 updates in the /webapp/frontend directory: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin), [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) and [apexcharts](https://github.com/apexcharts/apexcharts.js). Updates `@typescript-eslint/eslint-plugin` from 8.59.2 to 8.60.1 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.60.1/packages/eslint-plugin) Updates `@typescript-eslint/parser` from 8.59.2 to 8.60.1 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.60.1/packages/parser) Updates `apexcharts` from 5.11.0 to 5.14.0 - [Release notes](https://github.com/apexcharts/apexcharts.js/releases) - [Commits](https://github.com/apexcharts/apexcharts.js/compare/v5.11.0...v5.14.0) --- updated-dependencies: - dependency-name: "@typescript-eslint/eslint-plugin" dependency-version: 8.60.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-dependencies - dependency-name: "@typescript-eslint/parser" dependency-version: 8.60.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-dependencies - dependency-name: apexcharts dependency-version: 5.14.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-dependencies ... Signed-off-by: dependabot[bot] --- webapp/frontend/package-lock.json | 120 +++++++++++++++--------------- webapp/frontend/package.json | 6 +- 2 files changed, 63 insertions(+), 63 deletions(-) diff --git a/webapp/frontend/package-lock.json b/webapp/frontend/package-lock.json index 628c6d021..fbfa5d416 100644 --- a/webapp/frontend/package-lock.json +++ b/webapp/frontend/package-lock.json @@ -49,9 +49,9 @@ "@types/jasmine": "^4.3.0", "@types/lodash": "~4.17.24", "@types/node": "^20.19.27", - "@typescript-eslint/eslint-plugin": "^8.59.2", - "@typescript-eslint/parser": "^8.59.2", - "apexcharts": "~5.11.0", + "@typescript-eslint/eslint-plugin": "^8.60.1", + "@typescript-eslint/parser": "^8.60.1", + "apexcharts": "~5.14.0", "eslint": "^8.57.1", "eslint-config-prettier": "^8.10.2", "eslint-plugin-prettier": "^4.2.5", @@ -4755,17 +4755,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.2.tgz", - "integrity": "sha512-j/bwmkBvHUtPNxzuWe5z6BEk3q54YRyGlBXkSsmfoih7zNrBvl5A9A98anlp/7JbyZcWIJ8KXo/3Tq/DjFLtuQ==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.1.tgz", + "integrity": "sha512-JQ4S5GB0tfjO8BuJ4fcX+HodkzJjYBV+7OJ+wLygaX7OGQ7FudyHL4NSCA6ob+w3Yn+5MkKIozOwQhXeM7opVg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.59.2", - "@typescript-eslint/type-utils": "8.59.2", - "@typescript-eslint/utils": "8.59.2", - "@typescript-eslint/visitor-keys": "8.59.2", + "@typescript-eslint/scope-manager": "8.60.1", + "@typescript-eslint/type-utils": "8.60.1", + "@typescript-eslint/utils": "8.60.1", + "@typescript-eslint/visitor-keys": "8.60.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -4778,7 +4778,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.59.2", + "@typescript-eslint/parser": "^8.60.1", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } @@ -4794,16 +4794,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.2.tgz", - "integrity": "sha512-plR3pp6D+SSUn1HM7xvSkx12/DhoHInI2YF35KAcVFNZvlC0gtrWqx7Qq1oH2Ssgi0vlFRCTbP+DZc7B9+TtsQ==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.60.1.tgz", + "integrity": "sha512-A0M6ua6H252bVjPvvtSgl2QA4+ET9S5Mtkb2GDyTxIhH/C4qDItT7RQNO5PhMC6NXGYXOR9dIalcDDgBKT7oFA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.59.2", - "@typescript-eslint/types": "8.59.2", - "@typescript-eslint/typescript-estree": "8.59.2", - "@typescript-eslint/visitor-keys": "8.59.2", + "@typescript-eslint/scope-manager": "8.60.1", + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/typescript-estree": "8.60.1", + "@typescript-eslint/visitor-keys": "8.60.1", "debug": "^4.4.3" }, "engines": { @@ -4844,14 +4844,14 @@ "license": "MIT" }, "node_modules/@typescript-eslint/project-service": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.2.tgz", - "integrity": "sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.60.1.tgz", + "integrity": "sha512-eXkTH2bxmXlqD1RnOPmLZ9ZM9D3VwSx04JOwBnP9RQ+yUA5a2Mu7SfW8uaV2Aon53NJzZlZYuX7tn91Izf+xaw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.59.2", - "@typescript-eslint/types": "^8.59.2", + "@typescript-eslint/tsconfig-utils": "^8.60.1", + "@typescript-eslint/types": "^8.60.1", "debug": "^4.4.3" }, "engines": { @@ -4891,14 +4891,14 @@ "license": "MIT" }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.2.tgz", - "integrity": "sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.60.1.tgz", + "integrity": "sha512-gvI5OQoptnxQnchOirukCuQ55svJSTuD/4k5+pC267xyBtYry748R9/c3tYUzb/iE6RZfllRz2lVulLCHkTm4w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.2", - "@typescript-eslint/visitor-keys": "8.59.2" + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/visitor-keys": "8.60.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4909,9 +4909,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.2.tgz", - "integrity": "sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.1.tgz", + "integrity": "sha512-nh8w4qAteiKuZu3pSSzG/yGKpw0OlkrKnzFmbVRenKaD4qc+7i1GrmZaLVkr8rk4uipiPGMOW4YsM6WmKZ5CvA==", "dev": true, "license": "MIT", "engines": { @@ -4926,15 +4926,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.2.tgz", - "integrity": "sha512-nhqaj1nmTdVVl/BP5omXNRGO38jn5iosis2vbdmupF2txCf8ylWT8lx+JlvMYYVqzGVKtjojUFoQ3JRWK+mfzQ==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.60.1.tgz", + "integrity": "sha512-sdwTrpjosW7ANQYJ39ZBF1ZyEMEGVB2UsikrserVM/30a/F1dTLnu9bGxEdosugyu5caigjLrR2qiD11asjI1A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.2", - "@typescript-eslint/typescript-estree": "8.59.2", - "@typescript-eslint/utils": "8.59.2", + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/typescript-estree": "8.60.1", + "@typescript-eslint/utils": "8.60.1", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -4976,9 +4976,9 @@ "license": "MIT" }, "node_modules/@typescript-eslint/types": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.2.tgz", - "integrity": "sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.60.1.tgz", + "integrity": "sha512-4h0tY8ppCkdCzcrl2YM5M3my0xsE1Tf8om3owEu5oPWmXwkKRmk0j0LGDzYBGUcAlesEbxBhazqu/K4cu3Ug7w==", "dev": true, "license": "MIT", "engines": { @@ -4990,16 +4990,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.2.tgz", - "integrity": "sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.1.tgz", + "integrity": "sha512-alpRkfG8hlVE5kdJW2GkfgDgXxold3e8e4l6EnmhRmRLbekgAPCCGDVD++sABy9FcgPFroq+uFcCSM1vR57Cew==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.59.2", - "@typescript-eslint/tsconfig-utils": "8.59.2", - "@typescript-eslint/types": "8.59.2", - "@typescript-eslint/visitor-keys": "8.59.2", + "@typescript-eslint/project-service": "8.60.1", + "@typescript-eslint/tsconfig-utils": "8.60.1", + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/visitor-keys": "8.60.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -5082,16 +5082,16 @@ "license": "MIT" }, "node_modules/@typescript-eslint/utils": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.2.tgz", - "integrity": "sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.60.1.tgz", + "integrity": "sha512-h2MPBLoNtjc3qZWfY3Tl51yPorQ2McHn8pJfcMNTcIvrrZrr90Ykffit0yjrPFWQcRcUxzH20+6OcVdW4yHtUg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.59.2", - "@typescript-eslint/types": "8.59.2", - "@typescript-eslint/typescript-estree": "8.59.2" + "@typescript-eslint/scope-manager": "8.60.1", + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/typescript-estree": "8.60.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5106,13 +5106,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.2.tgz", - "integrity": "sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.1.tgz", + "integrity": "sha512-EbGRQg4FhrmwLodl+t3JNAnXHWVr9Vp+Zl1QBZVPY4ByfkzIT8cX3K6QWODHtkIZqqJVEWvhHSx3v5PDHsaQag==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/types": "8.60.1", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -5403,9 +5403,9 @@ } }, "node_modules/apexcharts": { - "version": "5.11.0", - "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-5.11.0.tgz", - "integrity": "sha512-iIXnZ1DhndVgY4G5fmAbyUtGBTR5j4LlMEb561eAd4+wkJB7wJXXKLMr9u+4V5aQEPXeVYWgkAbuw9Y1z6MIrg==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-5.14.0.tgz", + "integrity": "sha512-hBLz5Gd8B5ZuocEzNUwEeKdw9vd7uy4OnqbDjAYyvwiAEyHk3OnO9+tGzAznpM6pnyCqWjPXUKKYoNIwdskeuQ==", "dev": true, "license": "SEE LICENSE IN LICENSE" }, diff --git a/webapp/frontend/package.json b/webapp/frontend/package.json index 81a7ae5a3..3f2e97b27 100644 --- a/webapp/frontend/package.json +++ b/webapp/frontend/package.json @@ -61,9 +61,9 @@ "@types/jasmine": "^4.3.0", "@types/lodash": "~4.17.24", "@types/node": "^20.19.27", - "@typescript-eslint/eslint-plugin": "^8.59.2", - "@typescript-eslint/parser": "^8.59.2", - "apexcharts": "~5.11.0", + "@typescript-eslint/eslint-plugin": "^8.60.1", + "@typescript-eslint/parser": "^8.60.1", + "apexcharts": "~5.14.0", "eslint": "^8.57.1", "eslint-config-prettier": "^8.10.2", "eslint-plugin-prettier": "^4.2.5", From 6432a2c9b16223b8f3e1cfdf9c621ffedd2f5393 Mon Sep 17 00:00:00 2001 From: Eder Date: Mon, 8 Jun 2026 18:11:59 -0400 Subject: [PATCH 07/59] feat(notify): add heartbeat_enabled field to notify_urls --- .../migrations/m20260608000000/notify_url.go | 17 +++++++++++++++++ .../database/scrutiny_repository_migrations.go | 7 +++++++ webapp/backend/pkg/models/notify_url.go | 13 +++++++------ 3 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 webapp/backend/pkg/database/migrations/m20260608000000/notify_url.go diff --git a/webapp/backend/pkg/database/migrations/m20260608000000/notify_url.go b/webapp/backend/pkg/database/migrations/m20260608000000/notify_url.go new file mode 100644 index 000000000..7769f6020 --- /dev/null +++ b/webapp/backend/pkg/database/migrations/m20260608000000/notify_url.go @@ -0,0 +1,17 @@ +package m20260608000000 + +import "time" + +type NotifyUrl struct { + CreatedAt time.Time + UpdatedAt time.Time + URL string `gorm:"not null"` + Label string + Source string `gorm:"default:'ui'"` + HeartbeatEnabled bool `gorm:"default:true"` + ID uint `gorm:"primaryKey"` +} + +func (NotifyUrl) TableName() string { + return "notify_urls" +} diff --git a/webapp/backend/pkg/database/scrutiny_repository_migrations.go b/webapp/backend/pkg/database/scrutiny_repository_migrations.go index 8b6e014ab..b30776be8 100644 --- a/webapp/backend/pkg/database/scrutiny_repository_migrations.go +++ b/webapp/backend/pkg/database/scrutiny_repository_migrations.go @@ -31,6 +31,7 @@ import ( "github.com/analogj/scrutiny/webapp/backend/pkg/database/migrations/m20260514000000" "github.com/analogj/scrutiny/webapp/backend/pkg/database/migrations/m20260516000000" "github.com/analogj/scrutiny/webapp/backend/pkg/database/migrations/m20260523000000" + m20260608000000 "github.com/analogj/scrutiny/webapp/backend/pkg/database/migrations/m20260608000000" "github.com/analogj/scrutiny/webapp/backend/pkg/deviceid" "github.com/analogj/scrutiny/webapp/backend/pkg/models" "github.com/analogj/scrutiny/webapp/backend/pkg/models/collector" @@ -1117,6 +1118,12 @@ missed_ping_timeout_override INTEGER DEFAULT 0 `).Error }, }, + { + ID: "m20260608000000", // add heartbeat_enabled to notify_urls table + Migrate: func(tx *gorm.DB) error { + return tx.AutoMigrate(&m20260608000000.NotifyUrl{}) + }, + }, }) if err := m.Migrate(); err != nil { diff --git a/webapp/backend/pkg/models/notify_url.go b/webapp/backend/pkg/models/notify_url.go index fd6c01a1c..c2d3da22a 100644 --- a/webapp/backend/pkg/models/notify_url.go +++ b/webapp/backend/pkg/models/notify_url.go @@ -5,12 +5,13 @@ import "time" // NotifyUrl represents a user-configured notification endpoint stored in the database. // Only UI-sourced URLs are persisted here. Config/env URLs are read from Viper at runtime. type NotifyUrl struct { - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - URL string `json:"url" gorm:"not null"` - Label string `json:"label"` - Source string `json:"source" gorm:"default:'ui'"` - ID uint `json:"id" gorm:"primaryKey"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + URL string `json:"url" gorm:"not null"` + Label string `json:"label"` + Source string `json:"source" gorm:"default:'ui'"` + HeartbeatEnabled bool `json:"heartbeat_enabled" gorm:"default:true"` + ID uint `json:"id" gorm:"primaryKey"` } func (NotifyUrl) TableName() string { From 51d866237bc4b876ab0058bc7918b0d32ae856cb Mon Sep 17 00:00:00 2001 From: Eder Date: Mon, 8 Jun 2026 18:15:49 -0400 Subject: [PATCH 08/59] feat(notify): LoadHeartbeatDatabaseUrls filters by heartbeat_enabled --- webapp/backend/pkg/notify/notify.go | 16 ++++++++++ webapp/backend/pkg/notify/notify_test.go | 38 ++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/webapp/backend/pkg/notify/notify.go b/webapp/backend/pkg/notify/notify.go index 6da8339a6..4930f0021 100644 --- a/webapp/backend/pkg/notify/notify.go +++ b/webapp/backend/pkg/notify/notify.go @@ -415,6 +415,22 @@ func (n *Notify) LoadDatabaseUrls(ctx context.Context, repo database.DeviceRepo) } } +// LoadHeartbeatDatabaseUrls populates n.DatabaseUrls with only the URLs that +// have HeartbeatEnabled set to true. Used by the heartbeat monitor so users +// can choose which endpoints receive periodic health pings. +func (n *Notify) LoadHeartbeatDatabaseUrls(ctx context.Context, repo database.DeviceRepo) { + dbUrls, err := repo.GetNotifyUrls(ctx) + if err != nil { + n.Logger.Warnf("Could not load database notification URLs for heartbeat: %v", err) + return + } + for _, u := range dbUrls { + if u.HeartbeatEnabled { + n.DatabaseUrls = append(n.DatabaseUrls, u.URL) + } + } +} + // Send dispatches notifications to all configured URLs (config/env + database). func (n *Notify) Send() error { // Retrieve list of notification endpoints from config file / env var diff --git a/webapp/backend/pkg/notify/notify_test.go b/webapp/backend/pkg/notify/notify_test.go index e3ebec86c..bd6b65bef 100644 --- a/webapp/backend/pkg/notify/notify_test.go +++ b/webapp/backend/pkg/notify/notify_test.go @@ -1083,3 +1083,41 @@ func writeSMTPLine(t *testing.T, writer *bufio.Writer, line string) { require.NoError(t, err) require.NoError(t, writer.Flush()) } + +func TestLoadHeartbeatDatabaseUrls_FiltersDisabled(t *testing.T) { + t.Parallel() + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + fakeDatabase := mock_database.NewMockDeviceRepo(mockCtrl) + + fakeDatabase.EXPECT(). + GetNotifyUrls(gomock.Any()). + Return([]models.NotifyUrl{ + {ID: 1, URL: "slack://token", HeartbeatEnabled: true}, + {ID: 2, URL: "discord://token", HeartbeatEnabled: false}, + {ID: 3, URL: "generic://healthchecks.io", HeartbeatEnabled: true}, + }, nil). + Times(1) + + n := Notify{Logger: logrus.StandardLogger()} + n.LoadHeartbeatDatabaseUrls(context.Background(), fakeDatabase) + + require.Equal(t, []string{"slack://token", "generic://healthchecks.io"}, n.DatabaseUrls) +} + +func TestLoadHeartbeatDatabaseUrls_RepoError_Degrades(t *testing.T) { + t.Parallel() + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + fakeDatabase := mock_database.NewMockDeviceRepo(mockCtrl) + + fakeDatabase.EXPECT(). + GetNotifyUrls(gomock.Any()). + Return(nil, errors.New("db error")). + Times(1) + + n := Notify{Logger: logrus.StandardLogger()} + n.LoadHeartbeatDatabaseUrls(context.Background(), fakeDatabase) + + require.Empty(t, n.DatabaseUrls) +} From 24f359ac6981803461f3b8be90c06c30d81bacd4 Mon Sep 17 00:00:00 2001 From: Eder Date: Mon, 8 Jun 2026 18:20:54 -0400 Subject: [PATCH 09/59] feat(heartbeat): filter notify URLs by heartbeat_enabled --- webapp/backend/pkg/web/heartbeat_monitor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/backend/pkg/web/heartbeat_monitor.go b/webapp/backend/pkg/web/heartbeat_monitor.go index 94029835b..2b54415b0 100644 --- a/webapp/backend/pkg/web/heartbeat_monitor.go +++ b/webapp/backend/pkg/web/heartbeat_monitor.go @@ -260,7 +260,7 @@ func (m *HeartbeatMonitor) checkAndSendHeartbeat() { m.logger.Infof("All %d monitored drives healthy, sending heartbeat notification", monitoredCount) notification := notify.NewHeartbeat(m.logger, m.appEngine.Config, monitoredCount, totalCount) - notification.LoadDatabaseUrls(m.ctx, m.deviceRepo) + notification.LoadHeartbeatDatabaseUrls(m.ctx, m.deviceRepo) // Route through notification gate (bypass quiet hours -- heartbeats are informational) if gate := m.appEngine.NotificationGate; gate != nil { From 99bb24280c386a3be5fd04ed4975577ef5399340 Mon Sep 17 00:00:00 2001 From: Eder Date: Mon, 8 Jun 2026 18:31:07 -0400 Subject: [PATCH 10/59] feat(api): expose and update heartbeat_enabled on notify URLs --- webapp/backend/pkg/database/interface.go | 1 + .../pkg/database/mock/mock_database.go | 14 +++ .../scrutiny_repository_notify_urls.go | 9 ++ webapp/backend/pkg/web/handler/notify_urls.go | 72 ++++++++--- .../pkg/web/handler/notify_urls_test.go | 117 ++++++++++++++++++ webapp/backend/pkg/web/server.go | 1 + 6 files changed, 196 insertions(+), 18 deletions(-) create mode 100644 webapp/backend/pkg/web/handler/notify_urls_test.go diff --git a/webapp/backend/pkg/database/interface.go b/webapp/backend/pkg/database/interface.go index 31e36a3a7..de844aa6d 100644 --- a/webapp/backend/pkg/database/interface.go +++ b/webapp/backend/pkg/database/interface.go @@ -152,4 +152,5 @@ type DeviceRepo interface { GetNotifyUrls(ctx context.Context) ([]models.NotifyUrl, error) SaveNotifyUrl(ctx context.Context, notifyUrl *models.NotifyUrl) error DeleteNotifyUrl(ctx context.Context, id uint) error + UpdateNotifyUrlHeartbeat(ctx context.Context, id uint, enabled bool) error } diff --git a/webapp/backend/pkg/database/mock/mock_database.go b/webapp/backend/pkg/database/mock/mock_database.go index 1731e7937..738739a23 100644 --- a/webapp/backend/pkg/database/mock/mock_database.go +++ b/webapp/backend/pkg/database/mock/mock_database.go @@ -125,6 +125,20 @@ func (mr *MockDeviceRepoMockRecorder) DeleteNotifyUrl(ctx, id interface{}) *gomo return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteNotifyUrl", reflect.TypeOf((*MockDeviceRepo)(nil).DeleteNotifyUrl), ctx, id) } +// UpdateNotifyUrlHeartbeat mocks base method. +func (m *MockDeviceRepo) UpdateNotifyUrlHeartbeat(ctx context.Context, id uint, enabled bool) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateNotifyUrlHeartbeat", ctx, id, enabled) + ret0, _ := ret[0].(error) + return ret0 +} + +// UpdateNotifyUrlHeartbeat indicates an expected call of UpdateNotifyUrlHeartbeat. +func (mr *MockDeviceRepoMockRecorder) UpdateNotifyUrlHeartbeat(ctx, id, enabled interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateNotifyUrlHeartbeat", reflect.TypeOf((*MockDeviceRepo)(nil).UpdateNotifyUrlHeartbeat), ctx, id, enabled) +} + // DeleteZFSPool mocks base method. func (m *MockDeviceRepo) DeleteZFSPool(ctx context.Context, guid string) error { m.ctrl.T.Helper() diff --git a/webapp/backend/pkg/database/scrutiny_repository_notify_urls.go b/webapp/backend/pkg/database/scrutiny_repository_notify_urls.go index 62319fb05..5471bc8bd 100644 --- a/webapp/backend/pkg/database/scrutiny_repository_notify_urls.go +++ b/webapp/backend/pkg/database/scrutiny_repository_notify_urls.go @@ -25,3 +25,12 @@ func (sr *scrutinyRepository) SaveNotifyUrl(ctx context.Context, notifyUrl *mode func (sr *scrutinyRepository) DeleteNotifyUrl(ctx context.Context, id uint) error { return sr.gormClient.WithContext(ctx).Delete(&models.NotifyUrl{}, id).Error } + +// UpdateNotifyUrlHeartbeat updates the heartbeat_enabled flag for a notification URL +func (sr *scrutinyRepository) UpdateNotifyUrlHeartbeat(ctx context.Context, id uint, enabled bool) error { + return sr.gormClient.WithContext(ctx). + Model(&models.NotifyUrl{}). + Where("id = ?", id). + Update("heartbeat_enabled", enabled). + Error +} diff --git a/webapp/backend/pkg/web/handler/notify_urls.go b/webapp/backend/pkg/web/handler/notify_urls.go index f3a5053df..956fba28b 100644 --- a/webapp/backend/pkg/web/handler/notify_urls.go +++ b/webapp/backend/pkg/web/handler/notify_urls.go @@ -13,11 +13,14 @@ import ( "github.com/sirupsen/logrus" ) +const errInvalidIDFormat = "Invalid ID format" + type notifyUrlResponse struct { - URL string `json:"url"` - Label string `json:"label,omitempty"` - Source string `json:"source"` - ID uint `json:"id,omitempty"` + URL string `json:"url"` + Label string `json:"label,omitempty"` + Source string `json:"source"` + HeartbeatEnabled bool `json:"heartbeat_enabled"` + ID uint `json:"id,omitempty"` } // GetNotifyUrls returns a merged list of notification URLs from all sources. @@ -48,10 +51,11 @@ func GetNotifyUrls(c *gin.Context) { } for _, u := range dbUrls { result = append(result, notifyUrlResponse{ - ID: u.ID, - URL: notify.MaskNotifyUrl(u.URL), - Label: u.Label, - Source: u.Source, + ID: u.ID, + URL: notify.MaskNotifyUrl(u.URL), + Label: u.Label, + Source: u.Source, + HeartbeatEnabled: u.HeartbeatEnabled, }) } @@ -68,8 +72,9 @@ func SaveNotifyUrl(c *gin.Context) { deviceRepo := c.MustGet("DEVICE_REPOSITORY").(database.DeviceRepo) var input struct { - URL string `json:"url"` - Label string `json:"label"` + URL string `json:"url"` + Label string `json:"label"` + HeartbeatEnabled bool `json:"heartbeat_enabled"` } if err := c.BindJSON(&input); err != nil { logger.Errorln("Cannot parse notification URL:", err) @@ -83,8 +88,9 @@ func SaveNotifyUrl(c *gin.Context) { } entry := &models.NotifyUrl{ - URL: input.URL, - Label: input.Label, + URL: input.URL, + Label: input.Label, + HeartbeatEnabled: input.HeartbeatEnabled, } if err := deviceRepo.SaveNotifyUrl(c, entry); err != nil { @@ -96,10 +102,11 @@ func SaveNotifyUrl(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "success": true, "data": notifyUrlResponse{ - ID: entry.ID, - URL: notify.MaskNotifyUrl(entry.URL), - Label: entry.Label, - Source: entry.Source, + ID: entry.ID, + URL: notify.MaskNotifyUrl(entry.URL), + Label: entry.Label, + Source: entry.Source, + HeartbeatEnabled: entry.HeartbeatEnabled, }, }) } @@ -112,7 +119,7 @@ func DeleteNotifyUrl(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 32) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "Invalid ID format"}) + c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": errInvalidIDFormat}) return } @@ -125,6 +132,35 @@ func DeleteNotifyUrl(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"success": true}) } +// UpdateNotifyUrlHeartbeat toggles heartbeat_enabled for a notification URL by ID +func UpdateNotifyUrlHeartbeat(c *gin.Context) { + logger := c.MustGet("LOGGER").(*logrus.Entry) + deviceRepo := c.MustGet("DEVICE_REPOSITORY").(database.DeviceRepo) + + idStr := c.Param("id") + id, err := strconv.ParseUint(idStr, 10, 32) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": errInvalidIDFormat}) + return + } + + var input struct { + Enabled bool `json:"enabled"` + } + if err := c.BindJSON(&input); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "Invalid request body"}) + return + } + + if err := deviceRepo.UpdateNotifyUrlHeartbeat(c, uint(id), input.Enabled); err != nil { + logger.Errorln("Error updating notification URL heartbeat:", err) + c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "Failed to update notification URL"}) + return + } + + c.JSON(http.StatusOK, gin.H{"success": true}) +} + // TestNotifyUrl sends a test notification to a single specific URL by its database ID. // Uses SendToUrls to bypass the config URL loading and only send to the target URL. func TestNotifyUrl(c *gin.Context) { @@ -135,7 +171,7 @@ func TestNotifyUrl(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 32) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "Invalid ID format"}) + c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": errInvalidIDFormat}) return } diff --git a/webapp/backend/pkg/web/handler/notify_urls_test.go b/webapp/backend/pkg/web/handler/notify_urls_test.go new file mode 100644 index 000000000..1cbb5c315 --- /dev/null +++ b/webapp/backend/pkg/web/handler/notify_urls_test.go @@ -0,0 +1,117 @@ +package handler_test + +import ( + "bytes" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + mock_config "github.com/analogj/scrutiny/webapp/backend/pkg/config/mock" + mock_database "github.com/analogj/scrutiny/webapp/backend/pkg/database/mock" + "github.com/analogj/scrutiny/webapp/backend/pkg/models" + "github.com/analogj/scrutiny/webapp/backend/pkg/web/handler" + "github.com/gin-gonic/gin" + "github.com/golang/mock/gomock" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/require" +) + +func setupNotifyUrlsRouter(t *testing.T, mockRepo *mock_database.MockDeviceRepo, mockCfg *mock_config.MockInterface) *gin.Engine { + t.Helper() + gin.SetMode(gin.TestMode) + logger := logrus.WithField("test", t.Name()) + + r := gin.New() + r.Use(func(c *gin.Context) { + c.Set("LOGGER", logger) + c.Set("DEVICE_REPOSITORY", mockRepo) + c.Set("CONFIG", mockCfg) + c.Next() + }) + r.GET("/api/settings/notify-urls", handler.GetNotifyUrls) + r.POST("/api/settings/notify-urls", handler.SaveNotifyUrl) + r.PATCH("/api/settings/notify-urls/:id", handler.UpdateNotifyUrlHeartbeat) + return r +} + +func TestGetNotifyUrls_IncludesHeartbeatEnabled(t *testing.T) { + mockCtrl := gomock.NewController(t) + t.Cleanup(mockCtrl.Finish) + mockRepo := mock_database.NewMockDeviceRepo(mockCtrl) + mockCfg := mock_config.NewMockInterface(mockCtrl) + + mockCfg.EXPECT().GetStringSlice("notify.urls").Return([]string{}) + mockRepo.EXPECT().GetNotifyUrls(gomock.Any()).Return([]models.NotifyUrl{ + {ID: 1, URL: "slack://token", Label: "Slack", Source: "ui", HeartbeatEnabled: false}, + }, nil) + + router := setupNotifyUrlsRouter(t, mockRepo, mockCfg) + + w := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/api/settings/notify-urls", nil) + router.ServeHTTP(w, req) + + require.Equal(t, http.StatusOK, w.Code) + var resp struct { + Success bool `json:"success"` + Data []struct { + HeartbeatEnabled bool `json:"heartbeat_enabled"` + } `json:"data"` + } + require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp)) + require.True(t, resp.Success) + require.Len(t, resp.Data, 1) + require.False(t, resp.Data[0].HeartbeatEnabled) +} + +func TestSaveNotifyUrl_PersistsHeartbeatEnabled(t *testing.T) { + mockCtrl := gomock.NewController(t) + t.Cleanup(mockCtrl.Finish) + mockRepo := mock_database.NewMockDeviceRepo(mockCtrl) + mockCfg := mock_config.NewMockInterface(mockCtrl) + + mockRepo.EXPECT().SaveNotifyUrl(gomock.Any(), gomock.Any()).DoAndReturn( + func(_ interface{}, u *models.NotifyUrl) error { + require.True(t, u.HeartbeatEnabled) + u.ID = 42 + return nil + }, + ) + + router := setupNotifyUrlsRouter(t, mockRepo, mockCfg) + + body, _ := json.Marshal(map[string]interface{}{ + "url": "generic://healthchecks.io/ping/abc", + "label": "HC", + "heartbeat_enabled": true, + }) + w := httptest.NewRecorder() + req, _ := http.NewRequest("POST", "/api/settings/notify-urls", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + router.ServeHTTP(w, req) + + require.Equal(t, http.StatusOK, w.Code) +} + +func TestUpdateNotifyUrlHeartbeat_Success(t *testing.T) { + mockCtrl := gomock.NewController(t) + t.Cleanup(mockCtrl.Finish) + mockRepo := mock_database.NewMockDeviceRepo(mockCtrl) + mockCfg := mock_config.NewMockInterface(mockCtrl) + + mockRepo.EXPECT().UpdateNotifyUrlHeartbeat(gomock.Any(), uint(1), false).Return(nil) + + router := setupNotifyUrlsRouter(t, mockRepo, mockCfg) + + body, _ := json.Marshal(map[string]bool{"enabled": false}) + w := httptest.NewRecorder() + req, _ := http.NewRequest("PATCH", "/api/settings/notify-urls/1", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + router.ServeHTTP(w, req) + + require.Equal(t, http.StatusOK, w.Code) + var resp map[string]interface{} + require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp)) + require.Equal(t, true, resp["success"]) +} diff --git a/webapp/backend/pkg/web/server.go b/webapp/backend/pkg/web/server.go index b383ebeec..e2a3ece83 100644 --- a/webapp/backend/pkg/web/server.go +++ b/webapp/backend/pkg/web/server.go @@ -168,6 +168,7 @@ func (ae *AppEngine) Setup(logger *logrus.Entry) *gin.Engine { api.POST("/settings/notify-urls", handler.SaveNotifyUrl) api.DELETE("/settings/notify-urls/:id", handler.DeleteNotifyUrl) api.POST("/settings/notify-urls/:id/test", handler.TestNotifyUrl) + api.PATCH("/settings/notify-urls/:id", handler.UpdateNotifyUrlHeartbeat) // Scheduled report endpoints api.GET("/reports/generate", handler.GenerateReport) From e6bbfea6978eed6c625ac46bcab9b2fac945123a Mon Sep 17 00:00:00 2001 From: Eder Date: Mon, 8 Jun 2026 18:41:24 -0400 Subject: [PATCH 11/59] feat(ui): add heartbeat toggle per notification URL Add heartbeatEnabled to NotifyUrlEntry interface, update NotifyUrlService to map heartbeat_enabled from the API and send it on create/PATCH, and surface a mat-slide-toggle in the notify-URLs table and add-URL form. --- .../src/app/core/config/app.config.ts | 1 + .../src/app/core/config/notify-url.service.ts | 44 ++++++++++++++++--- .../dashboard-settings.component.html | 21 +++++++++ .../dashboard-settings.component.ts | 26 ++++++++++- 4 files changed, 85 insertions(+), 7 deletions(-) diff --git a/webapp/frontend/src/app/core/config/app.config.ts b/webapp/frontend/src/app/core/config/app.config.ts index a376622fb..bc6ff91cc 100644 --- a/webapp/frontend/src/app/core/config/app.config.ts +++ b/webapp/frontend/src/app/core/config/app.config.ts @@ -82,6 +82,7 @@ export interface NotifyUrlEntry { url: string; label?: string; source: NotifyUrlSource; + heartbeatEnabled?: boolean; } /** diff --git a/webapp/frontend/src/app/core/config/notify-url.service.ts b/webapp/frontend/src/app/core/config/notify-url.service.ts index 6491fa038..ab2f17889 100644 --- a/webapp/frontend/src/app/core/config/notify-url.service.ts +++ b/webapp/frontend/src/app/core/config/notify-url.service.ts @@ -7,12 +7,12 @@ import { NotifyUrlEntry } from './app.config'; interface NotifyUrlsResponse { success: boolean; - data: NotifyUrlEntry[]; + data: any[]; } interface NotifyUrlResponse { success: boolean; - data: NotifyUrlEntry; + data: any; } interface SimpleResponse { @@ -26,11 +26,41 @@ export class NotifyUrlService { private readonly http = inject(HttpClient); getNotifyUrls(): Observable { - return this.http.get(getBasePath() + '/api/settings/notify-urls').pipe(map((response) => response.data || [])); + return this.http.get(getBasePath() + '/api/settings/notify-urls').pipe( + map((response) => + (response.data || []).map( + (e: any) => + ({ + id: e.id, + url: e.url, + label: e.label, + source: e.source, + heartbeatEnabled: e.heartbeat_enabled, + } as NotifyUrlEntry) + ) + ) + ); } - addNotifyUrl(url: string, label: string): Observable { - return this.http.post(getBasePath() + '/api/settings/notify-urls', { url, label }).pipe(map((response) => response.data)); + addNotifyUrl(url: string, label: string, heartbeatEnabled: boolean = false): Observable { + return this.http + .post(getBasePath() + '/api/settings/notify-urls', { + url, + label, + heartbeat_enabled: heartbeatEnabled, + }) + .pipe( + map( + (response) => + ({ + id: response.data.id, + url: response.data.url, + label: response.data.label, + source: response.data.source, + heartbeatEnabled: response.data.heartbeat_enabled, + } as NotifyUrlEntry) + ) + ); } deleteNotifyUrl(id: number): Observable { @@ -40,4 +70,8 @@ export class NotifyUrlService { testNotifyUrl(id: number): Observable { return this.http.post(getBasePath() + '/api/settings/notify-urls/' + id + '/test', {}).pipe(map(() => undefined)); } + + setHeartbeatEnabled(id: number, enabled: boolean): Observable { + return this.http.patch(getBasePath() + '/api/settings/notify-urls/' + id, { enabled }).pipe(map(() => void 0)); + } } diff --git a/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.html b/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.html index 03b9d39b5..496dd50dd 100644 --- a/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.html +++ b/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.html @@ -587,6 +587,18 @@

Add New Override

+ + Heartbeat + + + + + + @@ -781,6 +793,15 @@

Add Notification Channel

} +
+ + Send heartbeat pings to this URL + +
+
diff --git a/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.ts b/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.ts index c672a193a..613a38b18 100644 --- a/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.ts +++ b/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.ts @@ -36,6 +36,7 @@ import { MatIcon } from '@angular/material/icon'; import { MatExpansionPanel, MatExpansionPanelHeader, MatExpansionPanelTitle, MatExpansionPanelDescription } from '@angular/material/expansion'; import { MatTable, MatColumnDef, MatHeaderCellDef, MatHeaderCell, MatCellDef, MatCell, MatHeaderRowDef, MatHeaderRow, MatRowDef, MatRow } from '@angular/material/table'; import { MatTooltip } from '@angular/material/tooltip'; +import { MatSlideToggle } from '@angular/material/slide-toggle'; @Component({ selector: 'app-dashboard-settings', @@ -73,6 +74,7 @@ import { MatTooltip } from '@angular/material/tooltip'; MatRow, MatDialogActions, MatDialogClose, + MatSlideToggle, ], }) export class DashboardSettingsComponent implements OnInit { @@ -156,7 +158,7 @@ export class DashboardSettingsComponent implements OnInit { // Notification URL management notifyUrls: NotifyUrlEntry[] = []; - notifyUrlColumns: string[] = ['label', 'url', 'source', 'actions']; + notifyUrlColumns: string[] = ['label', 'url', 'source', 'heartbeat', 'actions']; showAddUrlForm = false; selectedEngine: 'shoutrrr' | 'apprise' = 'shoutrrr'; selectedShoutrrrService: 'custom' | 'smtp' | 'discord' | 'slack' | 'telegram' = 'custom'; @@ -164,6 +166,7 @@ export class DashboardSettingsComponent implements OnInit { newUrlRaw = ''; newAppriseUrlRaw = ''; newUrlLabel = ''; + newUrlHeartbeatEnabled = false; // SMTP fields smtpHost = ''; smtpPort = '587'; @@ -367,6 +370,24 @@ export class DashboardSettingsComponent implements OnInit { }); } + toggleHeartbeat(entry: NotifyUrlEntry): void { + if (!entry.id) { + return; + } + const newValue = !entry.heartbeatEnabled; + this._notifyUrlService + .setHeartbeatEnabled(entry.id, newValue) + .pipe(takeUntil(this._unsubscribeAll)) + .subscribe({ + next: () => { + entry.heartbeatEnabled = newValue; + }, + error: () => { + this.loadNotifyUrls(); + }, + }); + } + buildShoutrrrUrl(): string { switch (this.selectedShoutrrrService) { case 'custom': @@ -447,7 +468,7 @@ export class DashboardSettingsComponent implements OnInit { const label = this.newUrlLabel.trim(); this._notifyUrlService - .addNotifyUrl(url, label) + .addNotifyUrl(url, label, this.newUrlHeartbeatEnabled) .pipe(takeUntil(this._unsubscribeAll)) .subscribe((saved) => { this.notifyUrls = [...this.notifyUrls, saved]; @@ -463,6 +484,7 @@ export class DashboardSettingsComponent implements OnInit { this.newUrlRaw = ''; this.newAppriseUrlRaw = ''; this.newUrlLabel = ''; + this.newUrlHeartbeatEnabled = false; this.smtpHost = ''; this.smtpPort = '587'; this.smtpUsername = ''; From 8c375c288aeaa68c1257a62be1cec71178ef3139 Mon Sep 17 00:00:00 2001 From: Eder Date: Mon, 8 Jun 2026 18:57:44 -0400 Subject: [PATCH 12/59] fix(api): return 404 when heartbeat toggle targets non-existent notify URL --- .../pkg/database/scrutiny_repository_notify_urls.go | 13 ++++++++++--- webapp/backend/pkg/web/handler/notify_urls.go | 6 ++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/webapp/backend/pkg/database/scrutiny_repository_notify_urls.go b/webapp/backend/pkg/database/scrutiny_repository_notify_urls.go index 5471bc8bd..11fff4e6d 100644 --- a/webapp/backend/pkg/database/scrutiny_repository_notify_urls.go +++ b/webapp/backend/pkg/database/scrutiny_repository_notify_urls.go @@ -4,6 +4,7 @@ import ( "context" "github.com/analogj/scrutiny/webapp/backend/pkg/models" + "gorm.io/gorm" ) // GetNotifyUrls retrieves all UI-sourced notification URLs from the database @@ -28,9 +29,15 @@ func (sr *scrutinyRepository) DeleteNotifyUrl(ctx context.Context, id uint) erro // UpdateNotifyUrlHeartbeat updates the heartbeat_enabled flag for a notification URL func (sr *scrutinyRepository) UpdateNotifyUrlHeartbeat(ctx context.Context, id uint, enabled bool) error { - return sr.gormClient.WithContext(ctx). + result := sr.gormClient.WithContext(ctx). Model(&models.NotifyUrl{}). Where("id = ?", id). - Update("heartbeat_enabled", enabled). - Error + Update("heartbeat_enabled", enabled) + if result.Error != nil { + return result.Error + } + if result.RowsAffected == 0 { + return gorm.ErrRecordNotFound + } + return nil } diff --git a/webapp/backend/pkg/web/handler/notify_urls.go b/webapp/backend/pkg/web/handler/notify_urls.go index 956fba28b..b56d17057 100644 --- a/webapp/backend/pkg/web/handler/notify_urls.go +++ b/webapp/backend/pkg/web/handler/notify_urls.go @@ -1,6 +1,7 @@ package handler import ( + "errors" "net/http" "strconv" @@ -11,6 +12,7 @@ import ( "github.com/analogj/scrutiny/webapp/backend/pkg/notify" "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" + "gorm.io/gorm" ) const errInvalidIDFormat = "Invalid ID format" @@ -153,6 +155,10 @@ func UpdateNotifyUrlHeartbeat(c *gin.Context) { } if err := deviceRepo.UpdateNotifyUrlHeartbeat(c, uint(id), input.Enabled); err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + c.JSON(http.StatusNotFound, gin.H{"success": false, "error": "Notification URL not found"}) + return + } logger.Errorln("Error updating notification URL heartbeat:", err) c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "Failed to update notification URL"}) return From 8ceb12b92d095dcb2c8001f6f07b8fda704b6713 Mon Sep 17 00:00:00 2001 From: Eder Date: Mon, 8 Jun 2026 21:55:07 -0400 Subject: [PATCH 13/59] feat(ui): add contextual help links for settings docs (#492) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add HelpLinkIconComponent — a standalone icon-button anchor — and wire it into the Notification Channels expansion panel title. Also add an inline "Learn more" link to the Consumer Drive Profiles field hint pointing to CONSUMER_DRIVE_PROFILES.md. --- .../dashboard-settings.component.html | 6 +++++- .../dashboard-settings.component.ts | 2 ++ .../help-link-icon/help-link-icon.component.html | 3 +++ .../help-link-icon/help-link-icon.component.ts | 14 ++++++++++++++ 4 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 webapp/frontend/src/app/layout/common/help-link-icon/help-link-icon.component.html create mode 100644 webapp/frontend/src/app/layout/common/help-link-icon/help-link-icon.component.ts diff --git a/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.html b/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.html index 496dd50dd..0ad1c496b 100644 --- a/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.html +++ b/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.html @@ -149,6 +149,7 @@

Scrutiny Settings

Apply vetted ATA consumer drive model or family overrides to SMART status evaluation and replacement-risk scoring. Disable to use generic ATA rules only. + Learn more
@@ -556,7 +557,10 @@

Add New Override

- Notification Channels + + Notification Channels + + Configure where notifications are sent diff --git a/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.ts b/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.ts index 613a38b18..3c9732b91 100644 --- a/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.ts +++ b/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.ts @@ -37,6 +37,7 @@ import { MatExpansionPanel, MatExpansionPanelHeader, MatExpansionPanelTitle, Mat import { MatTable, MatColumnDef, MatHeaderCellDef, MatHeaderCell, MatCellDef, MatCell, MatHeaderRowDef, MatHeaderRow, MatRowDef, MatRow } from '@angular/material/table'; import { MatTooltip } from '@angular/material/tooltip'; import { MatSlideToggle } from '@angular/material/slide-toggle'; +import { HelpLinkIconComponent } from 'app/layout/common/help-link-icon/help-link-icon.component'; @Component({ selector: 'app-dashboard-settings', @@ -75,6 +76,7 @@ import { MatSlideToggle } from '@angular/material/slide-toggle'; MatDialogActions, MatDialogClose, MatSlideToggle, + HelpLinkIconComponent, ], }) export class DashboardSettingsComponent implements OnInit { diff --git a/webapp/frontend/src/app/layout/common/help-link-icon/help-link-icon.component.html b/webapp/frontend/src/app/layout/common/help-link-icon/help-link-icon.component.html new file mode 100644 index 000000000..7130a44cf --- /dev/null +++ b/webapp/frontend/src/app/layout/common/help-link-icon/help-link-icon.component.html @@ -0,0 +1,3 @@ + + help_outline + diff --git a/webapp/frontend/src/app/layout/common/help-link-icon/help-link-icon.component.ts b/webapp/frontend/src/app/layout/common/help-link-icon/help-link-icon.component.ts new file mode 100644 index 000000000..f32550007 --- /dev/null +++ b/webapp/frontend/src/app/layout/common/help-link-icon/help-link-icon.component.ts @@ -0,0 +1,14 @@ +import { Component, Input } from '@angular/core'; +import { MatIconButton } from '@angular/material/button'; +import { MatIcon } from '@angular/material/icon'; +import { MatTooltip } from '@angular/material/tooltip'; + +@Component({ + selector: 'app-help-link-icon', + templateUrl: './help-link-icon.component.html', + imports: [MatIconButton, MatIcon, MatTooltip], +}) +export class HelpLinkIconComponent { + @Input() href: string = ''; + @Input() tooltip: string = 'View documentation'; +} From fca1ae313cecbdec26d0dfac51a82b6ae6605df3 Mon Sep 17 00:00:00 2001 From: Eder Date: Mon, 8 Jun 2026 22:12:52 -0400 Subject: [PATCH 14/59] feat(ui): add navigation visibility settings for ZFS, MDADM, Btrfs, Workload (#588) Adds four boolean settings (navigation.show_zfs_pools, show_mdadm, show_btrfs, show_workload) to hide unused nav links. Configurable via settings UI, scrutiny.yaml, or env vars (SCRUTINY_WEB_NAVIGATION_SHOW_*). Defaults to true (all visible). --- .../scrutiny_repository_migrations.go | 32 ++++++++++++++++ webapp/backend/pkg/models/settings.go | 6 +++ .../src/app/core/config/app.config.ts | 14 +++++++ .../dashboard-settings.component.html | 37 +++++++++++++++++++ .../dashboard-settings.component.ts | 18 +++++++++ .../mobile-tab-bar.component.html | 2 +- .../mobile-tab-bar.component.ts | 18 +++++++++ .../material/material.component.html | 8 ++++ .../horizontal/material/material.component.ts | 9 +++++ 9 files changed, 143 insertions(+), 1 deletion(-) diff --git a/webapp/backend/pkg/database/scrutiny_repository_migrations.go b/webapp/backend/pkg/database/scrutiny_repository_migrations.go index b30776be8..aead02772 100644 --- a/webapp/backend/pkg/database/scrutiny_repository_migrations.go +++ b/webapp/backend/pkg/database/scrutiny_repository_migrations.go @@ -1124,6 +1124,38 @@ missed_ping_timeout_override INTEGER DEFAULT 0 return tx.AutoMigrate(&m20260608000000.NotifyUrl{}) }, }, + { + ID: "m20260609000000", // add navigation visibility settings (#588) + Migrate: func(tx *gorm.DB) error { + var defaultSettings = []m20220716214900.Setting{ + { + SettingKeyName: "navigation.show_zfs_pools", + SettingKeyDescription: "Whether to show the ZFS Pools navigation link (true | false)", + SettingDataType: "bool", + SettingValueBool: true, + }, + { + SettingKeyName: "navigation.show_mdadm", + SettingKeyDescription: "Whether to show the MDADM RAID navigation link (true | false)", + SettingDataType: "bool", + SettingValueBool: true, + }, + { + SettingKeyName: "navigation.show_btrfs", + SettingKeyDescription: "Whether to show the Btrfs navigation link (true | false)", + SettingDataType: "bool", + SettingValueBool: true, + }, + { + SettingKeyName: "navigation.show_workload", + SettingKeyDescription: "Whether to show the Workload navigation link (true | false)", + SettingDataType: "bool", + SettingValueBool: true, + }, + } + return tx.Create(&defaultSettings).Error + }, + }, }) if err := m.Migrate(); err != nil { diff --git a/webapp/backend/pkg/models/settings.go b/webapp/backend/pkg/models/settings.go index 9cf3f2dc4..0478b281c 100644 --- a/webapp/backend/pkg/models/settings.go +++ b/webapp/backend/pkg/models/settings.go @@ -53,6 +53,12 @@ type Settings struct { Collector struct { RetrieveSCTHistory bool `json:"retrieve_sct_temperature_history" mapstructure:"retrieve_sct_temperature_history"` } `json:"collector" mapstructure:"collector"` // Missed collector ping notification settings + Navigation struct { + ShowZFSPools bool `json:"show_zfs_pools" mapstructure:"show_zfs_pools"` + ShowMDADM bool `json:"show_mdadm" mapstructure:"show_mdadm"` + ShowBtrfs bool `json:"show_btrfs" mapstructure:"show_btrfs"` + ShowWorkload bool `json:"show_workload" mapstructure:"show_workload"` + } `json:"navigation" mapstructure:"navigation"` // Scheduled report settings } diff --git a/webapp/frontend/src/app/core/config/app.config.ts b/webapp/frontend/src/app/core/config/app.config.ts index bc6ff91cc..aae05468d 100644 --- a/webapp/frontend/src/app/core/config/app.config.ts +++ b/webapp/frontend/src/app/core/config/app.config.ts @@ -152,6 +152,13 @@ export interface AppConfig { report_pdf_path?: string; }; + navigation?: { + show_zfs_pools?: boolean; + show_mdadm?: boolean; + show_btrfs?: boolean; + show_workload?: boolean; + }; + // Server capabilities (populated from API response, not stored in settings) server_version?: string; collector_trigger_enabled?: boolean; @@ -183,6 +190,13 @@ export const appConfig: AppConfig = { retrieve_sct_temperature_history: true, }, + navigation: { + show_zfs_pools: true, + show_mdadm: true, + show_btrfs: true, + show_workload: true, + }, + metrics: { notify_level: MetricsNotifyLevel.Fail, status_filter_attributes: MetricsStatusFilterAttributes.All, diff --git a/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.html b/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.html index 0ad1c496b..138066973 100644 --- a/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.html +++ b/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.html @@ -814,6 +814,43 @@

Add Notification Channel

}
+ + + + Navigation + Show or hide navigation links + +
+ + ZFS Pools + + Show + Hide + + + + MDADM RAID + + Show + Hide + + + + Btrfs + + Show + Hide + + + + Workload + + Show + Hide + + +
+
diff --git a/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.ts b/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.ts index 3c9732b91..e1c2b07cb 100644 --- a/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.ts +++ b/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.ts @@ -126,6 +126,12 @@ export class DashboardSettingsComponent implements OnInit { uptimeKumaTestLoading = false; uptimeKumaTestResult: string | null = null; + // Navigation visibility settings + showZFSPools: boolean; + showMDADM: boolean; + showBtrfs: boolean; + showWorkload: boolean; + // Report settings reportEnabled: boolean; reportDailyEnabled: boolean; @@ -239,6 +245,12 @@ export class DashboardSettingsComponent implements OnInit { this.uptimeKumaPushURL = config.metrics.uptime_kuma_push_url ?? ''; this.uptimeKumaIntervalSeconds = config.metrics.uptime_kuma_interval_seconds ?? 60; + // Navigation visibility settings + this.showZFSPools = config.navigation?.show_zfs_pools ?? true; + this.showMDADM = config.navigation?.show_mdadm ?? true; + this.showBtrfs = config.navigation?.show_btrfs ?? true; + this.showWorkload = config.navigation?.show_workload ?? true; + // Report settings this.reportEnabled = config.metrics.report_enabled ?? false; this.reportDailyEnabled = config.metrics.report_daily_enabled ?? false; @@ -501,6 +513,12 @@ export class DashboardSettingsComponent implements OnInit { saveSettings(): void { const newSettings: AppConfig = { + navigation: { + show_zfs_pools: this.showZFSPools, + show_mdadm: this.showMDADM, + show_btrfs: this.showBtrfs, + show_workload: this.showWorkload, + }, dashboard_display: this.dashboardDisplay as DashboardDisplay, dashboard_sort: this.dashboardSort as DashboardSort, temperature_unit: this.temperatureUnit as TemperatureUnit, diff --git a/webapp/frontend/src/app/layout/common/mobile-tab-bar/mobile-tab-bar.component.html b/webapp/frontend/src/app/layout/common/mobile-tab-bar/mobile-tab-bar.component.html index f5dff1cb9..89aa1d517 100644 --- a/webapp/frontend/src/app/layout/common/mobile-tab-bar/mobile-tab-bar.component.html +++ b/webapp/frontend/src/app/layout/common/mobile-tab-bar/mobile-tab-bar.component.html @@ -1,6 +1,6 @@ @if (!isDetailPage) {