From 2a6f721f4fa14274a1f5d696f19d6f89f683aeb9 Mon Sep 17 00:00:00 2001 From: Chrystian Huot Date: Tue, 11 Jan 2022 14:24:04 -0500 Subject: [PATCH] Version 6.1.3 --- CHANGELOG.md | 8 + COMPILING.md | 16 +- Makefile | 6 +- client/package-lock.json | 491 +++++++++--------- client/package.json | 10 +- .../src/app/shared/update/update.service.ts | 39 +- docs/faq.md | 4 + docs/platforms/darwin.md | 46 +- docs/platforms/freebsd.md | 46 +- docs/platforms/linux.md | 46 +- docs/platforms/windows.md | 44 +- server/access.go | 31 +- server/admin.go | 66 +-- server/api.go | 242 +-------- server/apikey.go | 31 +- server/call.go | 8 + server/controller.go | 10 +- server/database.go | 6 +- server/dirwatch.go | 73 +-- server/downstream.go | 26 +- server/go.mod | 8 +- server/go.sum | 33 +- server/group.go | 35 +- server/livefeed.go | 4 +- server/options.go | 16 + server/parsers.go | 163 ++++++ server/system.go | 38 +- server/tags.go | 34 +- server/version.go | 2 +- 29 files changed, 829 insertions(+), 753 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c47011b..c237f46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,14 @@ _v6.1.2_ - Dirwatch type trunk-recorder now deletes json files without audio (when deleteAfter is set). - Add a new `docs/update-from-v5.md` document. +_v6.1.3_ + +- Fix concurrent config write when autopopulate is enabled (issue #77). +- Fix API in regards to audio filename and audio type (issue #78). +- Fix some calls not playing on the native app (issue #87). +- Fix migration error on mysql database (issue #88). +- Fix admin password not read from mysql. + ## Version 6.0 - Backend server rewritten in Go for better performance and ease of installation. diff --git a/COMPILING.md b/COMPILING.md index d7138a4..b56bf4a 100644 --- a/COMPILING.md +++ b/COMPILING.md @@ -24,13 +24,13 @@ Clone the official repository on your computer and start the build process. When finished, you will find the precompiled versions for various platforms in the `dist` folder. - rdio-scanner-darwin-amd64-v6.1.2.zip - rdio-scanner-darwin-arm64-v6.1.2.zip - rdio-scanner-freebsd-amd64-v6.1.2.zip - rdio-scanner-linux-386-v6.1.2.zip - rdio-scanner-linux-amd64-v6.1.2.zip - rdio-scanner-linux-arm64-v6.1.2.zip - rdio-scanner-linux-arm-v6.1.2.zip - rdio-scanner-windows-amd64-v6.1.2.zip + rdio-scanner-darwin-amd64-v6.1.3.zip + rdio-scanner-darwin-arm64-v6.1.3.zip + rdio-scanner-freebsd-amd64-v6.1.3.zip + rdio-scanner-linux-386-v6.1.3.zip + rdio-scanner-linux-amd64-v6.1.3.zip + rdio-scanner-linux-arm64-v6.1.3.zip + rdio-scanner-linux-arm-v6.1.3.zip + rdio-scanner-windows-amd64-v6.1.3.zip **Happy Rdio scanning !** \ No newline at end of file diff --git a/Makefile b/Makefile index 776937a..72a83a7 100644 --- a/Makefile +++ b/Makefile @@ -16,8 +16,8 @@ ################################################################################ app := rdio-scanner -date := 2022/01/05 -ver := 6.1.2 +date := 2022/01/11 +ver := 6.1.3 client := $(wildcard client/*.json client/*.ts) server := $(wildcard server/*.go) @@ -39,7 +39,7 @@ clean: container: webapp linux-amd64 @podman build --platform linux/amd64,linux/arm,linux/arm64 --manifest rdio-scanner:latest . - @podman manifest push --format v2s2 localhost/rdio-scanner:latest docker://docker.io/chuot/rdio-scanner:latest + @podman manifest push --format v2s2 --rm localhost/rdio-scanner:latest docker://docker.io/chuot/rdio-scanner:latest dist: darwin freebsd linux windows diff --git a/client/package-lock.json b/client/package-lock.json index fdb6618..ffbd30d 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1,12 +1,12 @@ { "name": "rdio-scanner", - "version": "6.1.0", + "version": "6.1.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "rdio-scanner", - "version": "6.1.0", + "version": "6.1.3", "license": "LICENSE", "dependencies": { "@angular/animations": "^13.1.1", @@ -25,12 +25,12 @@ "zone.js": "~0.11.4" }, "devDependencies": { - "@angular-devkit/build-angular": "~13.1.2", + "@angular-devkit/build-angular": "^13.1.2", "@angular/cli": "^13.1.2", "@angular/compiler-cli": "~13.1.1", - "@types/node": "~17.0.7", - "@typescript-eslint/eslint-plugin": "^5.9.0", - "@typescript-eslint/parser": "^5.9.0", + "@types/node": "~17.0.8", + "@typescript-eslint/eslint-plugin": "^5.9.1", + "@typescript-eslint/parser": "^5.9.1", "ajv-keywords": "^5.1.0", "eslint": "^8.6.0", "npm-check-updates": "^12.1.0", @@ -612,9 +612,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.16.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.4.tgz", - "integrity": "sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.8.tgz", + "integrity": "sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q==", "dev": true, "engines": { "node": ">=6.9.0" @@ -987,14 +987,14 @@ } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.7.tgz", - "integrity": "sha512-C3o117GnP/j/N2OWo+oepeWbFEKRfNaay+F1Eo5Mj3A1SRjyx+qaFhm23nlipub7Cjv2azdUUiDH+VlpdwUFRg==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz", + "integrity": "sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-wrap-function": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/helper-wrap-function": "^7.16.8", + "@babel/types": "^7.16.8" }, "engines": { "node": ">=6.9.0" @@ -1083,15 +1083,15 @@ } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.7.tgz", - "integrity": "sha512-7a9sABeVwcunnztZZ7WTgSw6jVYLzM1wua0Z4HIXm9S3/HC96WKQTkFgGEaj5W06SHHihPJ6Le6HzS5cGOQMNw==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz", + "integrity": "sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==", "dev": true, "dependencies": { "@babel/helper-function-name": "^7.16.7", "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/traverse": "^7.16.8", + "@babel/types": "^7.16.8" }, "engines": { "node": ">=6.9.0" @@ -1154,9 +1154,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.7.tgz", - "integrity": "sha512-sR4eaSrnM7BV7QPzGfEX5paG/6wrZM3I0HDzfIAK06ESvo9oy3xBuVBxE3MbQaKNhvg8g/ixjMWo2CGpzpHsDA==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.8.tgz", + "integrity": "sha512-i7jDUfrVBWc+7OKcBzEe5n7fbv3i2fWtxKzzCvOjnzSxMfWMigAhtfJ7qzZNGFNMsCCd67+uz553dYKWXPvCKw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -1887,9 +1887,9 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.7.tgz", - "integrity": "sha512-h2RP2kE7He1ZWKyAlanMZrAbdv+Acw1pA8dQZhE025WJZE2z0xzFADAinXA9fxd5bn7JnM+SdOGcndGx1ARs9w==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz", + "integrity": "sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA==", "dev": true, "dependencies": { "@babel/helper-module-transforms": "^7.16.7", @@ -1940,9 +1940,9 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.7.tgz", - "integrity": "sha512-kFy35VwmwIQwCjwrAQhl3+c/kr292i4KdLPKp5lPH03Ltc51qnFlIADoyPxc/6Naz3ok3WdYKg+KK6AH+D4utg==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz", + "integrity": "sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==", "dev": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.16.7" @@ -2321,19 +2321,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.7.tgz", - "integrity": "sha512-8KWJPIb8c2VvY8AJrydh6+fVRo2ODx1wYBU2398xJVq0JomuLBZmVQzLPBblJgHIGYG4znCpUZUZ0Pt2vdmVYQ==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.8.tgz", + "integrity": "sha512-xe+H7JlvKsDQwXRsBhSnq1/+9c+LlQcCK3Tn/l5sbx02HYns/cn7ibp9+RV1sIUqu7hKg91NWsgHurO9dowITQ==", "dev": true, "dependencies": { "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.16.7", + "@babel/generator": "^7.16.8", "@babel/helper-environment-visitor": "^7.16.7", "@babel/helper-function-name": "^7.16.7", "@babel/helper-hoist-variables": "^7.16.7", "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7", + "@babel/parser": "^7.16.8", + "@babel/types": "^7.16.8", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -2342,12 +2342,12 @@ } }, "node_modules/@babel/traverse/node_modules/@babel/generator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.7.tgz", - "integrity": "sha512-/ST3Sg8MLGY5HVYmrjOgL60ENux/HfO/CsUh7y4MalThufhE/Ff/6EibFDHi4jiDCaWfJKoqbE6oTh21c5hrRg==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz", + "integrity": "sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7", + "@babel/types": "^7.16.8", "jsesc": "^2.5.1", "source-map": "^0.5.0" }, @@ -2365,9 +2365,9 @@ } }, "node_modules/@babel/types": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.7.tgz", - "integrity": "sha512-E8HuV7FO9qLpx6OtoGfUQ2cjIYnbFwvZWYBS+87EwtdMvmUPJSwykpovFB+8insbpF0uJcpr8KMUi64XZntZcg==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.8.tgz", + "integrity": "sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.16.7", @@ -2763,9 +2763,9 @@ "dev": true }, "node_modules/@types/eslint": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.2.1.tgz", - "integrity": "sha512-UP9rzNn/XyGwb5RQ2fok+DzcIRIYwc16qTXse5+Smsy8MOIccCChT15KAwnsgQx4PzJkaMq4myFyZ4CL5TjhIQ==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.2.2.tgz", + "integrity": "sha512-nQxgB8/Sg+QKhnV8e0WzPpxjIGT3tuJDDzybkDi8ItE/IgTlHo07U0shaIjzhcvQxlq9SDRE42lsJ23uvEgJ2A==", "dev": true, "dependencies": { "@types/estree": "*", @@ -2773,9 +2773,9 @@ } }, "node_modules/@types/eslint-scope": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.2.tgz", - "integrity": "sha512-TzgYCWoPiTeRg6RQYgtuW7iODtVoKu3RVL72k3WohqhjfaOLK5Mg2T4Tg1o2bSfu0vPkoI48wdQFv5b/Xe04wQ==", + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", + "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", "dev": true, "dependencies": { "@types/eslint": "*", @@ -2804,9 +2804,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "17.0.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.7.tgz", - "integrity": "sha512-1QUk+WAUD4t8iR+Oj+UgI8oJa6yyxaB8a8pHaC8uqM6RrS1qbL7bf3Pwl5rHv0psm2CuDErgho6v5N+G+5fwtQ==", + "version": "17.0.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.8.tgz", + "integrity": "sha512-YofkM6fGv4gDJq78g4j0mMuGMkZVxZDgtU0JRdx6FgiJDG+0fY0GKVolOV8WqVmEhLCXkQRjwDdKyPxJp/uucg==", "dev": true }, "node_modules/@types/parse-json": { @@ -2822,14 +2822,14 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.9.0.tgz", - "integrity": "sha512-qT4lr2jysDQBQOPsCCvpPUZHjbABoTJW8V9ZzIYKHMfppJtpdtzszDYsldwhFxlhvrp7aCHeXD1Lb9M1zhwWwQ==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.9.1.tgz", + "integrity": "sha512-Xv9tkFlyD4MQGpJgTo6wqDqGvHIRmRgah/2Sjz1PUnJTawjHWIwBivUE9x0QtU2WVii9baYgavo/bHjrZJkqTw==", "dev": true, "dependencies": { - "@typescript-eslint/experimental-utils": "5.9.0", - "@typescript-eslint/scope-manager": "5.9.0", - "@typescript-eslint/type-utils": "5.9.0", + "@typescript-eslint/experimental-utils": "5.9.1", + "@typescript-eslint/scope-manager": "5.9.1", + "@typescript-eslint/type-utils": "5.9.1", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -2876,15 +2876,15 @@ } }, "node_modules/@typescript-eslint/experimental-utils": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.9.0.tgz", - "integrity": "sha512-ZnLVjBrf26dn7ElyaSKa6uDhqwvAi4jBBmHK1VxuFGPRAxhdi18ubQYSGA7SRiFiES3q9JiBOBHEBStOFkwD2g==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.9.1.tgz", + "integrity": "sha512-cb1Njyss0mLL9kLXgS/eEY53SZQ9sT519wpX3i+U457l2UXRDuo87hgKfgRazmu9/tQb0x2sr3Y0yrU+Zz0y+w==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.9.0", - "@typescript-eslint/types": "5.9.0", - "@typescript-eslint/typescript-estree": "5.9.0", + "@typescript-eslint/scope-manager": "5.9.1", + "@typescript-eslint/types": "5.9.1", + "@typescript-eslint/typescript-estree": "5.9.1", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -2900,14 +2900,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.9.0.tgz", - "integrity": "sha512-/6pOPz8yAxEt4PLzgbFRDpZmHnXCeZgPDrh/1DaVKOjvn/UPMlWhbx/gA96xRi2JxY1kBl2AmwVbyROUqys5xQ==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.9.1.tgz", + "integrity": "sha512-PLYO0AmwD6s6n0ZQB5kqPgfvh73p0+VqopQQLuNfi7Lm0EpfKyDalchpVwkE+81k5HeiRrTV/9w1aNHzjD7C4g==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.9.0", - "@typescript-eslint/types": "5.9.0", - "@typescript-eslint/typescript-estree": "5.9.0", + "@typescript-eslint/scope-manager": "5.9.1", + "@typescript-eslint/types": "5.9.1", + "@typescript-eslint/typescript-estree": "5.9.1", "debug": "^4.3.2" }, "engines": { @@ -2927,13 +2927,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.9.0.tgz", - "integrity": "sha512-DKtdIL49Qxk2a8icF6whRk7uThuVz4A6TCXfjdJSwOsf+9ree7vgQWcx0KOyCdk0i9ETX666p4aMhrRhxhUkyg==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.9.1.tgz", + "integrity": "sha512-8BwvWkho3B/UOtzRyW07ffJXPaLSUKFBjpq8aqsRvu6HdEuzCY57+ffT7QoV4QXJXWSU1+7g3wE4AlgImmQ9pQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.9.0", - "@typescript-eslint/visitor-keys": "5.9.0" + "@typescript-eslint/types": "5.9.1", + "@typescript-eslint/visitor-keys": "5.9.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2944,12 +2944,12 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.9.0.tgz", - "integrity": "sha512-uVCb9dJXpBrK1071ri5aEW7ZHdDHAiqEjYznF3HSSvAJXyrkxGOw2Ejibz/q6BXdT8lea8CMI0CzKNFTNI6TEQ==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.9.1.tgz", + "integrity": "sha512-tRSpdBnPRssjlUh35rE9ug5HrUvaB9ntREy7gPXXKwmIx61TNN7+l5YKgi1hMKxo5NvqZCfYhA5FvyuJG6X6vg==", "dev": true, "dependencies": { - "@typescript-eslint/experimental-utils": "5.9.0", + "@typescript-eslint/experimental-utils": "5.9.1", "debug": "^4.3.2", "tsutils": "^3.21.0" }, @@ -2991,9 +2991,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.9.0.tgz", - "integrity": "sha512-mWp6/b56Umo1rwyGCk8fPIzb9Migo8YOniBGPAQDNC6C52SeyNGN4gsVwQTAR+RS2L5xyajON4hOLwAGwPtUwg==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.9.1.tgz", + "integrity": "sha512-SsWegWudWpkZCwwYcKoDwuAjoZXnM1y2EbEerTHho19Hmm+bQ56QG4L4jrtCu0bI5STaRTvRTZmjprWlTw/5NQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -3004,13 +3004,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.9.0.tgz", - "integrity": "sha512-kxo3xL2mB7XmiVZcECbaDwYCt3qFXz99tBSuVJR4L/sR7CJ+UNAPrYILILktGj1ppfZ/jNt/cWYbziJUlHl1Pw==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.9.1.tgz", + "integrity": "sha512-gL1sP6A/KG0HwrahVXI9fZyeVTxEYV//6PmcOn1tD0rw8VhUWYeZeuWHwwhnewnvEMcHjhnJLOBhA9rK4vmb8A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.9.0", - "@typescript-eslint/visitor-keys": "5.9.0", + "@typescript-eslint/types": "5.9.1", + "@typescript-eslint/visitor-keys": "5.9.1", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -3052,12 +3052,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.9.0.tgz", - "integrity": "sha512-6zq0mb7LV0ThExKlecvpfepiB+XEtFv/bzx7/jKSgyXTFD7qjmSu1FoiS0x3OZaiS+UIXpH2vd9O89f02RCtgw==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.9.1.tgz", + "integrity": "sha512-Xh37pNz9e9ryW4TVdwiFzmr4hloty8cFj8GTWMXh3Z8swGwyQWeCcNgF0hm6t09iZd6eiZmIf4zHedQVP6TVtg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.9.0", + "@typescript-eslint/types": "5.9.1", "eslint-visitor-keys": "^3.0.0" }, "engines": { @@ -4169,9 +4169,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001296", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001296.tgz", - "integrity": "sha512-WfrtPEoNSoeATDlf4y3QvkwiELl9GyPLISV5GejTbbQRtQx4LhsXmc9IQ6XCL2d7UxCyEzToEZNMeqR79OUw8Q==", + "version": "1.0.30001298", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001298.tgz", + "integrity": "sha512-AcKqikjMLlvghZL/vfTHorlQsLDhGRalYf1+GmWCf5SCMziSGjRYQW/JEksj14NaYHIR6KIhrFAy0HV5C25UzQ==", "dev": true, "funding": { "type": "opencollective", @@ -5435,9 +5435,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.34", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.34.tgz", - "integrity": "sha512-B7g6Y9No9XMYk1VNrQ8KAmSEo1Iltrz/5EjOGxl1DffQAb3z/XbpHRCfYKwV8D+CPXm4Q7Xg1sceSt9osNwRIA==", + "version": "1.4.40", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.40.tgz", + "integrity": "sha512-j+eVIyQGt2EU5xPWUblhpp5P5z5xyAdRgzogBgfe2F5JGV17gr9pfzWBua6DlPL00LavbOjxubWkWkbVQe9Wlw==", "dev": true }, "node_modules/emoji-regex": { @@ -6420,9 +6420,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "version": "3.2.10", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.10.tgz", + "integrity": "sha512-s9nFhFnvR63wls6/kM88kQqDhMu0AfdjqouE2l5GVQPbqLgyFjjU5ry/r2yKsJxpb9Py1EYNqieFrmMaX4v++A==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -6432,7 +6432,7 @@ "micromatch": "^4.0.4" }, "engines": { - "node": ">=8" + "node": ">=8.6.0" } }, "node_modules/fast-json-stable-stringify": { @@ -6609,9 +6609,9 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.14.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz", - "integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==", + "version": "1.14.7", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", + "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==", "dev": true, "funding": [ { @@ -6853,16 +6853,16 @@ } }, "node_modules/globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" }, "engines": { @@ -6895,9 +6895,9 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", "dev": true }, "node_modules/handle-thing": { @@ -6987,9 +6987,9 @@ "dev": true }, "node_modules/hosted-git-info": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", - "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -7479,9 +7479,9 @@ } }, "node_modules/is-core-module": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", - "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -7779,9 +7779,9 @@ } }, "node_modules/jest-worker": { - "version": "27.4.5", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.5.tgz", - "integrity": "sha512-f2s8kEdy15cv9r7q4KkzGXvlY0JTcmCbMHZBfSQDwW77REr45IDWwd0lksDFeVHH2jJ5pqb90T77XscrjeGzzg==", + "version": "27.4.6", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.6.tgz", + "integrity": "sha512-gHWJF/6Xi5CTG5QCvROr6GcmpIqNYpDJyc8A1h/DyXqH1tD6SnRCM0d3U5msV31D2LB/U+E0M+W4oyvKV44oNw==", "dev": true, "dependencies": { "@types/node": "*", @@ -8716,9 +8716,9 @@ "dev": true }, "node_modules/nanoid": { - "version": "3.1.30", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz", - "integrity": "sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==", + "version": "3.1.31", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.31.tgz", + "integrity": "sha512-ZivnJm0o9bb13p2Ot5CpgC2rQdzB9Uxm/mFZweqm5eMViqOJe3PV6LU2E30SiLgheesmcPrjquqraoolONSA0A==", "dev": true, "bin": { "nanoid": "bin/nanoid.cjs" @@ -12227,9 +12227,9 @@ "dev": true }, "node_modules/selfsigned": { - "version": "1.10.11", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.11.tgz", - "integrity": "sha512-aVmbPOfViZqOZPgRBT0+3u4yZFHpmnIghLMlAcb5/xhp5ZtB/RVnKhz5vl2M32CLXAqR4kha9zfhNg0Lf/sxKA==", + "version": "1.10.14", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.14.tgz", + "integrity": "sha512-lkjaiAye+wBZDCBsu5BGi0XiLRxeUlsGod5ZP924CRSEoGuZAw/f7y9RKu28rwTfiHVhdavhB0qH0INV6P1lEA==", "dev": true, "dependencies": { "node-forge": "^0.10.0" @@ -12585,6 +12585,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", "dev": true, "dependencies": { "atob": "^2.1.2", @@ -14401,9 +14402,9 @@ } }, "@babel/compat-data": { - "version": "7.16.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.4.tgz", - "integrity": "sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.8.tgz", + "integrity": "sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q==", "dev": true }, "@babel/core": { @@ -14695,14 +14696,14 @@ "dev": true }, "@babel/helper-remap-async-to-generator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.7.tgz", - "integrity": "sha512-C3o117GnP/j/N2OWo+oepeWbFEKRfNaay+F1Eo5Mj3A1SRjyx+qaFhm23nlipub7Cjv2azdUUiDH+VlpdwUFRg==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz", + "integrity": "sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-wrap-function": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/helper-wrap-function": "^7.16.8", + "@babel/types": "^7.16.8" }, "dependencies": { "@babel/helper-annotate-as-pure": { @@ -14769,15 +14770,15 @@ "dev": true }, "@babel/helper-wrap-function": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.7.tgz", - "integrity": "sha512-7a9sABeVwcunnztZZ7WTgSw6jVYLzM1wua0Z4HIXm9S3/HC96WKQTkFgGEaj5W06SHHihPJ6Le6HzS5cGOQMNw==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz", + "integrity": "sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==", "dev": true, "requires": { "@babel/helper-function-name": "^7.16.7", "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/traverse": "^7.16.8", + "@babel/types": "^7.16.8" }, "dependencies": { "@babel/template": { @@ -14829,9 +14830,9 @@ } }, "@babel/parser": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.7.tgz", - "integrity": "sha512-sR4eaSrnM7BV7QPzGfEX5paG/6wrZM3I0HDzfIAK06ESvo9oy3xBuVBxE3MbQaKNhvg8g/ixjMWo2CGpzpHsDA==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.8.tgz", + "integrity": "sha512-i7jDUfrVBWc+7OKcBzEe5n7fbv3i2fWtxKzzCvOjnzSxMfWMigAhtfJ7qzZNGFNMsCCd67+uz553dYKWXPvCKw==", "dev": true }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { @@ -15311,9 +15312,9 @@ } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.7.tgz", - "integrity": "sha512-h2RP2kE7He1ZWKyAlanMZrAbdv+Acw1pA8dQZhE025WJZE2z0xzFADAinXA9fxd5bn7JnM+SdOGcndGx1ARs9w==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz", + "integrity": "sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA==", "dev": true, "requires": { "@babel/helper-module-transforms": "^7.16.7", @@ -15346,9 +15347,9 @@ } }, "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.7.tgz", - "integrity": "sha512-kFy35VwmwIQwCjwrAQhl3+c/kr292i4KdLPKp5lPH03Ltc51qnFlIADoyPxc/6Naz3ok3WdYKg+KK6AH+D4utg==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz", + "integrity": "sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==", "dev": true, "requires": { "@babel/helper-create-regexp-features-plugin": "^7.16.7" @@ -15620,30 +15621,30 @@ } }, "@babel/traverse": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.7.tgz", - "integrity": "sha512-8KWJPIb8c2VvY8AJrydh6+fVRo2ODx1wYBU2398xJVq0JomuLBZmVQzLPBblJgHIGYG4znCpUZUZ0Pt2vdmVYQ==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.8.tgz", + "integrity": "sha512-xe+H7JlvKsDQwXRsBhSnq1/+9c+LlQcCK3Tn/l5sbx02HYns/cn7ibp9+RV1sIUqu7hKg91NWsgHurO9dowITQ==", "dev": true, "requires": { "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.16.7", + "@babel/generator": "^7.16.8", "@babel/helper-environment-visitor": "^7.16.7", "@babel/helper-function-name": "^7.16.7", "@babel/helper-hoist-variables": "^7.16.7", "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7", + "@babel/parser": "^7.16.8", + "@babel/types": "^7.16.8", "debug": "^4.1.0", "globals": "^11.1.0" }, "dependencies": { "@babel/generator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.7.tgz", - "integrity": "sha512-/ST3Sg8MLGY5HVYmrjOgL60ENux/HfO/CsUh7y4MalThufhE/Ff/6EibFDHi4jiDCaWfJKoqbE6oTh21c5hrRg==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz", + "integrity": "sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==", "dev": true, "requires": { - "@babel/types": "^7.16.7", + "@babel/types": "^7.16.8", "jsesc": "^2.5.1", "source-map": "^0.5.0" } @@ -15657,9 +15658,9 @@ } }, "@babel/types": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.7.tgz", - "integrity": "sha512-E8HuV7FO9qLpx6OtoGfUQ2cjIYnbFwvZWYBS+87EwtdMvmUPJSwykpovFB+8insbpF0uJcpr8KMUi64XZntZcg==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.8.tgz", + "integrity": "sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.16.7", @@ -15970,9 +15971,9 @@ "dev": true }, "@types/eslint": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.2.1.tgz", - "integrity": "sha512-UP9rzNn/XyGwb5RQ2fok+DzcIRIYwc16qTXse5+Smsy8MOIccCChT15KAwnsgQx4PzJkaMq4myFyZ4CL5TjhIQ==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.2.2.tgz", + "integrity": "sha512-nQxgB8/Sg+QKhnV8e0WzPpxjIGT3tuJDDzybkDi8ItE/IgTlHo07U0shaIjzhcvQxlq9SDRE42lsJ23uvEgJ2A==", "dev": true, "requires": { "@types/estree": "*", @@ -15980,9 +15981,9 @@ } }, "@types/eslint-scope": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.2.tgz", - "integrity": "sha512-TzgYCWoPiTeRg6RQYgtuW7iODtVoKu3RVL72k3WohqhjfaOLK5Mg2T4Tg1o2bSfu0vPkoI48wdQFv5b/Xe04wQ==", + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", + "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", "dev": true, "requires": { "@types/eslint": "*", @@ -16011,9 +16012,9 @@ "dev": true }, "@types/node": { - "version": "17.0.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.7.tgz", - "integrity": "sha512-1QUk+WAUD4t8iR+Oj+UgI8oJa6yyxaB8a8pHaC8uqM6RrS1qbL7bf3Pwl5rHv0psm2CuDErgho6v5N+G+5fwtQ==", + "version": "17.0.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.8.tgz", + "integrity": "sha512-YofkM6fGv4gDJq78g4j0mMuGMkZVxZDgtU0JRdx6FgiJDG+0fY0GKVolOV8WqVmEhLCXkQRjwDdKyPxJp/uucg==", "dev": true }, "@types/parse-json": { @@ -16029,14 +16030,14 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.9.0.tgz", - "integrity": "sha512-qT4lr2jysDQBQOPsCCvpPUZHjbABoTJW8V9ZzIYKHMfppJtpdtzszDYsldwhFxlhvrp7aCHeXD1Lb9M1zhwWwQ==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.9.1.tgz", + "integrity": "sha512-Xv9tkFlyD4MQGpJgTo6wqDqGvHIRmRgah/2Sjz1PUnJTawjHWIwBivUE9x0QtU2WVii9baYgavo/bHjrZJkqTw==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "5.9.0", - "@typescript-eslint/scope-manager": "5.9.0", - "@typescript-eslint/type-utils": "5.9.0", + "@typescript-eslint/experimental-utils": "5.9.1", + "@typescript-eslint/scope-manager": "5.9.1", + "@typescript-eslint/type-utils": "5.9.1", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -16063,48 +16064,48 @@ } }, "@typescript-eslint/experimental-utils": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.9.0.tgz", - "integrity": "sha512-ZnLVjBrf26dn7ElyaSKa6uDhqwvAi4jBBmHK1VxuFGPRAxhdi18ubQYSGA7SRiFiES3q9JiBOBHEBStOFkwD2g==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.9.1.tgz", + "integrity": "sha512-cb1Njyss0mLL9kLXgS/eEY53SZQ9sT519wpX3i+U457l2UXRDuo87hgKfgRazmu9/tQb0x2sr3Y0yrU+Zz0y+w==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.9.0", - "@typescript-eslint/types": "5.9.0", - "@typescript-eslint/typescript-estree": "5.9.0", + "@typescript-eslint/scope-manager": "5.9.1", + "@typescript-eslint/types": "5.9.1", + "@typescript-eslint/typescript-estree": "5.9.1", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" } }, "@typescript-eslint/parser": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.9.0.tgz", - "integrity": "sha512-/6pOPz8yAxEt4PLzgbFRDpZmHnXCeZgPDrh/1DaVKOjvn/UPMlWhbx/gA96xRi2JxY1kBl2AmwVbyROUqys5xQ==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.9.1.tgz", + "integrity": "sha512-PLYO0AmwD6s6n0ZQB5kqPgfvh73p0+VqopQQLuNfi7Lm0EpfKyDalchpVwkE+81k5HeiRrTV/9w1aNHzjD7C4g==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.9.0", - "@typescript-eslint/types": "5.9.0", - "@typescript-eslint/typescript-estree": "5.9.0", + "@typescript-eslint/scope-manager": "5.9.1", + "@typescript-eslint/types": "5.9.1", + "@typescript-eslint/typescript-estree": "5.9.1", "debug": "^4.3.2" } }, "@typescript-eslint/scope-manager": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.9.0.tgz", - "integrity": "sha512-DKtdIL49Qxk2a8icF6whRk7uThuVz4A6TCXfjdJSwOsf+9ree7vgQWcx0KOyCdk0i9ETX666p4aMhrRhxhUkyg==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.9.1.tgz", + "integrity": "sha512-8BwvWkho3B/UOtzRyW07ffJXPaLSUKFBjpq8aqsRvu6HdEuzCY57+ffT7QoV4QXJXWSU1+7g3wE4AlgImmQ9pQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.9.0", - "@typescript-eslint/visitor-keys": "5.9.0" + "@typescript-eslint/types": "5.9.1", + "@typescript-eslint/visitor-keys": "5.9.1" } }, "@typescript-eslint/type-utils": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.9.0.tgz", - "integrity": "sha512-uVCb9dJXpBrK1071ri5aEW7ZHdDHAiqEjYznF3HSSvAJXyrkxGOw2Ejibz/q6BXdT8lea8CMI0CzKNFTNI6TEQ==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.9.1.tgz", + "integrity": "sha512-tRSpdBnPRssjlUh35rE9ug5HrUvaB9ntREy7gPXXKwmIx61TNN7+l5YKgi1hMKxo5NvqZCfYhA5FvyuJG6X6vg==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "5.9.0", + "@typescript-eslint/experimental-utils": "5.9.1", "debug": "^4.3.2", "tsutils": "^3.21.0" }, @@ -16127,19 +16128,19 @@ } }, "@typescript-eslint/types": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.9.0.tgz", - "integrity": "sha512-mWp6/b56Umo1rwyGCk8fPIzb9Migo8YOniBGPAQDNC6C52SeyNGN4gsVwQTAR+RS2L5xyajON4hOLwAGwPtUwg==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.9.1.tgz", + "integrity": "sha512-SsWegWudWpkZCwwYcKoDwuAjoZXnM1y2EbEerTHho19Hmm+bQ56QG4L4jrtCu0bI5STaRTvRTZmjprWlTw/5NQ==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.9.0.tgz", - "integrity": "sha512-kxo3xL2mB7XmiVZcECbaDwYCt3qFXz99tBSuVJR4L/sR7CJ+UNAPrYILILktGj1ppfZ/jNt/cWYbziJUlHl1Pw==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.9.1.tgz", + "integrity": "sha512-gL1sP6A/KG0HwrahVXI9fZyeVTxEYV//6PmcOn1tD0rw8VhUWYeZeuWHwwhnewnvEMcHjhnJLOBhA9rK4vmb8A==", "dev": true, "requires": { - "@typescript-eslint/types": "5.9.0", - "@typescript-eslint/visitor-keys": "5.9.0", + "@typescript-eslint/types": "5.9.1", + "@typescript-eslint/visitor-keys": "5.9.1", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -16165,12 +16166,12 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.9.0.tgz", - "integrity": "sha512-6zq0mb7LV0ThExKlecvpfepiB+XEtFv/bzx7/jKSgyXTFD7qjmSu1FoiS0x3OZaiS+UIXpH2vd9O89f02RCtgw==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.9.1.tgz", + "integrity": "sha512-Xh37pNz9e9ryW4TVdwiFzmr4hloty8cFj8GTWMXh3Z8swGwyQWeCcNgF0hm6t09iZd6eiZmIf4zHedQVP6TVtg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.9.0", + "@typescript-eslint/types": "5.9.1", "eslint-visitor-keys": "^3.0.0" } }, @@ -17043,9 +17044,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001296", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001296.tgz", - "integrity": "sha512-WfrtPEoNSoeATDlf4y3QvkwiELl9GyPLISV5GejTbbQRtQx4LhsXmc9IQ6XCL2d7UxCyEzToEZNMeqR79OUw8Q==", + "version": "1.0.30001298", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001298.tgz", + "integrity": "sha512-AcKqikjMLlvghZL/vfTHorlQsLDhGRalYf1+GmWCf5SCMziSGjRYQW/JEksj14NaYHIR6KIhrFAy0HV5C25UzQ==", "dev": true }, "canonical-path": { @@ -17994,9 +17995,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.4.34", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.34.tgz", - "integrity": "sha512-B7g6Y9No9XMYk1VNrQ8KAmSEo1Iltrz/5EjOGxl1DffQAb3z/XbpHRCfYKwV8D+CPXm4Q7Xg1sceSt9osNwRIA==", + "version": "1.4.40", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.40.tgz", + "integrity": "sha512-j+eVIyQGt2EU5xPWUblhpp5P5z5xyAdRgzogBgfe2F5JGV17gr9pfzWBua6DlPL00LavbOjxubWkWkbVQe9Wlw==", "dev": true }, "emoji-regex": { @@ -18697,9 +18698,9 @@ "dev": true }, "fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "version": "3.2.10", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.10.tgz", + "integrity": "sha512-s9nFhFnvR63wls6/kM88kQqDhMu0AfdjqouE2l5GVQPbqLgyFjjU5ry/r2yKsJxpb9Py1EYNqieFrmMaX4v++A==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -18854,9 +18855,9 @@ "dev": true }, "follow-redirects": { - "version": "1.14.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz", - "integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==", + "version": "1.14.7", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", + "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==", "dev": true }, "forwarded": { @@ -19023,16 +19024,16 @@ "dev": true }, "globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "requires": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" } }, @@ -19056,9 +19057,9 @@ } }, "graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", "dev": true }, "handle-thing": { @@ -19127,9 +19128,9 @@ "dev": true }, "hosted-git-info": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", - "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -19510,9 +19511,9 @@ } }, "is-core-module": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", - "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", "dev": true, "requires": { "has": "^1.0.3" @@ -19716,9 +19717,9 @@ } }, "jest-worker": { - "version": "27.4.5", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.5.tgz", - "integrity": "sha512-f2s8kEdy15cv9r7q4KkzGXvlY0JTcmCbMHZBfSQDwW77REr45IDWwd0lksDFeVHH2jJ5pqb90T77XscrjeGzzg==", + "version": "27.4.6", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.6.tgz", + "integrity": "sha512-gHWJF/6Xi5CTG5QCvROr6GcmpIqNYpDJyc8A1h/DyXqH1tD6SnRCM0d3U5msV31D2LB/U+E0M+W4oyvKV44oNw==", "dev": true, "requires": { "@types/node": "*", @@ -20430,9 +20431,9 @@ "dev": true }, "nanoid": { - "version": "3.1.30", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz", - "integrity": "sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==", + "version": "3.1.31", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.31.tgz", + "integrity": "sha512-ZivnJm0o9bb13p2Ot5CpgC2rQdzB9Uxm/mFZweqm5eMViqOJe3PV6LU2E30SiLgheesmcPrjquqraoolONSA0A==", "dev": true }, "natural-compare": { @@ -23071,9 +23072,9 @@ "dev": true }, "selfsigned": { - "version": "1.10.11", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.11.tgz", - "integrity": "sha512-aVmbPOfViZqOZPgRBT0+3u4yZFHpmnIghLMlAcb5/xhp5ZtB/RVnKhz5vl2M32CLXAqR4kha9zfhNg0Lf/sxKA==", + "version": "1.10.14", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.14.tgz", + "integrity": "sha512-lkjaiAye+wBZDCBsu5BGi0XiLRxeUlsGod5ZP924CRSEoGuZAw/f7y9RKu28rwTfiHVhdavhB0qH0INV6P1lEA==", "dev": true, "requires": { "node-forge": "^0.10.0" diff --git a/client/package.json b/client/package.json index 5066753..d9cbb68 100644 --- a/client/package.json +++ b/client/package.json @@ -17,12 +17,12 @@ "zone.js": "~0.11.4" }, "devDependencies": { - "@angular-devkit/build-angular": "~13.1.2", + "@angular-devkit/build-angular": "^13.1.2", "@angular/cli": "^13.1.2", "@angular/compiler-cli": "~13.1.1", - "@types/node": "~17.0.7", - "@typescript-eslint/eslint-plugin": "^5.9.0", - "@typescript-eslint/parser": "^5.9.0", + "@types/node": "~17.0.8", + "@typescript-eslint/eslint-plugin": "^5.9.1", + "@typescript-eslint/parser": "^5.9.1", "ajv-keywords": "^5.1.0", "eslint": "^8.6.0", "npm-check-updates": "^12.1.0", @@ -39,5 +39,5 @@ "build": "ng build --base-href ./ --configuration production", "start": "ng serve" }, - "version": "6.1.2" + "version": "6.1.3" } diff --git a/client/src/app/shared/update/update.service.ts b/client/src/app/shared/update/update.service.ts index 59f26e3..1eb0dfb 100644 --- a/client/src/app/shared/update/update.service.ts +++ b/client/src/app/shared/update/update.service.ts @@ -17,42 +17,45 @@ * **************************************************************************** */ -import { Injectable } from '@angular/core'; +import { ApplicationRef, Injectable } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; -import { SwUpdate } from '@angular/service-worker'; +import { SwUpdate, VersionEvent } from '@angular/service-worker'; import { AppUpdateComponent } from './update.component'; +import { concat, interval } from 'rxjs'; +import { first } from 'rxjs/operators'; @Injectable() export class AppUpdateService { - private prompted: boolean = false; - constructor( private matDialog: MatDialog, - private swUpdate: SwUpdate, + private ngAppRef: ApplicationRef, + private ngSwUpdate: SwUpdate, ) { - if (swUpdate.isEnabled) { - swUpdate.versionUpdates.subscribe(() => this.prompt()); - setInterval(() => swUpdate.checkForUpdate(), 60 * 60 * 1000); - } - } - - prompt(): void { - if (this.prompted) { + if (!ngSwUpdate.isEnabled) { return; } - this.prompted = true + concat( + this.ngAppRef.isStable.pipe(first((stable) => stable === true)), + interval(10 * 60 * 1000), + ).subscribe(() => this.ngSwUpdate.checkForUpdate()); + this.ngSwUpdate.versionUpdates.subscribe((event: VersionEvent) => { + if (event.type === 'VERSION_READY') { + this.prompt(); + } + }); + } + + prompt(): void { this.matDialog.open(AppUpdateComponent).afterClosed().subscribe((doUpdate) => { if (doUpdate) { - if (this.swUpdate.isEnabled) { - this.swUpdate.activateUpdate().then(() => document.location.reload()); + if (this.ngSwUpdate.isEnabled) { + this.ngSwUpdate.activateUpdate().then(() => document.location.reload()); } else { document.location.reload(); } } - - this.prompted = false; }); } } \ No newline at end of file diff --git a/docs/faq.md b/docs/faq.md index 8f7caa6..fdf7d11 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -16,4 +16,8 @@ A: No problem, just drop us a line at **[rdio-scanner@saubeo.solutions](mailto:r A: First, don't use the update.js script, it won't work since the server portion of version 6 as been completely rewritten in GO. Simply unzip the archive that contains the Rdio Scanner executable and its PDF document to a new folder, then copy the database.sqlite from version 5 to the new folder that contains version 6 and make sure to rename it to _rdio-scanner.db_. +**Q: The web app keeps displaying a dialog telling me that a new version is available** + +A: Force a refresh of the web application from the browser (usually with ctrl-shift-r) to resolve the issue. Alternatively, you can click on the icon just to the left of the URL address and select website settings, then clear all website data. + \pagebreak{} \ No newline at end of file diff --git a/docs/platforms/darwin.md b/docs/platforms/darwin.md index 4e0e91b..0aca720 100644 --- a/docs/platforms/darwin.md +++ b/docs/platforms/darwin.md @@ -42,8 +42,8 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd rdio@macos ~ % mkdir rdio-scanner rdio@macos ~ % cd rdio-scanner rdio@macos rdio-scanner % unzip \ - > ~/Downloads/rdio-scanner-darwin-arm64-v6.1.2.zip - Archive: /Users/rdio/Downloads/rdio-scanner-darwin-arm64-v6.1.2.zip + > ~/Downloads/rdio-scanner-darwin-arm64-v6.1.3.zip + Archive: /Users/rdio/Downloads/rdio-scanner-darwin-arm64-v6.1.3.zip inflating: rdio-scanner inflating: rdio-scanner.pdf @@ -51,11 +51,11 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd rdio@macos rdio-scanner % ./rdio-scanner - Rdio Scanner v6.1.2 + Rdio Scanner v6.1.3 ---------------------------------- - 2022/01/05 08:38:06 server started - 2022/01/05 08:38:06 main interface at http://macos.local:3000 - 2022/01/05 08:38:06 admin interface at http://macos.local:3000/admin + 2022/01/11 08:38:06 server started + 2022/01/11 08:38:06 main interface at http://macos.local:3000 + 2022/01/11 08:38:06 admin interface at http://macos.local:3000/admin 4. Access the administrative dashboard to finalize the configuration. @@ -77,11 +77,11 @@ Here we want our [Rdio Scanner](https://guthub.com/chuot/rdio-scanner) instance rdio@macos rdio-scanner % ./rdio-scanner --listen :80 - Rdio Scanner v6.1.2 + Rdio Scanner v6.1.3 ---------------------------------- - 2022/01/05 08:48:03 server started - 2022/01/05 08:48:03 main interface at http://macos.local - 2022/01/05 08:48:03 admin interface at http://macos.local/admin + 2022/01/11 08:48:03 server started + 2022/01/11 08:48:03 main interface at http://macos.local + 2022/01/11 08:48:03 admin interface at http://macos.local/admin ## Listening with SSL with self-signed certificates @@ -90,8 +90,8 @@ It is advised to share your [Rdio Scanner](https://github.com/chuot/rdio-scanner We can create such self-signed certificates by running [Rdio Scanner](https://github.com/chuot/rdio-scanner) with the **-ssl_create** argument. rdio@macos rdio-scanner % ./rdio-scanner -ssl_create - 2022/01/05 08:49:16 generating ssl certificate files - 2022/01/05 08:49:17 ssl files created + 2022/01/11 08:49:16 generating ssl certificate files + 2022/01/11 08:49:17 ssl files created This has generated these files in the same folder as your [Rdio Scanner](https://github.com/chuot/rdio-scanner) executable. @@ -108,12 +108,12 @@ Now we use the self-signed server certificate to run our instance. > -ssl_key_file server.key \ > -ssl_listen :443 - Rdio Scanner v6.1.2 + Rdio Scanner v6.1.3 ---------------------------------- - 2022/01/05 08:50:58 server started - 2022/01/05 08:50:58 main interface at http://macos.local - 2022/01/05 08:50:58 main interface at https://macos.local - 2022/01/05 08:50:58 admin interface at https://macos.local/admin + 2022/01/11 08:50:58 server started + 2022/01/11 08:50:58 main interface at http://macos.local + 2022/01/11 08:50:58 main interface at https://macos.local + 2022/01/11 08:50:58 admin interface at https://macos.local/admin ## Save your advanced configuration to a config file @@ -125,7 +125,7 @@ You don't want to have to type everytime a long list of arguments. No problem, y > -ssl_key_file server.key \ > -ssl_listen :443 \ > -config_save - 2022/01/05 08:52:24 rdio-scanner.ini file created + 2022/01/11 08:52:24 rdio-scanner.ini file created All of your parameters passed as arguments to [Rdio Scanner](https://github.com/chuot/rdio-scanner) have been saved to an INI file which has the same arguments/values list. @@ -140,12 +140,12 @@ Then simply run [Rdio Scanner](https://github.com/chuot/rdio-scanner) without an rdio@macos rdio-scanner % ./rdio-scanner - Rdio Scanner v6.1.2 + Rdio Scanner v6.1.3 ---------------------------------- - 2022/01/05 08:54:08 server started - 2022/01/05 08:54:08 main interface at http://macos.local - 2022/01/05 08:54:08 main interface at https://macos.local - 2022/01/05 08:54:08 admin interface at https://macos.local/admin + 2022/01/11 08:54:08 server started + 2022/01/11 08:54:08 main interface at http://macos.local + 2022/01/11 08:54:08 main interface at https://macos.local + 2022/01/11 08:54:08 admin interface at https://macos.local/admin ## Install Rdio Scanner as a service diff --git a/docs/platforms/freebsd.md b/docs/platforms/freebsd.md index 0314c4e..03e00b4 100644 --- a/docs/platforms/freebsd.md +++ b/docs/platforms/freebsd.md @@ -42,8 +42,8 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd rdio@pc-freebsd:~ $ mkdir rdio-scanner rdio@pc-freebsd:~ $ cd rdio-scanner rdio@pc-freebsd:~/rdio-scanner $ unzip \ - > ~/rdio-scanner-freebsd-amd64-v6.1.2.zip - Archive: ../rdio-scanner-freebsd-amd64-v6.1.2.zip + > ~/rdio-scanner-freebsd-amd64-v6.1.3.zip + Archive: ../rdio-scanner-freebsd-amd64-v6.1.3.zip extracting: rdio-scanner extracting: rdio-scanner.pdf @@ -51,11 +51,11 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd rdio@pc-freebsd:~/rdio-scanner $ ./rdio-scanner - Rdio Scanner v6.1.2 + Rdio Scanner v6.1.3 ---------------------------------- - 2022/01/05 08:16:36 server started - 2022/01/05 08:16:36 main interface at http://pc-freebsd:3000 - 2022/01/05 08:16:36 admin interface at http://pc-freebsd:3000/admin + 2022/01/11 08:16:36 server started + 2022/01/11 08:16:36 main interface at http://pc-freebsd:3000 + 2022/01/11 08:16:36 admin interface at http://pc-freebsd:3000/admin 4. Access the administrative dashboard to finalize the configuration. @@ -81,11 +81,11 @@ Here we want our [Rdio Scanner](https://guthub.com/chuot/rdio-scanner) instance Password: root@pc-freebsd:/home/rdio/rdio-scanner # ./rdio-scanner -listen :80 - Rdio Scanner v6.1.2 + Rdio Scanner v6.1.3 ---------------------------------- - 2022/01/05 08:19:38 server started - 2022/01/05 08:19:38 main interface at http://pc-freebsd - 2022/01/05 08:19:38 admin interface at http://pc-freebsd/admin + 2022/01/11 08:19:38 server started + 2022/01/11 08:19:38 main interface at http://pc-freebsd + 2022/01/11 08:19:38 admin interface at http://pc-freebsd/admin ## Listening with SSL with self-signed certificates @@ -94,8 +94,8 @@ It is advised to share your [Rdio Scanner](https://github.com/chuot/rdio-scanner We can create such self-signed certificates by running [Rdio Scanner](https://github.com/chuot/rdio-scanner) with the **-ssl_create** argument. rdio@pc-freebsd:~/rdio-scanner $ ./rdio-scanner -ssl_create - 2022/01/05 10:32:17 generating ssl certificate files - 2022/01/05 10:32:19 ssl files created + 2022/01/11 10:32:17 generating ssl certificate files + 2022/01/11 10:32:19 ssl files created This has generated these files in the same folder as your [Rdio Scanner](https://github.com/chuot/rdio-scanner) executable. @@ -114,12 +114,12 @@ Now we use the self-signed server certificate to run our instance. > -ssl_key_file server.key \ > -ssl_listen :443 - Rdio Scanner v6.1.2 + Rdio Scanner v6.1.3 ---------------------------------- - 2022/01/05 10:34:29 server started - 2022/01/05 10:34:29 main interface at http://pc-freebsd - 2022/01/05 10:34:29 main interface at https://pc-freebsd - 2022/01/05 10:34:29 admin interface at https://pc-freebsd/admin + 2022/01/11 10:34:29 server started + 2022/01/11 10:34:29 main interface at http://pc-freebsd + 2022/01/11 10:34:29 main interface at https://pc-freebsd + 2022/01/11 10:34:29 admin interface at https://pc-freebsd/admin ## Save your advanced configuration to a config file @@ -131,7 +131,7 @@ You don't want to have to type everytime a long list of arguments. No problem, y > -ssl_key_file server.key \ > -ssl_listen :443 \ > -config_save - 2022/01/05 10:37:00 rdio-scanner.ini file created + 2022/01/11 10:37:00 rdio-scanner.ini file created All of your parameters passed as arguments to [Rdio Scanner](https://github.com/chuot/rdio-scanner) have been saved to an INI file which has the same arguments/values list. @@ -148,12 +148,12 @@ Then simply run [Rdio Scanner](https://github.com/chuot/rdio-scanner) without an Password: root@pc-freebsd:/home/rdio/rdio-scanner # ./rdio-scanner - Rdio Scanner v6.1.2 + Rdio Scanner v6.1.3 ---------------------------------- - 2022/01/05 10:38:28 server started - 2022/01/05 10:38:29 main interface at http://pc-freebsd - 2022/01/05 10:38:29 main interface at https://pc-freebsd - 2022/01/05 10:38:29 admin interface at https://pc-freebsd/admin + 2022/01/11 10:38:28 server started + 2022/01/11 10:38:29 main interface at http://pc-freebsd + 2022/01/11 10:38:29 main interface at https://pc-freebsd + 2022/01/11 10:38:29 admin interface at https://pc-freebsd/admin ## Install Rdio Scanner as a service diff --git a/docs/platforms/linux.md b/docs/platforms/linux.md index 752d4b7..14cad63 100644 --- a/docs/platforms/linux.md +++ b/docs/platforms/linux.md @@ -42,8 +42,8 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd [rdio@pc-linux ~]$ mkdir rdio-scanner [rdio@pc-linux ~]$ cd rdio-scanner [rdio@pc-linux rdio-scanner]$ unzip \ - > ~/Downloads/rdio-scanner-linux-amd64-v6.1.2.zip - Archive: /home/rdio/Downloads/rdio-scanner-linux-amd64-v6.1.2.zip + > ~/Downloads/rdio-scanner-linux-amd64-v6.1.3.zip + Archive: /home/rdio/Downloads/rdio-scanner-linux-amd64-v6.1.3.zip inflating: rdio-scanner inflating: rdio-scanner.pdf @@ -51,11 +51,11 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd [rdio@pc-linux rdio-scanner]$ ./rdio-scanner - Rdio Scanner v6.1.2 + Rdio Scanner v6.1.3 ---------------------------------- - 2022/01/05 09:11:48 server started - 2022/01/05 09:11:48 main interface at http://pc-linux:3000 - 2022/01/05 09:11:48 admin interface at http://pc-linux:3000/admin + 2022/01/11 09:11:48 server started + 2022/01/11 09:11:48 main interface at http://pc-linux:3000 + 2022/01/11 09:11:48 admin interface at http://pc-linux:3000/admin 4. Access the administrative dashboard to finalize the configuration. @@ -78,11 +78,11 @@ Here we want our [Rdio Scanner](https://guthub.com/chuot/rdio-scanner) instance [rdio@pc-linux rdio-scanner]$ sudo ./rdio-scanner -listen :80 [sudo] password for rdio: - Rdio Scanner v6.1.2 + Rdio Scanner v6.1.3 ---------------------------------- - 2022/01/05 09:14:00 server started - 2022/01/05 09:14:00 main interface at http://pc-linux - 2022/01/05 09:14:00 admin interface at http://pc-linux/admin + 2022/01/11 09:14:00 server started + 2022/01/11 09:14:00 main interface at http://pc-linux + 2022/01/11 09:14:00 admin interface at http://pc-linux/admin ## Listening with SSL with self-signed certificates @@ -91,8 +91,8 @@ It is advised to share your [Rdio Scanner](https://github.com/chuot/rdio-scanner We can create such self-signed certificates by running [Rdio Scanner](https://github.com/chuot/rdio-scanner) with the **-ssl_create** argument. [rdio@pc-linux rdio-scanner]$ ./rdio-scanner -ssl_create - 2022/01/05 09:15:03 generating ssl certificate files - 2022/01/05 09:15:05 ssl files created + 2022/01/11 09:15:03 generating ssl certificate files + 2022/01/11 09:15:05 ssl files created This has generated these files in the same folder as your [Rdio Scanner](https://github.com/chuot/rdio-scanner) executable. @@ -110,12 +110,12 @@ Now we use the self-signed server certificate to run our instance. > -ssl_listen :443 [sudo] password for rdio: - Rdio Scanner v6.1.2 + Rdio Scanner v6.1.3 ---------------------------------- - 2022/01/05 09:16:47 server started - 2022/01/05 09:16:47 main interface at http://pc-linux - 2022/01/05 09:16:47 main interface at https://pc-linux - 2022/01/05 09:16:47 admin interface at https://pc-linux/admin + 2022/01/11 09:16:47 server started + 2022/01/11 09:16:47 main interface at http://pc-linux + 2022/01/11 09:16:47 main interface at https://pc-linux + 2022/01/11 09:16:47 admin interface at https://pc-linux/admin ## Save your advanced configuration to a config file @@ -127,7 +127,7 @@ You don't want to have to type everytime a long list of arguments. No problem, y > -ssl_key_file server.key \ > -ssl_listen :443 \ > -config_save - 2022/01/05 09:19:29 rdio-scanner.ini file created + 2022/01/11 09:19:29 rdio-scanner.ini file created All of your parameters passed as arguments to [Rdio Scanner](https://github.com/chuot/rdio-scanner) have been saved to an INI file which has the same arguments/values list. @@ -143,12 +143,12 @@ Then simply run [Rdio Scanner](https://github.com/chuot/rdio-scanner) without an [rdio@pc-linux rdio-scanner]$ sudo ./rdio-scanner [sudo] Mot de passe de rdio : - Rdio Scanner v6.1.2 + Rdio Scanner v6.1.3 ---------------------------------- - 2022/01/05 09:20:40 server started - 2022/01/05 09:20:40 main interface at http://pc-linux - 2022/01/05 09:20:40 main interface at https://pc-linux - 2022/01/05 09:20:40 admin interface at https://pc-linux/admin + 2022/01/11 09:20:40 server started + 2022/01/11 09:20:40 main interface at http://pc-linux + 2022/01/11 09:20:40 main interface at https://pc-linux + 2022/01/11 09:20:40 admin interface at https://pc-linux/admin ## Install Rdio Scanner as a service diff --git a/docs/platforms/windows.md b/docs/platforms/windows.md index 9a552c9..01ef9d7 100644 --- a/docs/platforms/windows.md +++ b/docs/platforms/windows.md @@ -44,7 +44,7 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd C:\Users\rdio> cd rdio-scanner C:\Users\rdio\rdio-scanner> tar -xvf ^ - ..\downloads\rdio-scanner-windows-amd64-v6.1.2.zip + ..\downloads\rdio-scanner-windows-amd64-v6.1.3.zip x rdio-scanner.exe x rdio-scanner.pdf @@ -52,11 +52,11 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd C:\Users\rdio\rdio-scanner>rdio-scanner - Rdio Scanner v6.1.2 + Rdio Scanner v6.1.3 ---------------------------------- - 2022/01/05 13:48:48 server started - 2022/01/05 13:48:48 main interface at http://pc-windows:3000 - 2022/01/05 13:48:48 admin interface at http://pc-windows:3000/admin + 2022/01/11 13:48:48 server started + 2022/01/11 13:48:48 main interface at http://pc-windows:3000 + 2022/01/11 13:48:48 admin interface at http://pc-windows:3000/admin 4. Access the administrative dashboard to finalize the configuration. @@ -78,11 +78,11 @@ Here we want our [Rdio Scanner](https://guthub.com/chuot/rdio-scanner) instance C:\Users\rdio\rdio-scanner>rdio-scanner -listen :80 - Rdio Scanner v6.1.2 + Rdio Scanner v6.1.3 ---------------------------------- - 2022/01/05 10:53:31 server started - 2022/01/05 10:53:31 main interface at http://pc-windows - 2022/01/05 10:53:31 admin interface at http://pc-windows/admin + 2022/01/11 10:53:31 server started + 2022/01/11 10:53:31 main interface at http://pc-windows + 2022/01/11 10:53:31 admin interface at http://pc-windows/admin ## Listening with SSL with self-signed certificates @@ -91,8 +91,8 @@ It is advised to share your [Rdio Scanner](https://github.com/chuot/rdio-scanner We can create such self-signed certificates by running [Rdio Scanner](https://github.com/chuot/rdio-scanner) with the **-ssl_create** argument. C:\Users\rdio\rdio-scanner>rdio-scanner -ssl_create - 2022/01/05 10:58:51 generating ssl certificate files - 2022/01/05 10:58:54 ssl files created + 2022/01/11 10:58:51 generating ssl certificate files + 2022/01/11 10:58:54 ssl files created This has generated these files in the same folder as your [Rdio Scanner](https://github.com/chuot/rdio-scanner) executable. @@ -109,12 +109,12 @@ Now we use the self-signed server certificate to run our instance. -ssl_key_file server.key ^ -ssl_listen :443 - Rdio Scanner v6.1.2 + Rdio Scanner v6.1.3 ---------------------------------- - 2022/01/05 11:05:43 server started - 2022/01/05 11:05:43 main interface at http://pc-windows - 2022/01/05 11:05:43 main interface at https://pc-windows - 2022/01/05 11:05:43 admin interface at https://pc-windows/admin + 2022/01/11 11:05:43 server started + 2022/01/11 11:05:43 main interface at http://pc-windows + 2022/01/11 11:05:43 main interface at https://pc-windows + 2022/01/11 11:05:43 admin interface at https://pc-windows/admin ## Save your advanced configuration to a config file @@ -126,7 +126,7 @@ You don't want to have to type everytime a long list of arguments. No problem, y -ssl_key_file server.key ^ -ssl_listen :443 ^ -config_save - 2022/01/05 11:08:28 rdio-scanner.ini file created + 2022/01/11 11:08:28 rdio-scanner.ini file created All of your parameters passed as arguments to [Rdio Scanner](https://github.com/chuot/rdio-scanner) have been saved to an INI file which has the same arguments/values list. @@ -141,12 +141,12 @@ Then simply run [Rdio Scanner](https://github.com/chuot/rdio-scanner) without an C:\Users\rdio\rdio-scanner>rdio-scanner - Rdio Scanner v6.1.2 + Rdio Scanner v6.1.3 ---------------------------------- - 2022/01/05 11:11:28 server started - 2022/01/05 11:11:28 main interface at http://pc-windows - 2022/01/05 11:11:28 main interface at https://pc-windows - 2022/01/05 11:11:28 admin interface at https://pc-windows/admin + 2022/01/11 11:11:28 server started + 2022/01/11 11:11:28 main interface at http://pc-windows + 2022/01/11 11:11:28 main interface at https://pc-windows + 2022/01/11 11:11:28 admin interface at https://pc-windows/admin \pagebreak{} diff --git a/server/access.go b/server/access.go index 2515833..f98673a 100644 --- a/server/access.go +++ b/server/access.go @@ -20,6 +20,7 @@ import ( "encoding/json" "fmt" "strings" + "sync" "time" ) @@ -124,7 +125,10 @@ func (access *Access) HasExpired() bool { return false } -type Accesses []Access +type Accesses struct { + List []*Access + mutex sync.Mutex +} func (accesses *Accesses) FromMap(f []interface{}) { *accesses = Accesses{} @@ -132,24 +136,24 @@ func (accesses *Accesses) FromMap(f []interface{}) { for _, r := range f { switch m := r.(type) { case map[string]interface{}: - access := Access{} + access := &Access{} access.FromMap(m) - *accesses = append(*accesses, access) + accesses.List = append(accesses.List, access) } } } func (accesses *Accesses) GetAccess(code string) (access *Access, ok bool) { - for _, access := range *accesses { + for _, access := range accesses.List { if access.Code == code { - return &access, true + return access, true } } return nil, false } func (accesses *Accesses) IsRestricted() bool { - return len(*accesses) > 0 + return len(accesses.List) > 0 } func (accesses *Accesses) Read(db *Database) error { @@ -164,7 +168,7 @@ func (accesses *Accesses) Read(db *Database) error { t time.Time ) - *accesses = Accesses{} + accesses.List = []*Access{} formatError := func(err error) error { return fmt.Errorf("accesses.read: %v", err) @@ -175,7 +179,8 @@ func (accesses *Accesses) Read(db *Database) error { } for rows.Next() { - access := Access{} + access := &Access{} + if err = rows.Scan(&id, &access.Code, &expiration, &access.Ident, &limit, &order, &systems); err != nil { break } @@ -206,7 +211,7 @@ func (accesses *Accesses) Read(db *Database) error { access.Systems = []interface{}{} } - *accesses = append(*accesses, access) + accesses.List = append(accesses.List, access) } rows.Close() @@ -227,11 +232,13 @@ func (accesses *Accesses) Write(db *Database) error { systems interface{} ) + accesses.mutex.Lock() + formatError := func(err error) error { return fmt.Errorf("accesses.write: %v", err) } - for _, access := range *accesses { + for _, access := range accesses.List { switch access.Systems { case "*": systems = `"*"` @@ -265,7 +272,7 @@ func (accesses *Accesses) Write(db *Database) error { var id uint rows.Scan(&id) remove := true - for _, access := range *accesses { + for _, access := range accesses.List { if access.Id == nil || access.Id == id { remove = false break @@ -294,5 +301,7 @@ func (accesses *Accesses) Write(db *Database) error { } } + accesses.mutex.Unlock() + return nil } diff --git a/server/admin.go b/server/admin.go index f04e6c2..07f246f 100644 --- a/server/admin.go +++ b/server/admin.go @@ -81,30 +81,36 @@ func (admin *Admin) BroadcastConfig() { } func (admin *Admin) ChangePassword(currentPassword string, newPassword string) error { - if len(currentPassword) > 0 { - if err := bcrypt.CompareHashAndPassword([]byte(admin.Controller.Options.adminPassword), []byte(currentPassword)); err == nil { - var hash []byte - if hash, err = bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost); err != nil { - return err - } + var ( + err error + hash []byte + ) - admin.Controller.Options.adminPassword = string(hash) - admin.Controller.Options.adminPasswordNeedChange = newPassword == defaults.adminPassword - - if err := admin.Controller.Options.Write(admin.Controller.Database); err != nil { - return err - } - - if err := admin.Controller.Options.Read(admin.Controller.Database); err != nil { - return err - } - - LogEvent(admin.Controller.Database, LogLevelInfo, "admin password changed.") - - } else { - return err - } + if len(newPassword) == 0 { + return errors.New("newPassword is empty") } + + if err = bcrypt.CompareHashAndPassword([]byte(admin.Controller.Options.adminPassword), []byte(currentPassword)); err != nil { + return err + } + + if hash, err = bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost); err != nil { + return err + } + + admin.Controller.Options.adminPassword = string(hash) + admin.Controller.Options.adminPasswordNeedChange = newPassword == defaults.adminPassword + + if err := admin.Controller.Options.Write(admin.Controller.Database); err != nil { + return err + } + + if err := admin.Controller.Options.Read(admin.Controller.Database); err != nil { + return err + } + + LogEvent(admin.Controller.Database, LogLevelInfo, "admin password changed.") + return nil } @@ -287,7 +293,7 @@ func (admin *Admin) GetAuthorization(r *http.Request) string { func (admin *Admin) GetConfig() map[string]interface{} { systems := []map[string]interface{}{} - for _, system := range *admin.Controller.Systems { + for _, system := range admin.Controller.Systems.List { systems = append(systems, map[string]interface{}{ "_id": system.RowId, "autoPopulate": system.AutoPopulate, @@ -302,14 +308,14 @@ func (admin *Admin) GetConfig() map[string]interface{} { } return map[string]interface{}{ - "access": *admin.Controller.Accesses, - "apiKeys": *admin.Controller.Apikeys, - "dirWatch": *admin.Controller.Dirwatches, - "downstreams": *admin.Controller.Downstreams, - "groups": *admin.Controller.Groups, - "options": *admin.Controller.Options, + "access": admin.Controller.Accesses.List, + "apiKeys": admin.Controller.Apikeys.List, + "dirWatch": admin.Controller.Dirwatches.List, + "downstreams": admin.Controller.Downstreams.List, + "groups": admin.Controller.Groups.List, + "options": admin.Controller.Options, "systems": systems, - "tags": *admin.Controller.Tags, + "tags": admin.Controller.Tags.List, } } diff --git a/server/api.go b/server/api.go index 98a7aed..bef829d 100644 --- a/server/api.go +++ b/server/api.go @@ -16,16 +16,12 @@ package main import ( - "encoding/json" "fmt" "io" "mime" "mime/multipart" "net/http" - "regexp" - "strconv" "strings" - "time" ) const ( @@ -45,23 +41,8 @@ func (api *Api) CallUploadHandler(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodPost: var ( - audio []byte - audioName string - audioType string - dateTime time.Time - frequencies = []map[string]interface{}{} - frequency interface{} - key string - patches = []uint{} - source interface{} - sources = []map[string]interface{}{} - system uint - systemLabel interface{} - talkgroup uint - talkgroupGroup interface{} - talkgroupLabel interface{} - talkgroupTag interface{} - units interface{} + call = NewCall() + key string ) mediaType, params, err := mime.ParseMediaType(r.Header.Get("Content-Type")) @@ -93,182 +74,15 @@ func (api *Api) CallUploadHandler(w http.ResponseWriter, r *http.Request) { } switch p.FormName() { - case "audio": - audio = b - audioName = p.FileName() - audioType = p.Header.Get("Content-Type") - - case "audioName": - audioName = string(b) - - case "audioType": - audioType = string(b) - - case "dateTime": - if regexp.MustCompile(`^[0-9]+$`).Match(b) { - if i, err := strconv.Atoi(string(b)); err == nil { - dateTime = time.Unix(int64(i), 0).UTC() - } - } else { - dateTime, _ = time.Parse(time.RFC3339, string(b)) - dateTime = dateTime.UTC() - } - - case "frequencies": - var f interface{} - if err := json.Unmarshal(b, &f); err == nil { - switch v := f.(type) { - case []interface{}: - for _, f := range v { - freq := map[string]interface{}{} - switch v := f.(type) { - case map[string]interface{}: - switch v := v["errorCount"].(type) { - case float64: - freq["errorCount"] = uint(v) - } - switch v := v["freq"].(type) { - case float64: - freq["freq"] = uint(v) - } - switch v := v["len"].(type) { - case float64: - freq["len"] = uint(v) - } - switch v := v["pos"].(type) { - case float64: - freq["pos"] = uint(v) - } - switch v := v["spikeCount"].(type) { - case float64: - freq["spikeCount"] = uint(v) - } - } - - frequencies = append(frequencies, freq) - } - } - } - - case "frequency": - if i, err := strconv.Atoi(string(b)); err == nil { - frequency = uint(i) - } - case "key": key = string(b) - - case "patches", "patched_talkgroups": - var f interface{} - if err := json.Unmarshal(b, &f); err == nil { - switch v := f.(type) { - case []interface{}: - for _, patch := range v { - switch v := patch.(type) { - case float64: - if v > 0 { - patches = append(patches, uint(v)) - } - } - } - } - } - - case "source": - if i, err := strconv.Atoi(string(b)); err == nil { - source = int(i) - } - - case "sources": - var f interface{} - if err := json.Unmarshal(b, &f); err == nil { - switch v := f.(type) { - case []interface{}: - for _, f := range v { - src := map[string]interface{}{} - switch v := f.(type) { - case map[string]interface{}: - switch v := v["pos"].(type) { - case float64: - src["pos"] = uint(v) - } - switch s := v["src"].(type) { - case float64: - if s > 0 { - src["src"] = uint(s) - switch t := v["tag"].(type) { - case string: - var u Units - switch v := units.(type) { - case Units: - u = v - default: - u = Units{} - } - u.Add(int(s), t) - units = u - } - } - } - } - - sources = append(sources, src) - } - } - } - - case "system", "systemId": - if i, err := strconv.Atoi(string(b)); err == nil { - system = uint(i) - } - - case "systemLabel": - systemLabel = string(b) - - case "talkgroup", "talkgroupId": - if i, err := strconv.Atoi(string(b)); err == nil { - talkgroup = uint(i) - } - - case "talkgroupGroup": - if s := string(b); len(s) > 1 { - talkgroupGroup = s - } - - case "talkgroupLabel": - if s := string(b); len(s) > 1 { - talkgroupLabel = s - } - - case "talkgroupTag": - if s := string(b); len(s) > 1 { - talkgroupTag = s - } + default: + ParseMultipartContent(call, p, b) } } - call := &Call{ - Audio: audio, - AudioName: audioName, - AudioType: audioType, - DateTime: dateTime, - Frequencies: frequencies, - Frequency: frequency, - Patches: patches, - Source: source, - Sources: sources, - System: system, - Talkgroup: talkgroup, - systemLabel: systemLabel, - talkgroupGroup: talkgroupGroup, - talkgroupLabel: talkgroupLabel, - talkgroupTag: talkgroupTag, - units: units, - } - if call.IsValid() { api.HandleCall(key, call, w) - } else { w.WriteHeader(http.StatusExpectationFailed) w.Write([]byte("Incomplete call data")) @@ -306,12 +120,8 @@ func (api *Api) TrunkRecorderCallUploadHandler(w http.ResponseWriter, r *http.Re switch r.Method { case http.MethodPost: var ( - audio []byte - audioName string - audioType string - key string - meta []byte - system uint + call = NewCall() + key string ) mediaType, params, err := mime.ParseMediaType(r.Header.Get("Content-Type")) @@ -343,35 +153,33 @@ func (api *Api) TrunkRecorderCallUploadHandler(w http.ResponseWriter, r *http.Re } switch p.FormName() { - case "audio": - audio = b - audioName = p.FileName() - audioType = p.Header.Get("Content-Type") - case "key": key = string(b) - case "meta": - meta = b - - case "system": - if i, err := strconv.Atoi(string(b)); err == nil { - system = uint(i) + if err := ParseTrunkRecorderMeta(call, b); err != nil { + w.WriteHeader(http.StatusExpectationFailed) + w.Write([]byte("Invalid call data")) + return } } } - call := &Call{ - Audio: audio, - AudioName: audioName, - AudioType: audioType, - System: system, - } + mr = multipart.NewReader(r.Body, params["boundary"]) - if err := ParseTrunkRecorderMeta(call, meta); err != nil { - w.WriteHeader(http.StatusExpectationFailed) - w.Write([]byte("Invalid call data")) - return + for { + p, err := mr.NextPart() + if err == io.EOF { + break + } else if err != nil { + continue + } + + b, err := io.ReadAll(p) + if err != nil { + continue + } + + ParseMultipartContent(call, p, b) } if call.IsValid() { diff --git a/server/apikey.go b/server/apikey.go index 9558aef..faf5030 100644 --- a/server/apikey.go +++ b/server/apikey.go @@ -20,6 +20,7 @@ import ( "encoding/json" "fmt" "strings" + "sync" "github.com/google/uuid" ) @@ -107,25 +108,28 @@ func (apikey *Apikey) HasAccess(call *Call) bool { return false } -type Apikeys []Apikey +type Apikeys struct { + List []*Apikey + mutex sync.Mutex +} func (apikeys *Apikeys) FromMap(f []interface{}) { - *apikeys = Apikeys{} + apikeys.List = []*Apikey{} for _, r := range f { switch m := r.(type) { case map[string]interface{}: - apikey := Apikey{} + apikey := &Apikey{} apikey.FromMap(m) - *apikeys = append(*apikeys, apikey) + apikeys.List = append(apikeys.List, apikey) } } } func (apikeys *Apikeys) GetApikey(key string) (apikey *Apikey, ok bool) { - for _, apikey := range *apikeys { + for _, apikey := range apikeys.List { if apikey.Key == key { - return &apikey, true + return apikey, true } } return nil, false @@ -140,7 +144,7 @@ func (apikeys *Apikeys) Read(db *Database) error { systems string ) - *apikeys = Apikeys{} + apikeys.List = []*Apikey{} formatError := func(err error) error { return fmt.Errorf("apikeys.read: %v", err) @@ -151,7 +155,8 @@ func (apikeys *Apikeys) Read(db *Database) error { } for rows.Next() { - apikey := Apikey{} + apikey := &Apikey{} + if err = rows.Scan(&id, &apikey.Disabled, &apikey.Ident, &apikey.Key, &order, &systems); err != nil { break } @@ -176,7 +181,7 @@ func (apikeys *Apikeys) Read(db *Database) error { apikey.Systems = []interface{}{} } - *apikeys = append(*apikeys, apikey) + apikeys.List = append(apikeys.List, apikey) } rows.Close() @@ -197,11 +202,13 @@ func (apikeys *Apikeys) Write(db *Database) error { systems interface{} ) + apikeys.mutex.Lock() + formatError := func(err error) error { return fmt.Errorf("apikeys.write %v", err) } - for _, apikey := range *apikeys { + for _, apikey := range apikeys.List { switch apikey.Systems { case "*": systems = `"*"` @@ -235,7 +242,7 @@ func (apikeys *Apikeys) Write(db *Database) error { var id uint rows.Scan(&id) remove := true - for _, apikey := range *apikeys { + for _, apikey := range apikeys.List { if apikey.Id == nil || apikey.Id == id { remove = false break @@ -264,5 +271,7 @@ func (apikeys *Apikeys) Write(db *Database) error { } } + apikeys.mutex.Unlock() + return nil } diff --git a/server/call.go b/server/call.go index 23dcbba..90cf227 100644 --- a/server/call.go +++ b/server/call.go @@ -43,6 +43,14 @@ type Call struct { units interface{} } +func NewCall() *Call { + return &Call{ + Frequencies: []map[string]interface{}{}, + Patches: []uint{}, + Sources: []map[string]interface{}{}, + } +} + func (call *Call) IsValid() bool { if len(call.Audio) <= 44 { return false diff --git a/server/controller.go b/server/controller.go index e74027d..2cba28f 100644 --- a/server/controller.go +++ b/server/controller.go @@ -219,7 +219,7 @@ func (controller *Controller) IngestCall(call *Call) { system.Label = fmt.Sprintf("System %v", call.System) } - *controller.Systems = append(*controller.Systems, system) + controller.Systems.List = append(controller.Systems.List, system) } if system != nil && talkgroup == nil && (controller.Options.AutoPopulate || system.AutoPopulate) { @@ -242,7 +242,7 @@ func (controller *Controller) IngestCall(call *Call) { if group, ok = controller.Groups.GetGroup(groupLabel); !ok { group = &Group{Label: groupLabel} - *controller.Groups = append(*controller.Groups, *group) + controller.Groups.List = append(controller.Groups.List, group) if err = controller.Groups.Write(controller.Database); err != nil { logError(err) @@ -271,7 +271,7 @@ func (controller *Controller) IngestCall(call *Call) { if tag, ok = controller.Tags.GetTag(tagLabel); !ok { tag = &Tag{Label: tagLabel} - *controller.Tags = append(*controller.Tags, *tag) + controller.Tags.List = append(controller.Tags.List, tag) if err = controller.Tags.Write(controller.Database); err != nil { logError(err) @@ -508,8 +508,8 @@ func (controller *Controller) ProcessMessageCommandPin(client *Client, message * switch v := client.Access.Limit.(type) { case uint: count := uint(0) - for _, acc := range *controller.Accesses { - if acc == *client.Access { + for _, acc := range controller.Accesses.List { + if acc == client.Access { count++ } } diff --git a/server/database.go b/server/database.go index 2a4648b..5a2b69f 100644 --- a/server/database.go +++ b/server/database.go @@ -206,7 +206,7 @@ func (db *Database) migration20191028144433(verbose bool) error { } } else { queries = []string{ - "create table `rdioScannerSystems` (`id` integer primary key auto_increment, `createdAt` datetime not null, `updatedAt` datetime not null, `name` varchar(255) not null, `system` integer not null, `talkgroups` json not null default '[]')", + "create table `rdioScannerSystems` (`id` integer primary key auto_increment, `createdAt` datetime not null, `updatedAt` datetime not null, `name` varchar(255) not null, `system` integer not null, `talkgroups` json not null)", "create unique index `rdio_scanner_systems_system` on `rdioScannerSystems` (`system`)", } } @@ -261,7 +261,7 @@ func (db *Database) migration20191220093214(verbose bool) error { queries = []string{ "alter table `rdioScannerCalls` add column `audioName` varchar(255)", "alter table `rdioScannerCalls` add column `audioType` varchar(255)", - "alter table `rdioScannerSystems` add column `aliases` json not null default '[]'", + "alter table `rdioScannerSystems` add column `aliases` json not null", } } return db.migrateWithSchema("20191220093214-new-v3-tables", queries, verbose) @@ -297,7 +297,7 @@ func (db *Database) migration20200428132918(verbose bool) error { } else { queries = []string{ "drop table `rdioScannerSystems`", - "create table `rdioScannerCalls2` (`id` integer primary key auto_increment, `audio` longblob not null, `audioName` varchar(255), `audioType` varchar(255), `dateTime` datetime not null, `frequencies` json not null default '[]', `frequency` integer, `source` integer, `sources` json not null default '[]', `system` integer not null, `talkgroup` integer not null)", + "create table `rdioScannerCalls2` (`id` integer primary key auto_increment, `audio` longblob not null, `audioName` varchar(255), `audioType` varchar(255), `dateTime` datetime not null, `frequencies` json not null, `frequency` integer, `source` integer, `sources` json not null, `system` integer not null, `talkgroup` integer not null)", "insert into `rdioScannerCalls2` select `id`, `audio`, `audioName`, `audioType`, `startTime`, `freqList`, `freq`, null, `srcList`, `system`, `talkgroup` from `rdioScannerCalls`", "drop table `rdioScannerCalls`", "alter table `rdioScannerCalls2` rename to `rdioScannerCalls`", diff --git a/server/dirwatch.go b/server/dirwatch.go index a739d8c..7694ae1 100644 --- a/server/dirwatch.go +++ b/server/dirwatch.go @@ -29,6 +29,7 @@ import ( "sort" "strconv" "strings" + "sync" "time" "github.com/fsnotify/fsnotify" @@ -161,16 +162,16 @@ func (dirwatch *Dirwatch) ingestDefault(p string) error { } if strings.EqualFold(path.Ext(p), ext) { - call := Call{ - AudioName: path.Base(p), - AudioType: mime.TypeByExtension(path.Ext(p)), - } + call := NewCall() + + call.AudioName = path.Base(p) + call.AudioType = mime.TypeByExtension(path.Ext(p)) if call.Audio, err = os.ReadFile(p); err != nil { return err } - dirwatch.parseMask(&call) + dirwatch.parseMask(call) switch v := dirwatch.SystemId.(type) { case uint: @@ -183,7 +184,7 @@ func (dirwatch *Dirwatch) ingestDefault(p string) error { } if call.IsValid() { - dirwatch.controller.Ingest <- &call + dirwatch.controller.Ingest <- call if dirwatch.DeleteAfter { if err = os.Remove(p); err != nil { @@ -217,23 +218,23 @@ func (dirwatch *Dirwatch) ingestSdrTrunk(p string) error { return nil } - call := Call{ - AudioName: path.Base(p), - AudioType: mime.TypeByExtension(path.Ext(p)), - } + call := NewCall() + + call.AudioName = path.Base(p) + call.AudioType = mime.TypeByExtension(path.Ext(p)) if call.Audio, err = os.ReadFile(p); err != nil { fmt.Println("err1") return err } - if err = ParseSdrTrunkMeta(&call, dirwatch.controller); err != nil { + if err = ParseSdrTrunkMeta(call, dirwatch.controller); err != nil { fmt.Println("err2") return err } if call.IsValid() { - dirwatch.controller.Ingest <- &call + dirwatch.controller.Ingest <- call if dirwatch.DeleteAfter { if err = os.Remove(p); err != nil { @@ -272,10 +273,10 @@ func (dirwatch *Dirwatch) ingestTrunkRecorder(p string) error { audioName := base + ext - call := Call{ - AudioName: path.Base(audioName), - AudioType: mime.TypeByExtension(path.Ext(audioName)), - } + call := NewCall() + + call.AudioName = path.Base(audioName) + call.AudioType = mime.TypeByExtension(path.Ext(audioName)) switch v := dirwatch.SystemId.(type) { case uint: @@ -290,12 +291,12 @@ func (dirwatch *Dirwatch) ingestTrunkRecorder(p string) error { return err } - if err = ParseTrunkRecorderMeta(&call, b); err != nil { + if err = ParseTrunkRecorderMeta(call, b); err != nil { return err } if call.IsValid() { - dirwatch.controller.Ingest <- &call + dirwatch.controller.Ingest <- call } if dirwatch.DeleteAfter { @@ -428,7 +429,10 @@ func (dirwatch *Dirwatch) parseMask(call *Call) { switch v := metaval["unit"].(type) { case string: if i, err := strconv.Atoi(v); err == nil { - call.Sources = map[string]interface{}{"pos": 0, "src": uint(i)} + switch sources := call.Sources.(type) { + case []map[string]interface{}: + call.Sources = append(sources, map[string]interface{}{"pos": 0, "src": uint(i)}) + } } } } @@ -530,19 +534,22 @@ func (dirwatch *Dirwatch) Stop() { } } -type Dirwatches []Dirwatch +type Dirwatches struct { + List []*Dirwatch + mutex sync.Mutex +} func (dirwatches *Dirwatches) FromMap(f []interface{}) { dirwatches.Stop() - *dirwatches = Dirwatches{} + dirwatches.List = []*Dirwatch{} for _, f := range f { switch v := f.(type) { case map[string]interface{}: - dirwatch := Dirwatch{} + dirwatch := &Dirwatch{} dirwatch.FromMap(v) - *dirwatches = append(*dirwatches, dirwatch) + dirwatches.List = append(dirwatches.List, dirwatch) } } } @@ -575,7 +582,7 @@ func (dirwatches *Dirwatches) Read(db *Database) error { } for rows.Next() { - dirwatch := Dirwatch{} + dirwatch := &Dirwatch{} if err = rows.Scan(&id, &delay, &dirwatch.DeleteAfter, &dirwatch.Directory, &dirwatch.Disabled, &extension, &frequency, &mask, &order, &systemId, &talkgroupId, &kind, &dirwatch.UsePolling); err != nil { break @@ -617,7 +624,7 @@ func (dirwatches *Dirwatches) Read(db *Database) error { dirwatch.Kind = kind.String } - *dirwatches = append(*dirwatches, dirwatch) + dirwatches.List = append(dirwatches.List, dirwatch) } rows.Close() @@ -630,8 +637,8 @@ func (dirwatches *Dirwatches) Read(db *Database) error { } func (dirwatches *Dirwatches) Start(controller *Controller) { - for i := range *dirwatches { - if err := (*dirwatches)[i].Start(controller); err != nil { + for i := range dirwatches.List { + if err := dirwatches.List[i].Start(controller); err != nil { LogEvent( controller.Database, LogLevelError, @@ -642,8 +649,8 @@ func (dirwatches *Dirwatches) Start(controller *Controller) { } func (dirwatches *Dirwatches) Stop() { - for i := range *dirwatches { - (*dirwatches)[i].Stop() + for i := range dirwatches.List { + dirwatches.List[i].Stop() } } @@ -655,11 +662,13 @@ func (dirwatches *Dirwatches) Write(db *Database) error { rowIds = []uint{} ) + dirwatches.mutex.Lock() + formatError := func(err error) error { return fmt.Errorf("dirwatches.write: %v", err) } - for _, dirwatch := range *dirwatches { + for _, dirwatch := range dirwatches.List { if err = db.Sql.QueryRow("select count(*) from `rdioScannerDirWatches` where `_id` = ?", dirwatch.Id).Scan(&count); err != nil { break } @@ -686,7 +695,7 @@ func (dirwatches *Dirwatches) Write(db *Database) error { var rowId uint rows.Scan(&rowId) remove := true - for _, dirwatch := range *dirwatches { + for _, dirwatch := range dirwatches.List { if dirwatch.Id == nil || dirwatch.Id == rowId { remove = false break @@ -715,6 +724,8 @@ func (dirwatches *Dirwatches) Write(db *Database) error { } } + dirwatches.mutex.Unlock() + return nil } diff --git a/server/downstream.go b/server/downstream.go index 1ab4982..0b1ab55 100644 --- a/server/downstream.go +++ b/server/downstream.go @@ -25,6 +25,7 @@ import ( "net/url" "path" "strings" + "sync" "time" "github.com/google/uuid" @@ -321,7 +322,10 @@ func (downstream *Downstream) Send(call *Call) error { return nil } -type Downstreams []Downstream +type Downstreams struct { + List []*Downstream + mutex sync.Mutex +} func (downstreams *Downstreams) FromMap(f []interface{}) { *downstreams = Downstreams{} @@ -329,9 +333,9 @@ func (downstreams *Downstreams) FromMap(f []interface{}) { for _, r := range f { switch m := r.(type) { case map[string]interface{}: - downstream := Downstream{} + downstream := &Downstream{} downstream.FromMap(m) - *downstreams = append(*downstreams, downstream) + downstreams.List = append(downstreams.List, downstream) } } } @@ -344,7 +348,7 @@ func (downstreams *Downstreams) Read(db *Database) error { rows *sql.Rows ) - *downstreams = Downstreams{} + downstreams.List = []*Downstream{} formatError := func(err error) error { return fmt.Errorf("downstreams.read: %v", err) @@ -355,7 +359,7 @@ func (downstreams *Downstreams) Read(db *Database) error { } for rows.Next() { - downstream := Downstream{} + downstream := &Downstream{} if err = rows.Scan(&id, &downstream.Apikey, &downstream.Disabled, &order, &downstream.Systems, &downstream.Url); err != nil { break @@ -382,7 +386,7 @@ func (downstreams *Downstreams) Read(db *Database) error { continue } - *downstreams = append(*downstreams, downstream) + downstreams.List = append(downstreams.List, downstream) } rows.Close() @@ -395,7 +399,7 @@ func (downstreams *Downstreams) Read(db *Database) error { } func (downstreams *Downstreams) Send(controller *Controller, call *Call) { - for _, downstream := range *downstreams { + for _, downstream := range downstreams.List { logEvent := func(logLevel string, message string) { LogEvent( controller.Database, @@ -423,11 +427,13 @@ func (downstreams *Downstreams) Write(db *Database) error { systems interface{} ) + downstreams.mutex.Lock() + formatError := func(err error) error { return fmt.Errorf("downstreams.write: %v", err) } - for _, downstream := range *downstreams { + for _, downstream := range downstreams.List { switch downstream.Systems { case "*": systems = `"*"` @@ -461,7 +467,7 @@ func (downstreams *Downstreams) Write(db *Database) error { var rowId uint rows.Scan(&rowId) remove := true - for _, downstream := range *downstreams { + for _, downstream := range downstreams.List { if downstream.Id == nil || downstream.Id == rowId { remove = false break @@ -490,5 +496,7 @@ func (downstreams *Downstreams) Write(db *Database) error { } } + downstreams.mutex.Unlock() + return nil } diff --git a/server/go.mod b/server/go.mod index f2576f0..e3f7f7b 100644 --- a/server/go.mod +++ b/server/go.mod @@ -9,10 +9,10 @@ require ( github.com/golang-jwt/jwt/v4 v4.2.0 github.com/google/uuid v1.3.0 github.com/gorilla/websocket v1.4.2 - github.com/kardianos/service v1.2.0 + github.com/kardianos/service v1.2.1 golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 gopkg.in/ini.v1 v1.66.2 - modernc.org/sqlite v1.14.3 + modernc.org/sqlite v1.14.4 ) require ( @@ -21,8 +21,8 @@ require ( github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect github.com/stretchr/testify v1.7.0 // indirect golang.org/x/mod v0.5.1 // indirect - golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98 // indirect - golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect + golang.org/x/net v0.0.0-20220111093109-d55c255bac03 // indirect + golang.org/x/sys v0.0.0-20220111092808-5a964db01320 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/tools v0.1.8 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect diff --git a/server/go.sum b/server/go.sum index b4e1791..37929a1 100644 --- a/server/go.sum +++ b/server/go.sum @@ -16,15 +16,15 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/kardianos/service v1.2.0 h1:bGuZ/epo3vrt8IPC7mnKQolqFeYJb7Cs8Rk4PSOBB/g= -github.com/kardianos/service v1.2.0/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM= +github.com/kardianos/service v1.2.1 h1:AYndMsehS+ywIS6RB9KOlcXzteWUzxgMgBymJD7+BYk= +github.com/kardianos/service v1.2.1/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA= -github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.10 h1:MLn+5bFRlWMGoSRmJour3CL1w/qL96mvipqpwQW/Sfk= +github.com/mattn/go-sqlite3 v1.14.10/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= 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/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= @@ -47,8 +47,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98 h1:+6WJMRLHlD7X7frgp7TUZ36RnQzSf9wVVTNakEp+nqY= -golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220111093109-d55c255bac03 h1:0FB83qp0AzVJm+0wcIlauAjJ+tNdh7jLuacRYCIVv7s= +golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -65,8 +65,8 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220111092808-5a964db01320 h1:0jf+tOCoZ3LyutmCOWpVni1chK4VfFLhRsDK7MhqGRY= +golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 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= @@ -142,10 +142,8 @@ modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3 modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY= modernc.org/ccgo/v3 v3.12.84/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w= modernc.org/ccgo/v3 v3.12.86/go.mod h1:dN7S26DLTgVSni1PVA3KxxHTcykyDurf3OgUzNqTSrU= -modernc.org/ccgo/v3 v3.12.88/go.mod h1:0MFzUHIuSIthpVZyMWiFYMwjiFnhrN5MkvBrUwON+ZM= modernc.org/ccgo/v3 v3.12.90/go.mod h1:obhSc3CdivCRpYZmrvO88TXlW0NvoSVvdh/ccRjJYko= modernc.org/ccgo/v3 v3.12.92/go.mod h1:5yDdN7ti9KWPi5bRVWPl8UNhpEAtCjuEE7ayQnzzqHA= -modernc.org/ccgo/v3 v3.12.95/go.mod h1:ZcLyvtocXYi8uF+9Ebm3G8EF8HNY5hGomBqthDp4eC8= modernc.org/ccgo/v3 v3.13.1/go.mod h1:aBYVOUfIlcSnrsRVU8VRS35y2DIfpgkmVkYZ0tpIXi4= modernc.org/ccgo/v3 v3.14.0 h1:Zr1Ny9+7r5yAiXpBdgp8XiXqkNA4ARrRphHGHVXeAp0= modernc.org/ccgo/v3 v3.14.0/go.mod h1:hBrkiBlUwvr5vV/ZH9YzXIp982jKE8Ek8tR1ytoAL6Q= @@ -188,11 +186,8 @@ modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI= modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE= modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY= modernc.org/libc v1.11.88/go.mod h1:h3oIVe8dxmTcchcFuCcJ4nAWaoiwzKCdv82MM0oiIdQ= -modernc.org/libc v1.11.90/go.mod h1:ynK5sbjsU77AP+nn61+k+wxUGRx9rOFcIqWYYMaDZ4c= modernc.org/libc v1.11.98/go.mod h1:ynK5sbjsU77AP+nn61+k+wxUGRx9rOFcIqWYYMaDZ4c= -modernc.org/libc v1.11.99/go.mod h1:wLLYgEiY2D17NbBOEp+mIJJJBGSiy7fLL4ZrGGZ+8jI= modernc.org/libc v1.11.101/go.mod h1:wLLYgEiY2D17NbBOEp+mIJJJBGSiy7fLL4ZrGGZ+8jI= -modernc.org/libc v1.11.104/go.mod h1:2MH3DaF/gCU8i/UBiVE1VFRos4o523M7zipmwH8SIgQ= modernc.org/libc v1.12.0/go.mod h1:2MH3DaF/gCU8i/UBiVE1VFRos4o523M7zipmwH8SIgQ= modernc.org/libc v1.13.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk= modernc.org/libc v1.13.2 h1:GCFjY9bmwDZ/TJC4OZOUWaNgxIxwb104C/QZrqpcVEA= @@ -207,13 +202,13 @@ modernc.org/memory v1.0.5 h1:XRch8trV7GgvTec2i7jc33YlUI0RKVDBvZ5eZ5m8y14= modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM= modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A= modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.14.3 h1:psrTwgpEujgWEP3FNdsC9yNh5tSeA77U0GeWhHH4XmQ= -modernc.org/sqlite v1.14.3/go.mod h1:xMpicS1i2MJ4C8+Ap0vYBqTwYfpFvdnPE6brbFOtV2Y= +modernc.org/sqlite v1.14.4 h1:F3DRiVZKnCLqIQ0LhEGqBLnw9LcdADciCwCIHQ8bD5g= +modernc.org/sqlite v1.14.4/go.mod h1:LWtcO8JtBrt29KKmTqNNXDjAn36vHa/3nHvOYoVIAjc= modernc.org/strutil v1.1.1 h1:xv+J1BXY3Opl2ALrBwyfEikFAj8pmqcpnfmuwUwcozs= modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= -modernc.org/tcl v1.9.2 h1:YA87dFLOsR2KqMka371a2Xgr+YsyUwo7OmHVSv/kztw= -modernc.org/tcl v1.9.2/go.mod h1:aw7OnlIoiuJgu1gwbTZtrKnGpDqH9wyH++jZcxdqNsg= +modernc.org/tcl v1.10.0 h1:vux2MNFhSXYqD8Kq4Uc9RjWcgv2c7Atx3da3VpLPPEw= +modernc.org/tcl v1.10.0/go.mod h1:WzWapmP/7dHVhFoyPpEaNSVTL8xtewhouN/cqSJ5A2s= modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk= modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.2.20 h1:DyboxM1sJR2NB803j2StnbnL6jcQXz273OhHDGu8dGk= -modernc.org/z v1.2.20/go.mod h1:zU9FiF4PbHdOTUxw+IF8j7ArBMRPsHgq10uVPt6xTzo= +modernc.org/z v1.2.21 h1:UO0ptLQLHM2eNistCE6ofm1fMKKXk0n0IkrQly3z5HA= +modernc.org/z v1.2.21/go.mod h1:uXrObx4pGqXWIMliC5MiKuwAyMrltzwpteOFUP1PWCc= diff --git a/server/group.go b/server/group.go index 8d63caf..2052bda 100644 --- a/server/group.go +++ b/server/group.go @@ -20,6 +20,7 @@ import ( "encoding/json" "fmt" "strings" + "sync" ) type Group struct { @@ -39,17 +40,20 @@ func (group *Group) FromMap(m map[string]interface{}) { } } -type Groups []Group +type Groups struct { + List []*Group + mutex sync.Mutex +} func (groups *Groups) FromMap(f []interface{}) { - *groups = Groups{} + groups.List = []*Group{} for _, r := range f { switch m := r.(type) { case map[string]interface{}: - group := Group{} + group := &Group{} group.FromMap(m) - *groups = append(*groups, group) + groups.List = append(groups.List, group) } } } @@ -57,15 +61,15 @@ func (groups *Groups) FromMap(f []interface{}) { func (groups *Groups) GetGroup(f interface{}) (group *Group, ok bool) { switch v := f.(type) { case uint: - for _, group := range *groups { + for _, group := range groups.List { if group.Id == v { - return &group, true + return group, true } } case string: - for _, group := range *groups { + for _, group := range groups.List { if group.Label == v { - return &group, true + return group, true } } } @@ -147,7 +151,7 @@ func (groups *Groups) Read(db *Database) error { rows *sql.Rows ) - *groups = Groups{} + groups.List = []*Group{} formatError := func(err error) error { return fmt.Errorf("groups.read: %v", err) @@ -158,7 +162,7 @@ func (groups *Groups) Read(db *Database) error { } for rows.Next() { - group := Group{} + group := &Group{} if err = rows.Scan(&id, &group.Label); err != nil { break @@ -172,7 +176,7 @@ func (groups *Groups) Read(db *Database) error { continue } - *groups = append(*groups, group) + groups.List = append(groups.List, group) } rows.Close() @@ -192,11 +196,13 @@ func (groups *Groups) Write(db *Database) error { rowIds = []uint{} ) + groups.mutex.Lock() + formatError := func(err error) error { return fmt.Errorf("groups.write %v", err) } - for _, group := range *groups { + for _, group := range groups.List { if err = db.Sql.QueryRow("select count(*) from `rdioScannerGroups` where `_id` = ?", group.Id).Scan(&count); err != nil { break } @@ -223,7 +229,7 @@ func (groups *Groups) Write(db *Database) error { var rowId uint rows.Scan(&rowId) remove := true - for _, group := range *groups { + for _, group := range groups.List { if group.Id == nil || group.Id == rowId { remove = false break @@ -251,6 +257,9 @@ func (groups *Groups) Write(db *Database) error { } } } + + groups.mutex.Unlock() + return nil } diff --git a/server/livefeed.go b/server/livefeed.go index 67e0e46..23f57ee 100644 --- a/server/livefeed.go +++ b/server/livefeed.go @@ -68,7 +68,9 @@ sys: } func (livefeedMap *LivefeedMap) IsEnabled(call *Call) bool { - if (*livefeedMap)[call.System][call.Talkgroup] { + if livefeedMap == nil { + return false + } else if (*livefeedMap)[call.System][call.Talkgroup] { return true } else { switch v := call.Patches.(type) { diff --git a/server/options.go b/server/options.go index 757cbfa..b47afed 100644 --- a/server/options.go +++ b/server/options.go @@ -19,6 +19,7 @@ import ( "database/sql" "encoding/json" "fmt" + "sync" "golang.org/x/crypto/bcrypt" ) @@ -36,6 +37,7 @@ type Options struct { TagsToggle bool `json:"tagsToggle"` adminPassword string adminPasswordNeedChange bool + mutex sync.Mutex secret string } @@ -136,6 +138,11 @@ func (options *Options) Read(db *Database) error { err = db.Sql.QueryRow("select `val` from `rdioScannerConfigs` where `key` = 'adminPassword'").Scan(&f) if err == nil { switch v := f.(type) { + case []uint8: + var f string + if err = json.Unmarshal(v, &f); err == nil { + options.adminPassword = f + } case string: var f string if err = json.Unmarshal([]byte(v), &f); err == nil { @@ -147,6 +154,11 @@ func (options *Options) Read(db *Database) error { err = db.Sql.QueryRow("select `val` from `rdioScannerConfigs` where `key` = 'adminPasswordNeedChange'").Scan(&f) if err == nil { switch v := f.(type) { + case []uint8: + var f bool + if err = json.Unmarshal(v, &f); err == nil { + options.adminPasswordNeedChange = f + } case string: var f bool if err = json.Unmarshal([]byte(v), &f); err == nil { @@ -238,6 +250,8 @@ func (options *Options) Write(db *Database) error { res sql.Result ) + options.mutex.Lock() + formatError := func(err error) error { return fmt.Errorf("options.write: %v", err) } @@ -289,5 +303,7 @@ func (options *Options) Write(db *Database) error { db.Sql.Exec("insert into `rdioScannerConfigs` (`key`, `val`) values (?, ?)", "options", string(b)) } + options.mutex.Unlock() + return nil } diff --git a/server/parsers.go b/server/parsers.go index 951ea57..2dec522 100644 --- a/server/parsers.go +++ b/server/parsers.go @@ -18,6 +18,9 @@ package main import ( "bytes" "encoding/json" + "mime" + "mime/multipart" + "path" "regexp" "strconv" "strings" @@ -111,6 +114,166 @@ func ParseSdrTrunkMeta(call *Call, controller *Controller) error { return nil } +func ParseMultipartContent(call *Call, p *multipart.Part, b []byte) { + switch p.FormName() { + case "audio": + call.Audio = b + call.AudioName = p.FileName() + + case "audioName": + call.AudioName = string(b) + call.AudioType = mime.TypeByExtension(path.Ext(string(b))) + + case "dateTime": + if regexp.MustCompile(`^[0-9]+$`).Match(b) { + if i, err := strconv.Atoi(string(b)); err == nil { + call.DateTime = time.Unix(int64(i), 0).UTC() + } + } else { + call.DateTime, _ = time.Parse(time.RFC3339, string(b)) + call.DateTime = call.DateTime.UTC() + } + + case "frequencies": + var f interface{} + if err := json.Unmarshal(b, &f); err == nil { + switch v := f.(type) { + case []interface{}: + var frequencies = []map[string]interface{}{} + for _, f := range v { + freq := map[string]interface{}{} + switch v := f.(type) { + case map[string]interface{}: + switch v := v["errorCount"].(type) { + case float64: + freq["errorCount"] = uint(v) + } + switch v := v["freq"].(type) { + case float64: + freq["freq"] = uint(v) + } + switch v := v["len"].(type) { + case float64: + freq["len"] = uint(v) + } + switch v := v["pos"].(type) { + case float64: + freq["pos"] = uint(v) + } + switch v := v["spikeCount"].(type) { + case float64: + freq["spikeCount"] = uint(v) + } + } + frequencies = append(frequencies, freq) + } + call.Frequencies = frequencies + } + } + + case "frequency": + if i, err := strconv.Atoi(string(b)); err == nil { + call.Frequency = uint(i) + } + + case "patches", "patched_talkgroups": + var ( + f interface{} + patches = []uint{} + ) + if err := json.Unmarshal(b, &f); err == nil { + switch v := f.(type) { + case []interface{}: + for _, patch := range v { + switch v := patch.(type) { + case float64: + if v > 0 { + patches = append(patches, uint(v)) + } + } + } + } + call.Patches = patches + } + + case "source": + if i, err := strconv.Atoi(string(b)); err == nil { + call.Source = int(i) + } + + case "sources": + var ( + f interface{} + units interface{} + ) + if err := json.Unmarshal(b, &f); err == nil { + switch v := f.(type) { + case []interface{}: + var sources = []map[string]interface{}{} + for _, f := range v { + src := map[string]interface{}{} + switch v := f.(type) { + case map[string]interface{}: + switch v := v["pos"].(type) { + case float64: + src["pos"] = uint(v) + } + switch s := v["src"].(type) { + case float64: + if s > 0 { + src["src"] = uint(s) + switch t := v["tag"].(type) { + case string: + var u Units + switch v := units.(type) { + case Units: + u = v + default: + u = Units{} + } + u.Add(int(s), t) + units = u + } + } + } + } + sources = append(sources, src) + } + call.Sources = sources + call.units = units + } + } + + case "system", "systemId": + if i, err := strconv.Atoi(string(b)); err == nil { + call.System = uint(i) + } + + case "systemLabel": + call.systemLabel = string(b) + + case "talkgroup", "talkgroupId": + if i, err := strconv.Atoi(string(b)); err == nil { + call.Talkgroup = uint(i) + } + + case "talkgroupGroup": + if s := string(b); len(s) > 1 { + call.talkgroupGroup = s + } + + case "talkgroupLabel": + if s := string(b); len(s) > 1 { + call.talkgroupLabel = s + } + + case "talkgroupTag": + if s := string(b); len(s) > 1 { + call.talkgroupTag = s + } + } +} + func ParseTrunkRecorderMeta(call *Call, b []byte) error { m := map[string]interface{}{} diff --git a/server/system.go b/server/system.go index 9c34cde..b668ee6 100644 --- a/server/system.go +++ b/server/system.go @@ -22,6 +22,7 @@ import ( "sort" "strconv" "strings" + "sync" ) type System struct { @@ -85,17 +86,20 @@ func (system *System) FromMap(m map[string]interface{}) { type SystemMap map[string]interface{} -type Systems []*System +type Systems struct { + List []*System + mutex sync.Mutex +} func (systems *Systems) FromMap(f []interface{}) { - *systems = Systems{} + systems.List = []*System{} for _, r := range f { switch m := r.(type) { case map[string]interface{}: system := &System{} system.FromMap(m) - *systems = append(*systems, system) + systems.List = append(systems.List, system) } } } @@ -103,7 +107,7 @@ func (systems *Systems) FromMap(f []interface{}) { func (systems *Systems) GetNewSystemId() uint { NextId: for i := uint(1); i < 65535; i++ { - for _, s := range *systems { + for _, s := range systems.List { if s.Id == i { continue NextId } @@ -116,13 +120,13 @@ NextId: func (systems *Systems) GetSystem(f interface{}) (system *System, ok bool) { switch v := f.(type) { case uint: - for _, system := range *systems { + for _, system := range systems.List { if system.Id == v { return system, true } } case string: - for _, system := range *systems { + for _, system := range systems.List { if system.Label == v { return system, true } @@ -133,21 +137,21 @@ func (systems *Systems) GetSystem(f interface{}) (system *System, ok bool) { func (systems *Systems) GetScopedSystems(client *Client, groups *Groups, tags *Tags, sortTalkgroups bool) *SystemsMap { var ( - rawSystems = Systems{} + rawSystems = []*System{} systemsMap = SystemsMap{} ) if client.Access == nil { - rawSystems = append(rawSystems, *systems...) + rawSystems = append(rawSystems, systems.List...) } else { switch v := client.Access.Systems.(type) { case nil: - rawSystems = append(rawSystems, *systems...) + rawSystems = append(rawSystems, systems.List...) case string: if v == "*" { - rawSystems = append(rawSystems, *systems...) + rawSystems = append(rawSystems, systems.List...) } case []interface{}: @@ -338,7 +342,7 @@ func (systems *Systems) Read(db *Database) error { return err } - *systems = append(*systems, system) + systems.List = append(systems.List, system) } rows.Close() @@ -347,8 +351,8 @@ func (systems *Systems) Read(db *Database) error { return formatError(err) } - sort.Slice(*systems, func(i int, j int) bool { - return (*systems)[i].Order < (*systems)[j].Order + sort.Slice(systems.List, func(i int, j int) bool { + return systems.List[i].Order < systems.List[j].Order }) return nil @@ -364,11 +368,13 @@ func (systems *Systems) Write(db *Database) error { systemIds = []uint{} ) + systems.mutex.Lock() + formatError := func(err error) error { return fmt.Errorf("systems.write: %v", err) } - for _, system := range *systems { + for _, system := range systems.List { if len(system.Blacklists) > 0 { blacklists = strings.Join([]string{"[", system.Blacklists.String(), "]"}, "") } else { @@ -410,7 +416,7 @@ func (systems *Systems) Write(db *Database) error { var systemId uint rows.Scan(&rowId, &systemId) remove := true - for _, system := range *systems { + for _, system := range systems.List { if system.RowId == nil || system.RowId == rowId { remove = false break @@ -456,6 +462,8 @@ func (systems *Systems) Write(db *Database) error { } } + systems.mutex.Unlock() + return nil } diff --git a/server/tags.go b/server/tags.go index 0fda854..7e0ec73 100644 --- a/server/tags.go +++ b/server/tags.go @@ -20,6 +20,7 @@ import ( "encoding/json" "fmt" "strings" + "sync" ) type Tag struct { @@ -39,17 +40,20 @@ func (tag *Tag) FromMap(m map[string]interface{}) { } } -type Tags []Tag +type Tags struct { + List []*Tag + mutex sync.Mutex +} func (tags *Tags) FromMap(f []interface{}) { - *tags = Tags{} + tags.List = []*Tag{} for _, r := range f { switch m := r.(type) { case map[string]interface{}: - tag := Tag{} + tag := &Tag{} tag.FromMap(m) - *tags = append(*tags, tag) + tags.List = append(tags.List, tag) } } } @@ -57,15 +61,15 @@ func (tags *Tags) FromMap(f []interface{}) { func (tags *Tags) GetTag(f interface{}) (tag *Tag, ok bool) { switch v := f.(type) { case uint: - for _, tag := range *tags { + for _, tag := range tags.List { if tag.Id == v { - return &tag, true + return tag, true } } case string: - for _, tag := range *tags { + for _, tag := range tags.List { if tag.Label == v { - return &tag, true + return tag, true } } } @@ -147,7 +151,7 @@ func (tags *Tags) Read(db *Database) error { rows *sql.Rows ) - *tags = Tags{} + tags.List = []*Tag{} formatError := func(err error) error { return fmt.Errorf("tags read: %v", err) @@ -158,7 +162,7 @@ func (tags *Tags) Read(db *Database) error { } for rows.Next() { - tag := Tag{} + tag := &Tag{} if err = rows.Scan(&id, &tag.Label); err != nil { break @@ -168,7 +172,7 @@ func (tags *Tags) Read(db *Database) error { tag.Id = uint(id.Float64) } - *tags = append(*tags, tag) + tags.List = append(tags.List, tag) } rows.Close() @@ -188,11 +192,13 @@ func (tags *Tags) Write(db *Database) error { rowIds = []uint{} ) + tags.mutex.Lock() + formatError := func(err error) error { return fmt.Errorf("tags write %v", err) } - for _, tag := range *tags { + for _, tag := range tags.List { if err = db.Sql.QueryRow("select count(*) from `rdioScannerTags` where `_id` = ?", tag.Id).Scan(&count); err != nil { break } @@ -218,7 +224,7 @@ func (tags *Tags) Write(db *Database) error { var rowId uint rows.Scan(&rowId) remove := true - for _, tag := range *tags { + for _, tag := range tags.List { if tag.Id == nil || tag.Id == rowId { remove = false break @@ -247,6 +253,8 @@ func (tags *Tags) Write(db *Database) error { } } + tags.mutex.Unlock() + return nil } diff --git a/server/version.go b/server/version.go index 46dceb5..c5d0add 100644 --- a/server/version.go +++ b/server/version.go @@ -15,4 +15,4 @@ package main -const Version = "6.1.2" +const Version = "6.1.3"