mirror of
https://github.com/unraid/api.git
synced 2026-05-04 06:02:47 -05:00
feat: almost working
This commit is contained in:
Generated
+659
-17
@@ -63,7 +63,7 @@
|
||||
"ini": "^4.1.2",
|
||||
"ip": "^2.0.1",
|
||||
"jose": "^5.3.0",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash-es": "^4.17.21",
|
||||
"multi-ini": "^2.2.0",
|
||||
"mustache": "^4.2.0",
|
||||
"nanobus": "^4.5.0",
|
||||
@@ -73,7 +73,6 @@
|
||||
"node-cache": "^5.1.2",
|
||||
"node-window-polyfill": "^1.0.2",
|
||||
"openid-client": "^5.6.5",
|
||||
"p-iteration": "^1.1.8",
|
||||
"p-retry": "^4.6.2",
|
||||
"passport-custom": "^1.1.1",
|
||||
"passport-http-header-strategy": "^1.1.0",
|
||||
@@ -87,6 +86,7 @@
|
||||
"stoppable": "^1.1.0",
|
||||
"systeminformation": "^5.22.9",
|
||||
"ts-command-line-args": "^2.5.1",
|
||||
"tsx": "^4.19.1",
|
||||
"uuid": "^10.0.0",
|
||||
"ws": "^8.17.0",
|
||||
"wtfnode": "^0.9.2",
|
||||
@@ -120,6 +120,7 @@
|
||||
"@types/graphql-fields": "^1.3.9",
|
||||
"@types/graphql-type-uuid": "^0.2.6",
|
||||
"@types/ini": "^4.1.0",
|
||||
"@types/ip": "^1.1.3",
|
||||
"@types/lodash": "^4.17.1",
|
||||
"@types/mustache": "^4.2.5",
|
||||
"@types/node": "^20.12.12",
|
||||
@@ -2023,6 +2024,21 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz",
|
||||
"integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.19.11",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.11.tgz",
|
||||
@@ -5325,6 +5341,15 @@
|
||||
"integrity": "sha512-mTehMtc+xtnWBBvqizcqYCktKDBH2WChvx1GU3Sfe4PysFDXiNe+1YwtpVX1MDtCa4NQrSPw2+3HmvXHY3gt1w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/ip": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/ip/-/ip-1.1.3.tgz",
|
||||
"integrity": "sha512-64waoJgkXFTYnCYDUWgSATJ/dXEBanVkaP5d4Sbk7P6U7cTTMhxVyROTckc6JKdwCrgnAjZMn0k3177aQxtDEA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/js-yaml": {
|
||||
"version": "4.0.5",
|
||||
"dev": true,
|
||||
@@ -10883,9 +10908,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/get-tsconfig": {
|
||||
"version": "4.5.0",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"version": "4.8.1",
|
||||
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz",
|
||||
"integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==",
|
||||
"dependencies": {
|
||||
"resolve-pkg-maps": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
|
||||
}
|
||||
@@ -12719,6 +12747,11 @@
|
||||
"version": "4.17.21",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash-es": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
|
||||
},
|
||||
"node_modules/lodash.camelcase": {
|
||||
"version": "4.3.0",
|
||||
"license": "MIT"
|
||||
@@ -14241,13 +14274,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/p-iteration": {
|
||||
"version": "1.1.8",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/p-limit": {
|
||||
"version": "3.1.0",
|
||||
"dev": true,
|
||||
@@ -15853,6 +15879,14 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve-pkg-maps": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
|
||||
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
|
||||
"funding": {
|
||||
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/response-iterator": {
|
||||
"version": "0.2.6",
|
||||
"license": "MIT",
|
||||
@@ -17926,6 +17960,407 @@
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/tsx": {
|
||||
"version": "4.19.1",
|
||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.1.tgz",
|
||||
"integrity": "sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==",
|
||||
"dependencies": {
|
||||
"esbuild": "~0.23.0",
|
||||
"get-tsconfig": "^4.7.5"
|
||||
},
|
||||
"bin": {
|
||||
"tsx": "dist/cli.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz",
|
||||
"integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/android-arm": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz",
|
||||
"integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz",
|
||||
"integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/android-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz",
|
||||
"integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz",
|
||||
"integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz",
|
||||
"integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz",
|
||||
"integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz",
|
||||
"integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz",
|
||||
"integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz",
|
||||
"integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz",
|
||||
"integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz",
|
||||
"integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz",
|
||||
"integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz",
|
||||
"integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz",
|
||||
"integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/esbuild": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz",
|
||||
"integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==",
|
||||
"hasInstallScript": true,
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.23.1",
|
||||
"@esbuild/android-arm": "0.23.1",
|
||||
"@esbuild/android-arm64": "0.23.1",
|
||||
"@esbuild/android-x64": "0.23.1",
|
||||
"@esbuild/darwin-arm64": "0.23.1",
|
||||
"@esbuild/darwin-x64": "0.23.1",
|
||||
"@esbuild/freebsd-arm64": "0.23.1",
|
||||
"@esbuild/freebsd-x64": "0.23.1",
|
||||
"@esbuild/linux-arm": "0.23.1",
|
||||
"@esbuild/linux-arm64": "0.23.1",
|
||||
"@esbuild/linux-ia32": "0.23.1",
|
||||
"@esbuild/linux-loong64": "0.23.1",
|
||||
"@esbuild/linux-mips64el": "0.23.1",
|
||||
"@esbuild/linux-ppc64": "0.23.1",
|
||||
"@esbuild/linux-riscv64": "0.23.1",
|
||||
"@esbuild/linux-s390x": "0.23.1",
|
||||
"@esbuild/linux-x64": "0.23.1",
|
||||
"@esbuild/netbsd-x64": "0.23.1",
|
||||
"@esbuild/openbsd-arm64": "0.23.1",
|
||||
"@esbuild/openbsd-x64": "0.23.1",
|
||||
"@esbuild/sunos-x64": "0.23.1",
|
||||
"@esbuild/win32-arm64": "0.23.1",
|
||||
"@esbuild/win32-ia32": "0.23.1",
|
||||
"@esbuild/win32-x64": "0.23.1"
|
||||
}
|
||||
},
|
||||
"node_modules/tunnel-agent": {
|
||||
"version": "0.6.0",
|
||||
"license": "Apache-2.0",
|
||||
@@ -20736,6 +21171,12 @@
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/openbsd-arm64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz",
|
||||
"integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/openbsd-x64": {
|
||||
"version": "0.19.11",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.11.tgz",
|
||||
@@ -23051,6 +23492,15 @@
|
||||
"integrity": "sha512-mTehMtc+xtnWBBvqizcqYCktKDBH2WChvx1GU3Sfe4PysFDXiNe+1YwtpVX1MDtCa4NQrSPw2+3HmvXHY3gt1w==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/ip": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/ip/-/ip-1.1.3.tgz",
|
||||
"integrity": "sha512-64waoJgkXFTYnCYDUWgSATJ/dXEBanVkaP5d4Sbk7P6U7cTTMhxVyROTckc6JKdwCrgnAjZMn0k3177aQxtDEA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/js-yaml": {
|
||||
"version": "4.0.5",
|
||||
"dev": true
|
||||
@@ -26842,8 +27292,12 @@
|
||||
}
|
||||
},
|
||||
"get-tsconfig": {
|
||||
"version": "4.5.0",
|
||||
"dev": true
|
||||
"version": "4.8.1",
|
||||
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz",
|
||||
"integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==",
|
||||
"requires": {
|
||||
"resolve-pkg-maps": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"getpass": {
|
||||
"version": "0.1.7",
|
||||
@@ -28047,6 +28501,11 @@
|
||||
"lodash": {
|
||||
"version": "4.17.21"
|
||||
},
|
||||
"lodash-es": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
|
||||
},
|
||||
"lodash.camelcase": {
|
||||
"version": "4.3.0"
|
||||
},
|
||||
@@ -29060,9 +29519,6 @@
|
||||
"version": "3.0.0",
|
||||
"dev": true
|
||||
},
|
||||
"p-iteration": {
|
||||
"version": "1.1.8"
|
||||
},
|
||||
"p-limit": {
|
||||
"version": "3.1.0",
|
||||
"dev": true,
|
||||
@@ -30113,6 +30569,11 @@
|
||||
"global-dirs": "^0.1.1"
|
||||
}
|
||||
},
|
||||
"resolve-pkg-maps": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
|
||||
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="
|
||||
},
|
||||
"response-iterator": {
|
||||
"version": "0.2.6"
|
||||
},
|
||||
@@ -31505,6 +31966,187 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"tsx": {
|
||||
"version": "4.19.1",
|
||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.1.tgz",
|
||||
"integrity": "sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==",
|
||||
"requires": {
|
||||
"esbuild": "~0.23.0",
|
||||
"fsevents": "~2.3.3",
|
||||
"get-tsconfig": "^4.7.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@esbuild/aix-ppc64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz",
|
||||
"integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/android-arm": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz",
|
||||
"integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/android-arm64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz",
|
||||
"integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/android-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/darwin-arm64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz",
|
||||
"integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/darwin-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/freebsd-arm64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz",
|
||||
"integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/freebsd-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/linux-arm": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz",
|
||||
"integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/linux-arm64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz",
|
||||
"integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/linux-ia32": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz",
|
||||
"integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/linux-loong64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz",
|
||||
"integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/linux-mips64el": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz",
|
||||
"integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/linux-ppc64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz",
|
||||
"integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/linux-riscv64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz",
|
||||
"integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/linux-s390x": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz",
|
||||
"integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/linux-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/netbsd-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/openbsd-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/sunos-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/win32-arm64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz",
|
||||
"integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/win32-ia32": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz",
|
||||
"integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==",
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/win32-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==",
|
||||
"optional": true
|
||||
},
|
||||
"esbuild": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz",
|
||||
"integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==",
|
||||
"requires": {
|
||||
"@esbuild/aix-ppc64": "0.23.1",
|
||||
"@esbuild/android-arm": "0.23.1",
|
||||
"@esbuild/android-arm64": "0.23.1",
|
||||
"@esbuild/android-x64": "0.23.1",
|
||||
"@esbuild/darwin-arm64": "0.23.1",
|
||||
"@esbuild/darwin-x64": "0.23.1",
|
||||
"@esbuild/freebsd-arm64": "0.23.1",
|
||||
"@esbuild/freebsd-x64": "0.23.1",
|
||||
"@esbuild/linux-arm": "0.23.1",
|
||||
"@esbuild/linux-arm64": "0.23.1",
|
||||
"@esbuild/linux-ia32": "0.23.1",
|
||||
"@esbuild/linux-loong64": "0.23.1",
|
||||
"@esbuild/linux-mips64el": "0.23.1",
|
||||
"@esbuild/linux-ppc64": "0.23.1",
|
||||
"@esbuild/linux-riscv64": "0.23.1",
|
||||
"@esbuild/linux-s390x": "0.23.1",
|
||||
"@esbuild/linux-x64": "0.23.1",
|
||||
"@esbuild/netbsd-x64": "0.23.1",
|
||||
"@esbuild/openbsd-arm64": "0.23.1",
|
||||
"@esbuild/openbsd-x64": "0.23.1",
|
||||
"@esbuild/sunos-x64": "0.23.1",
|
||||
"@esbuild/win32-arm64": "0.23.1",
|
||||
"@esbuild/win32-ia32": "0.23.1",
|
||||
"@esbuild/win32-x64": "0.23.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tunnel-agent": {
|
||||
"version": "0.6.0",
|
||||
"requires": {
|
||||
|
||||
+4
-2
@@ -43,6 +43,7 @@
|
||||
"install:unraid": "./scripts/install-in-unraid.sh",
|
||||
"start:plugin": "INTROSPECTION=true LOG_MOTHERSHIP_MESSAGES=true LOG_TYPE=pretty LOG_LEVEL=trace unraid-api start --debug",
|
||||
"start:plugin-verbose": "LOG_CONTEXT=true LOG_MOTHERSHIP_MESSAGES=true LOG_TYPE=pretty LOG_LEVEL=trace unraid-api start --debug",
|
||||
"start:tsx": "LOG_MOTHERSHIP_MESSAGES=true LOG_TYPE=pretty LOG_LEVEL=trace DOTENV_CONFIG_PATH=./.env.staging tsx -r dotenv/config src/cli.ts start --debug",
|
||||
"start:dev": "LOG_MOTHERSHIP_MESSAGES=true LOG_TYPE=pretty NODE_ENV=development LOG_LEVEL=trace NODE_ENV=development tsup --config ./tsup.config.ts --watch --onSuccess 'DOTENV_CONFIG_PATH=./.env.development node -r dotenv/config dist/unraid-api.cjs start --debug'",
|
||||
"restart:dev": "LOG_MOTHERSHIP_MESSAGES=true LOG_TYPE=pretty NODE_ENV=development LOG_LEVEL=trace NODE_ENV=development tsup --config ./tsup.config.ts --watch --onSuccess 'DOTENV_CONFIG_PATH=./.env.development node -r dotenv/config dist/unraid-api.cjs restart --debug'",
|
||||
"stop:dev": "LOG_MOTHERSHIP_MESSAGES=true LOG_TYPE=pretty NODE_ENV=development LOG_LEVEL=trace NODE_ENV=development tsup --config ./tsup.config.ts --onSuccess 'DOTENV_CONFIG_PATH=./.env.development node -r dotenv/config dist/unraid-api.cjs stop --debug'",
|
||||
@@ -114,7 +115,7 @@
|
||||
"ini": "^4.1.2",
|
||||
"ip": "^2.0.1",
|
||||
"jose": "^5.3.0",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash-es": "^4.17.21",
|
||||
"multi-ini": "^2.2.0",
|
||||
"mustache": "^4.2.0",
|
||||
"nanobus": "^4.5.0",
|
||||
@@ -124,7 +125,6 @@
|
||||
"node-cache": "^5.1.2",
|
||||
"node-window-polyfill": "^1.0.2",
|
||||
"openid-client": "^5.6.5",
|
||||
"p-iteration": "^1.1.8",
|
||||
"p-retry": "^4.6.2",
|
||||
"passport-custom": "^1.1.1",
|
||||
"passport-http-header-strategy": "^1.1.0",
|
||||
@@ -138,6 +138,7 @@
|
||||
"stoppable": "^1.1.0",
|
||||
"systeminformation": "^5.22.9",
|
||||
"ts-command-line-args": "^2.5.1",
|
||||
"tsx": "^4.19.1",
|
||||
"uuid": "^10.0.0",
|
||||
"ws": "^8.17.0",
|
||||
"wtfnode": "^0.9.2",
|
||||
@@ -168,6 +169,7 @@
|
||||
"@types/graphql-fields": "^1.3.9",
|
||||
"@types/graphql-type-uuid": "^0.2.6",
|
||||
"@types/ini": "^4.1.0",
|
||||
"@types/ip": "^1.1.3",
|
||||
"@types/lodash": "^4.17.1",
|
||||
"@types/mustache": "^4.2.5",
|
||||
"@types/node": "^20.12.12",
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import { expect, test, vi } from 'vitest';
|
||||
import { stop } from '@app/cli/commands/stop';
|
||||
import { spawn } from 'node:child_process';
|
||||
import { cliLogger } from '@app/core/log';
|
||||
import { sleep } from '@app/core/utils/misc/sleep';
|
||||
|
||||
import { getAllUnraidApiPids } from '@app/cli/get-unraid-api-pid';
|
||||
import path from 'node:path';
|
||||
|
||||
const spawnUnraidApiTestDaemon = (difficulty: 'easy' | 'hard') => {
|
||||
const pathToJsFile = difficulty === 'easy' ? '../../setup/child-process-easy-to-kill.js' : '../../setup/child-process-hard-to-kill.js';
|
||||
// Spawn child
|
||||
console.log('Spawning child process at', path.join(import.meta.dirname, pathToJsFile));
|
||||
const child = spawn('node', [path.join(import.meta.dirname, pathToJsFile)], {
|
||||
// In the parent set the tracking environment variable
|
||||
env: Object.assign(process.env, { _DAEMONIZE_PROCESS: '1' }),
|
||||
|
||||
stdio: 'ignore',
|
||||
detached: true,
|
||||
});
|
||||
|
||||
// Convert process into daemon
|
||||
child.unref();
|
||||
|
||||
cliLogger.debug('Daemonized successfully!');
|
||||
};
|
||||
|
||||
test('It stops successfully (easy)', async () => {
|
||||
spawnUnraidApiTestDaemon('easy');
|
||||
spawnUnraidApiTestDaemon('easy');
|
||||
|
||||
await sleep(100);
|
||||
|
||||
const { cliLogger } = await import('@app/core/log');
|
||||
const loggerSpy = vi.spyOn(cliLogger, 'info');
|
||||
|
||||
const pids = await getAllUnraidApiPids();
|
||||
expect(pids.length).toBe(2);
|
||||
await stop();
|
||||
const pids2 = await getAllUnraidApiPids();
|
||||
expect(pids2.length).toBe(0);
|
||||
expect(loggerSpy).toHaveBeenNthCalledWith(1,
|
||||
'Stopping %s unraid-api process(es)...',
|
||||
2,
|
||||
);
|
||||
});
|
||||
|
||||
test('It stops successfully (easy and hard)', async () => {
|
||||
spawnUnraidApiTestDaemon('easy');
|
||||
spawnUnraidApiTestDaemon('easy');
|
||||
spawnUnraidApiTestDaemon('hard');
|
||||
spawnUnraidApiTestDaemon('hard');
|
||||
|
||||
await sleep(100);
|
||||
|
||||
const { cliLogger } = await import('@app/core/log');
|
||||
const loggerSpy = vi.spyOn(cliLogger, 'info');
|
||||
|
||||
const pids = await getAllUnraidApiPids();
|
||||
expect(pids.length).toBe(4);
|
||||
await stop();
|
||||
const pids2 = await getAllUnraidApiPids();
|
||||
expect(pids2.length).toBe(0);
|
||||
expect(loggerSpy).toHaveBeenNthCalledWith(1,
|
||||
'Stopping %s unraid-api process(es)...',
|
||||
4,
|
||||
);
|
||||
expect(loggerSpy).toHaveBeenNthCalledWith(2,
|
||||
'Stopping %s unraid-api process(es)...',
|
||||
2,
|
||||
);
|
||||
expect(loggerSpy).toHaveBeenNthCalledWith(3,
|
||||
'Stopping %s unraid-api process(es)...',
|
||||
2,
|
||||
);
|
||||
expect(loggerSpy).toHaveBeenNthCalledWith(4, 'Process did not exit cleanly, forcing shutdown', expect.any(Error));
|
||||
}, 15_000);
|
||||
|
||||
@@ -3,7 +3,7 @@ import 'reflect-metadata';
|
||||
import { test, expect } from 'vitest';
|
||||
import { getWriteableConfig } from '@app/core/utils/files/config-file-normalizer';
|
||||
import { initialState } from '@app/store/modules/config';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
test('it creates a FLASH config with NO OPTIONAL values', () => {
|
||||
const basicConfig = initialState;
|
||||
|
||||
@@ -3,7 +3,7 @@ import { addExitCallback } from 'catch-exit';
|
||||
import { cliLogger } from '@app/core/log';
|
||||
import { mainOptions } from '@app/cli/options';
|
||||
import { logToSyslog } from '@app/cli/log-to-syslog';
|
||||
import { getters } from '@app/store';
|
||||
import { getters } from '@app/store/index';
|
||||
import { getAllUnraidApiPids } from '@app/cli/get-unraid-api-pid';
|
||||
import { API_VERSION } from '@app/environment';
|
||||
|
||||
@@ -11,78 +11,78 @@ import { API_VERSION } from '@app/environment';
|
||||
* Start a new API process.
|
||||
*/
|
||||
export const start = async () => {
|
||||
// Set process title
|
||||
// Set process title
|
||||
|
||||
process.title = 'unraid-api';
|
||||
const runningProcesses = await getAllUnraidApiPids();
|
||||
if (runningProcesses.length > 0) {
|
||||
cliLogger.info('unraid-api is Already Running!');
|
||||
cliLogger.info('Run "unraid-api restart" to stop all running processes and restart');
|
||||
process.exit(1);
|
||||
}
|
||||
process.title = 'unraid-api';
|
||||
const runningProcesses = await getAllUnraidApiPids();
|
||||
if (runningProcesses.length > 0) {
|
||||
cliLogger.info('unraid-api is Already Running!');
|
||||
cliLogger.info('Run "unraid-api restart" to stop all running processes and restart');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Start API
|
||||
cliLogger.info('Starting unraid-api@v%s', API_VERSION);
|
||||
// Start API
|
||||
cliLogger.info('Starting unraid-api@v%s', API_VERSION);
|
||||
|
||||
// If we're in debug mode or we're NOT
|
||||
// in debug but ARE in the child process
|
||||
if (mainOptions.debug || process.env._DAEMONIZE_PROCESS) {
|
||||
// Log when the API exits
|
||||
addExitCallback((signal, exitCode, error) => {
|
||||
if (exitCode === 0 || exitCode === 130 || signal === 'SIGTERM') {
|
||||
logToSyslog('👋 Farewell. UNRAID API shutting down!');
|
||||
return;
|
||||
}
|
||||
// If we're in debug mode or we're NOT
|
||||
console.log(mainOptions);
|
||||
// in debug but ARE in the child process
|
||||
if (mainOptions.debug || process.env._DAEMONIZE_PROCESS) {
|
||||
// Log when the API exits
|
||||
addExitCallback((signal, exitCode, error) => {
|
||||
if (exitCode === 0 || exitCode === 130 || signal === 'SIGTERM') {
|
||||
logToSyslog('👋 Farewell. UNRAID API shutting down!');
|
||||
return;
|
||||
}
|
||||
|
||||
// Log when the API crashes
|
||||
if (signal === 'uncaughtException' && error) {
|
||||
logToSyslog(`⚠️ Caught exception: ${error.message}`);
|
||||
}
|
||||
// Log when the API crashes
|
||||
if (signal === 'uncaughtException' && error) {
|
||||
logToSyslog(`⚠️ Caught exception: ${error.message}`);
|
||||
}
|
||||
|
||||
// Log when we crash
|
||||
if (exitCode) {
|
||||
logToSyslog(`⚠️ UNRAID API crashed with exit code ${exitCode}`);
|
||||
return;
|
||||
}
|
||||
// Log when we crash
|
||||
if (exitCode) {
|
||||
logToSyslog(`⚠️ UNRAID API crashed with exit code ${exitCode}`);
|
||||
return;
|
||||
}
|
||||
|
||||
logToSyslog('🛑 UNRAID API crashed without an exit code?');
|
||||
});
|
||||
logToSyslog('🛑 UNRAID API crashed without an exit code?');
|
||||
});
|
||||
|
||||
logToSyslog('✔️ UNRAID API started successfully!');
|
||||
}
|
||||
logToSyslog('✔️ UNRAID API started successfully!');
|
||||
}
|
||||
|
||||
// Load bundled index file
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
require('../../index');
|
||||
console.log(mainOptions);
|
||||
|
||||
if (!mainOptions.debug) {
|
||||
if ('_DAEMONIZE_PROCESS' in process.env) {
|
||||
// In the child, clean up the tracking environment variable
|
||||
delete process.env._DAEMONIZE_PROCESS;
|
||||
} else {
|
||||
cliLogger.debug('Daemonizing process. %s %o', process.execPath, process.argv);
|
||||
await import ('@app/index.ts');
|
||||
if (!mainOptions.debug) {
|
||||
if ('_DAEMONIZE_PROCESS' in process.env) {
|
||||
// In the child, clean up the tracking environment variable
|
||||
delete process.env._DAEMONIZE_PROCESS;
|
||||
} else {
|
||||
cliLogger.debug('Daemonizing process. %s %o', process.execPath, process.argv);
|
||||
|
||||
// Spawn child
|
||||
// First arg is path (inside PKG), second arg is restart, stop, etc, rest is args to main argument
|
||||
const [path, , ...rest] = process.argv.slice(1);
|
||||
const replacedCommand = [path, 'start', ...rest];
|
||||
const child = spawn(process.execPath, replacedCommand, {
|
||||
// In the parent set the tracking environment variable
|
||||
env: Object.assign(process.env, { _DAEMONIZE_PROCESS: '1' }),
|
||||
// The process MUST have it's cwd set to the
|
||||
// path where it resides within the Nexe VFS
|
||||
cwd: getters.paths()['unraid-api-base'],
|
||||
stdio: 'ignore',
|
||||
detached: true,
|
||||
});
|
||||
// Spawn child
|
||||
// First arg is path (inside PKG), second arg is restart, stop, etc, rest is args to main argument
|
||||
const [path, , ...rest] = process.argv.slice(1);
|
||||
const replacedCommand = [path, 'start', ...rest];
|
||||
const child = spawn(process.execPath, replacedCommand, {
|
||||
// In the parent set the tracking environment variable
|
||||
env: Object.assign(process.env, { _DAEMONIZE_PROCESS: '1' }),
|
||||
// The process MUST have it's cwd set to the
|
||||
// path where it resides within the Nexe VFS
|
||||
cwd: getters.paths()['unraid-api-base'],
|
||||
stdio: 'ignore',
|
||||
detached: true,
|
||||
});
|
||||
|
||||
// Convert process into daemon
|
||||
child.unref();
|
||||
// Convert process into daemon
|
||||
child.unref();
|
||||
|
||||
cliLogger.debug('Daemonized successfully!');
|
||||
cliLogger.debug('Daemonized successfully!');
|
||||
|
||||
// Exit cleanly
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
// Exit cleanly
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getters, type RootState, store } from '@app/store';
|
||||
import { uniq } from 'lodash';
|
||||
import uniq from 'lodash/uniq';
|
||||
import {
|
||||
getServerIps,
|
||||
getUrlForField,
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
blockDevices,
|
||||
diskLayout,
|
||||
} from 'systeminformation';
|
||||
import { map as asyncMap } from 'p-iteration';
|
||||
import {
|
||||
type Disk,
|
||||
DiskInterfaceType,
|
||||
@@ -91,8 +90,9 @@ export const getDisks = async (
|
||||
const partitions = await blockDevices().then((devices) =>
|
||||
devices.filter((device) => device.type === 'part')
|
||||
);
|
||||
const disks = await asyncMap(await diskLayout(), async (disk) =>
|
||||
parseDisk(disk, partitions)
|
||||
const diskLayoutData = await diskLayout();
|
||||
const disks = await Promise.all(
|
||||
diskLayoutData.map((disk) => parseDisk(disk, partitions))
|
||||
);
|
||||
|
||||
return disks;
|
||||
|
||||
@@ -9,7 +9,7 @@ import type {
|
||||
MyServersConfig,
|
||||
MyServersConfigMemory,
|
||||
} from '@app/types/my-servers-config';
|
||||
import { isEqual } from 'lodash';
|
||||
import { isEqual } from 'lodash-es';
|
||||
|
||||
export type ConfigType = 'flash' | 'memory';
|
||||
type ConfigObject<T> = T extends 'flash'
|
||||
|
||||
@@ -40,16 +40,23 @@ export const phpLoader = async (options: Options) => {
|
||||
encodeParameters(body),
|
||||
];
|
||||
|
||||
return execa('php', options_, { cwd: __dirname })
|
||||
.then(({ stdout }) => {
|
||||
// Missing php file
|
||||
if (stdout.includes(`Warning: include(${file}): failed to open stream: No such file or directory in ${path.join(__dirname, '/wrapper.php')}`)) {
|
||||
throw new FileMissingError(file);
|
||||
}
|
||||
return execa('php', options_, { cwd: import.meta.dirname })
|
||||
.then(({ stdout }) => {
|
||||
// Missing php file
|
||||
if (
|
||||
stdout.includes(
|
||||
`Warning: include(${file}): failed to open stream: No such file or directory in ${path.join(
|
||||
import.meta.dirname,
|
||||
'/wrapper.php'
|
||||
)}`
|
||||
)
|
||||
) {
|
||||
throw new FileMissingError(file);
|
||||
}
|
||||
|
||||
return stdout;
|
||||
})
|
||||
.catch(error => {
|
||||
throw new PhpError(error);
|
||||
});
|
||||
return stdout;
|
||||
})
|
||||
.catch((error) => {
|
||||
throw new PhpError(error);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { execa } from 'execa';
|
||||
import { map as asyncMap } from 'p-iteration';
|
||||
import { sync as commandExistsSync } from 'command-exists';
|
||||
|
||||
interface Device {
|
||||
@@ -13,9 +12,9 @@ interface Device {
|
||||
* @param devices Devices to be checked.
|
||||
* @returns Processed devices.
|
||||
*/
|
||||
export const filterDevices = async (devices: Device[]): Promise<Device[]> => asyncMap(devices, async (device: Device) => {
|
||||
export const filterDevices = async (devices: Device[]): Promise<Device[]> => {
|
||||
// Don't run if we don't have the udevadm command available
|
||||
if (!commandExistsSync('udevadm')) return device;
|
||||
if (!commandExistsSync('udevadm')) return devices;
|
||||
|
||||
const networkDeviceIds = await execa('udevadm', 'info -q path -p /sys/class/net/eth0'.split(' '))
|
||||
.then(({ stdout }) => {
|
||||
@@ -25,7 +24,11 @@ export const filterDevices = async (devices: Device[]): Promise<Device[]> => asy
|
||||
.catch(() => []);
|
||||
|
||||
const allowed = new Set(networkDeviceIds);
|
||||
device.allowed = allowed.has(device.id);
|
||||
|
||||
return device;
|
||||
});
|
||||
const processedDevices = devices.map((device: Device) => {
|
||||
device.allowed = allowed.has(device.id);
|
||||
return device;
|
||||
});
|
||||
|
||||
return processedDevices;
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
HttpLink,
|
||||
InMemoryCache,
|
||||
split,
|
||||
} from '@apollo/client/core/core.cjs';
|
||||
} from '@apollo/client/core';
|
||||
import { onError } from '@apollo/client/link/error';
|
||||
import { getInternalApiAddress } from '@app/consts';
|
||||
import WebSocket from 'ws';
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*!
|
||||
* Copyright 2022 Lime Technology Inc. All rights reserved.
|
||||
* Written by: Alexis Tyler
|
||||
*/
|
||||
|
||||
import { MOTHERSHIP_GRAPHQL_LINK } from '@app/consts';
|
||||
import { store } from '@app/store';
|
||||
import { getDnsCache } from '@app/store/getters';
|
||||
import { setDNSCheck } from '@app/store/modules/cache';
|
||||
import { lookup as lookupDNS, resolve as resolveDNS } from 'dns';
|
||||
import ip from 'ip';
|
||||
import { promisify } from 'util';
|
||||
|
||||
const msHostname = new URL(MOTHERSHIP_GRAPHQL_LINK).host;
|
||||
|
||||
/**
|
||||
* Check if the local and network resolvers are able to see mothership
|
||||
*
|
||||
* See: https://nodejs.org/docs/latest/api/dns.html#dns_implementation_considerations
|
||||
*/
|
||||
export const checkDNS = async (hostname = msHostname): Promise<{ cloudIp: string }> => {
|
||||
const dnsCachedResuslt = getDnsCache();
|
||||
if (dnsCachedResuslt) {
|
||||
if (dnsCachedResuslt.cloudIp) {
|
||||
return { cloudIp: dnsCachedResuslt.cloudIp };
|
||||
}
|
||||
|
||||
if (dnsCachedResuslt.error) {
|
||||
throw dnsCachedResuslt.error;
|
||||
}
|
||||
}
|
||||
|
||||
let local: string | null = null;
|
||||
let network: string | null = null;
|
||||
try {
|
||||
// Check the local resolver like "ping" does
|
||||
// Check the DNS server the server has set - does a DNS query on the network
|
||||
const [localRes, networkRes] = await Promise.all([
|
||||
promisify(lookupDNS)(hostname).then(({ address }) => address),
|
||||
promisify(resolveDNS)(hostname).then(([address]) => address),
|
||||
]);
|
||||
local = localRes;
|
||||
network = networkRes;
|
||||
// The user's server and the DNS server they're using are returning different results
|
||||
if (!local.includes(network)) throw new Error(`Local and network resolvers showing different IP for "${hostname}". [local="${local ?? 'NOT FOUND'}"] [network="${network ?? 'NOT FOUND'}"]`);
|
||||
|
||||
// The user likely has a PI-hole or something similar running.
|
||||
if (ip.isPrivate(local)) throw new Error(`"${hostname}" is being resolved to a private IP. [IP=${local ?? 'NOT FOUND'}]`);
|
||||
} catch (error: unknown) {
|
||||
if (!(error instanceof Error)) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
store.dispatch(setDNSCheck({ cloudIp: null, error }));
|
||||
}
|
||||
|
||||
if (typeof local === 'string' || typeof network === 'string') {
|
||||
const validIp: string = local ?? network ?? '';
|
||||
store.dispatch(setDNSCheck({ cloudIp: validIp, error: null }));
|
||||
|
||||
return { cloudIp: validIp };
|
||||
}
|
||||
|
||||
return { cloudIp: '' };
|
||||
};
|
||||
@@ -1,11 +1,4 @@
|
||||
import {
|
||||
cpu,
|
||||
cpuFlags,
|
||||
mem,
|
||||
memLayout,
|
||||
osInfo,
|
||||
versions,
|
||||
} from 'systeminformation';
|
||||
import { cpu, cpuFlags, mem, memLayout, osInfo, versions } from 'systeminformation';
|
||||
import { docker } from '@app/core/utils/clients/docker';
|
||||
import {
|
||||
type InfoApps,
|
||||
@@ -30,7 +23,6 @@ import { AppError } from '@app/core/errors/app-error';
|
||||
import { cleanStdout } from '@app/core/utils/misc/clean-stdout';
|
||||
import { execaCommandSync, execa } from 'execa';
|
||||
import { pathExists } from 'path-exists';
|
||||
import { filter as asyncFilter } from 'p-iteration';
|
||||
import { isSymlink } from 'path-type';
|
||||
import type { PciDevice } from '@app/core/types';
|
||||
import { vmRegExps } from '@app/core/utils/vms/domain/vm-regexps';
|
||||
@@ -63,8 +55,7 @@ export const generateOs = async (): Promise<InfoOs> => {
|
||||
};
|
||||
|
||||
export const generateCpu = async (): Promise<InfoCpu> => {
|
||||
const { cores, physicalCores, speedMin, speedMax, stepping, ...rest } =
|
||||
await cpu();
|
||||
const { cores, physicalCores, speedMin, speedMax, stepping, ...rest } = await cpu();
|
||||
const flags = await cpuFlags()
|
||||
.then((flags) => flags.split(' '))
|
||||
.catch(() => []);
|
||||
@@ -118,9 +109,9 @@ export const generateVersions = async (): Promise<Versions> => {
|
||||
};
|
||||
|
||||
export const generateMemory = async (): Promise<InfoMemory> => {
|
||||
const layout = await memLayout().then((dims) =>
|
||||
dims.map((dim) => dim as MemoryLayout)
|
||||
).catch(() => []);
|
||||
const layout = await memLayout()
|
||||
.then((dims) => dims.map((dim) => dim as MemoryLayout))
|
||||
.catch(() => []);
|
||||
const info = await mem();
|
||||
let max = info.total;
|
||||
|
||||
@@ -136,14 +127,10 @@ export const generateMemory = async (): Promise<InfoMemory> => {
|
||||
throw error;
|
||||
});
|
||||
const lines = memoryInfo.split('\n');
|
||||
const header = lines.find((line) =>
|
||||
line.startsWith('Physical Memory Array')
|
||||
);
|
||||
const header = lines.find((line) => line.startsWith('Physical Memory Array'));
|
||||
if (header) {
|
||||
const start = lines.indexOf(header);
|
||||
const nextHeaders = lines
|
||||
.slice(start, -1)
|
||||
.find((line) => line.startsWith('Handle '));
|
||||
const nextHeaders = lines.slice(start, -1).find((line) => line.startsWith('Handle '));
|
||||
|
||||
if (nextHeaders) {
|
||||
const end = lines.indexOf(nextHeaders);
|
||||
@@ -151,9 +138,7 @@ export const generateMemory = async (): Promise<InfoMemory> => {
|
||||
|
||||
max = toBytes(
|
||||
fields
|
||||
?.find((line) =>
|
||||
line.trim().startsWith('Maximum Capacity')
|
||||
)
|
||||
?.find((line) => line.trim().startsWith('Maximum Capacity'))
|
||||
?.trim()
|
||||
?.split(': ')[1] ?? '0'
|
||||
);
|
||||
@@ -215,11 +200,13 @@ export const generateDevices = async (): Promise<Devices> => {
|
||||
const basePath = '/sys/bus/pci/devices/0000:';
|
||||
|
||||
// Remove devices with no IOMMU support
|
||||
const filteredDevices = await asyncFilter(
|
||||
devices,
|
||||
async (device: Readonly<PciDevice>) =>
|
||||
pathExists(`${basePath}${device.id}/iommu_group/`)
|
||||
);
|
||||
const filteredDevices = await Promise.all(
|
||||
devices.map((device: Readonly<PciDevice>) =>
|
||||
pathExists(`${basePath}${device.id}/iommu_group/`).then((exists) =>
|
||||
exists ? device : null
|
||||
)
|
||||
)
|
||||
).then((devices) => devices.filter((device) => device !== null));
|
||||
|
||||
/**
|
||||
* Run device cleanup
|
||||
@@ -230,36 +217,29 @@ export const generateDevices = async (): Promise<Devices> => {
|
||||
* - Add whether kernel-bound driver exists
|
||||
* - Cleanup device vendor/product names
|
||||
*/
|
||||
const processedDevices = await filterDevices(filteredDevices).then(
|
||||
async (devices) =>
|
||||
Promise.all(
|
||||
devices
|
||||
// @ts-expect-error - Device is not PciDevice
|
||||
.map((device) => addDeviceClass(device))
|
||||
.map(async (device) => {
|
||||
// Attempt to get the current kernel-bound driver for this pci device
|
||||
await isSymlink(
|
||||
`${basePath}${device.id}/driver`
|
||||
).then((symlink) => {
|
||||
if (symlink) {
|
||||
// $strLink = @readlink('/sys/bus/pci/devices/0000:'.$arrMatch['id']. '/driver');
|
||||
// if (!empty($strLink)) {
|
||||
// $strDriver = basename($strLink);
|
||||
// }
|
||||
}
|
||||
});
|
||||
const processedDevices = await filterDevices(filteredDevices).then(async (devices) =>
|
||||
Promise.all(
|
||||
devices
|
||||
// @ts-expect-error - Device is not PciDevice
|
||||
.map((device) => addDeviceClass(device))
|
||||
.map(async (device) => {
|
||||
// Attempt to get the current kernel-bound driver for this pci device
|
||||
await isSymlink(`${basePath}${device.id}/driver`).then((symlink) => {
|
||||
if (symlink) {
|
||||
// $strLink = @readlink('/sys/bus/pci/devices/0000:'.$arrMatch['id']. '/driver');
|
||||
// if (!empty($strLink)) {
|
||||
// $strDriver = basename($strLink);
|
||||
// }
|
||||
}
|
||||
});
|
||||
|
||||
// Clean up the vendor and product name
|
||||
device.vendorname = sanitizeVendor(
|
||||
device.vendorname
|
||||
);
|
||||
device.productname = sanitizeProduct(
|
||||
device.productname
|
||||
);
|
||||
// Clean up the vendor and product name
|
||||
device.vendorname = sanitizeVendor(device.vendorname);
|
||||
device.productname = sanitizeProduct(device.productname);
|
||||
|
||||
return device;
|
||||
})
|
||||
)
|
||||
return device;
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
return processedDevices;
|
||||
@@ -298,10 +278,7 @@ export const generateDevices = async (): Promise<Devices> => {
|
||||
const getSystemUSBDevices = async () => {
|
||||
try {
|
||||
// Get a list of all usb hubs so we can filter the allowed/disallowed
|
||||
const usbHubs = await execa(
|
||||
'cat /sys/bus/usb/drivers/hub/*/modalias',
|
||||
{ shell: true }
|
||||
)
|
||||
const usbHubs = await execa('cat /sys/bus/usb/drivers/hub/*/modalias', { shell: true })
|
||||
.then(({ stdout }) =>
|
||||
stdout.split('\n').map((line) => {
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-regexp-exec
|
||||
@@ -318,8 +295,7 @@ export const generateDevices = async (): Promise<Devices> => {
|
||||
emhttp.var.flashGuid !== device.guid;
|
||||
|
||||
// Remove usb hubs
|
||||
const filterUsbHubs = (device: Readonly<PciDevice>): boolean =>
|
||||
!usbHubs.includes(device.id);
|
||||
const filterUsbHubs = (device: Readonly<PciDevice>): boolean => !usbHubs.includes(device.id);
|
||||
|
||||
// Clean up the name
|
||||
const sanitizeVendorName = (device: Readonly<PciDevice>) => {
|
||||
@@ -330,9 +306,7 @@ export const generateDevices = async (): Promise<Devices> => {
|
||||
};
|
||||
};
|
||||
|
||||
const parseDeviceLine = (
|
||||
line: Readonly<string>
|
||||
): { value: string; string: string } => {
|
||||
const parseDeviceLine = (line: Readonly<string>): { value: string; string: string } => {
|
||||
const emptyLine = { value: '', string: '' };
|
||||
|
||||
// If the line is blank return nothing
|
||||
@@ -361,31 +335,19 @@ export const generateDevices = async (): Promise<Devices> => {
|
||||
const modifiedDevice: PciDevice = {
|
||||
...device,
|
||||
};
|
||||
const info = execaCommandSync(
|
||||
`lsusb -d ${device.id} -v`
|
||||
).stdout.split('\n');
|
||||
const info = execaCommandSync(`lsusb -d ${device.id} -v`).stdout.split('\n');
|
||||
const deviceName = device.name.trim();
|
||||
const iSerial = parseDeviceLine(
|
||||
info.filter((line) => line.includes('iSerial'))[0]
|
||||
);
|
||||
const iProduct = parseDeviceLine(
|
||||
info.filter((line) => line.includes('iProduct'))[0]
|
||||
);
|
||||
const iSerial = parseDeviceLine(info.filter((line) => line.includes('iSerial'))[0]);
|
||||
const iProduct = parseDeviceLine(info.filter((line) => line.includes('iProduct'))[0]);
|
||||
const iManufacturer = parseDeviceLine(
|
||||
info.filter((line) => line.includes('iManufacturer'))[0]
|
||||
);
|
||||
const idProduct = parseDeviceLine(
|
||||
info.filter((line) => line.includes('idProduct'))[0]
|
||||
);
|
||||
const idVendor = parseDeviceLine(
|
||||
info.filter((line) => line.includes('idVendor'))[0]
|
||||
);
|
||||
const serial = `${iSerial.string
|
||||
const idProduct = parseDeviceLine(info.filter((line) => line.includes('idProduct'))[0]);
|
||||
const idVendor = parseDeviceLine(info.filter((line) => line.includes('idVendor'))[0]);
|
||||
const serial = `${iSerial.string.slice(8).slice(0, 4)}-${iSerial.string
|
||||
.slice(8)
|
||||
.slice(0, 4)}-${iSerial.string.slice(8).slice(4)}`;
|
||||
const guid = `${idVendor.value.slice(
|
||||
2
|
||||
)}-${idProduct.value.slice(2)}-${serial}`;
|
||||
.slice(4)}`;
|
||||
const guid = `${idVendor.value.slice(2)}-${idProduct.value.slice(2)}-${serial}`;
|
||||
|
||||
modifiedDevice.serial = iSerial.string;
|
||||
modifiedDevice.product = iProduct.string;
|
||||
@@ -394,8 +356,7 @@ export const generateDevices = async (): Promise<Devices> => {
|
||||
|
||||
// Set name if missing
|
||||
if (deviceName === '') {
|
||||
modifiedDevice.name =
|
||||
`${iProduct.string} ${iManufacturer.string}`.trim();
|
||||
modifiedDevice.name = `${iProduct.string} ${iManufacturer.string}`.trim();
|
||||
}
|
||||
|
||||
// Name still blank? Replace using fallback default
|
||||
|
||||
@@ -2,6 +2,8 @@ import { join } from 'path';
|
||||
import { loadFilesSync } from '@graphql-tools/load-files';
|
||||
import { mergeTypeDefs } from '@graphql-tools/merge';
|
||||
|
||||
const files = loadFilesSync(join(__dirname, '../src/graphql/schema/types'), { extensions: ['graphql'] });
|
||||
const files = loadFilesSync(join(import.meta.dirname, '../src/graphql/schema/types'), {
|
||||
extensions: ['graphql'],
|
||||
});
|
||||
|
||||
export const typeDefs = mergeTypeDefs(files);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { hasSubscribedToChannel } from '@app/ws';
|
||||
import { type User } from '@app/core/types/states/user';
|
||||
import { AppError } from '@app/core/errors/app-error';
|
||||
import { ensurePermission } from '@app/core/utils/permissions/ensure-permission';
|
||||
import { pubsub } from '@app/core/pubsub';
|
||||
import { store } from '@app/store';
|
||||
import { graphqlLogger } from '@app/core/log';
|
||||
import {
|
||||
ServerStatus,
|
||||
type Server,
|
||||
@@ -15,6 +15,43 @@ export interface Context {
|
||||
websocketId: string;
|
||||
}
|
||||
|
||||
type Subscription = {
|
||||
total: number;
|
||||
channels: string[];
|
||||
};
|
||||
|
||||
const subscriptions: Record<string, Subscription> = {};
|
||||
|
||||
/**
|
||||
* Return current ws connection count.
|
||||
*/
|
||||
export const getWsConnectionCount = () => Object.values(subscriptions).filter(subscription => subscription.total >= 1).length;
|
||||
|
||||
/**
|
||||
* Return current ws connection count in channel.
|
||||
*/
|
||||
export const getWsConnectionCountInChannel = (channel: string) => Object.values(subscriptions).filter(subscription => subscription.channels.includes(channel)).length;
|
||||
|
||||
|
||||
|
||||
export const hasSubscribedToChannel = (id: string, channel: string) => {
|
||||
|
||||
graphqlLogger.debug('Subscribing to %s', channel);
|
||||
|
||||
// Setup initial object
|
||||
if (subscriptions[id] === undefined) {
|
||||
subscriptions[id] = {
|
||||
total: 1,
|
||||
channels: [channel],
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
subscriptions[id].total++;
|
||||
subscriptions[id].channels.push(channel);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Create a pubsub subscription.
|
||||
* @param channel The pubsub channel to subscribe to.
|
||||
@@ -33,7 +70,7 @@ export const createSubscription = (channel: string, resource?: string) => ({
|
||||
possession: 'any',
|
||||
});
|
||||
|
||||
hasSubscribedToChannel(context.websocketId, channel);
|
||||
hasSubscribedToChannel(context.websocketId, channel);
|
||||
return pubsub.asyncIterator(channel);
|
||||
},
|
||||
});
|
||||
|
||||
+3
-1
@@ -28,6 +28,7 @@ import { bootstrapNestServer } from '@app/unraid-api/main';
|
||||
import { type NestFastifyApplication } from '@nestjs/platform-fastify';
|
||||
import { type RawServerDefault } from 'fastify';
|
||||
import { setupLogRotation } from '@app/core/logrotate/setup-logrotate';
|
||||
import { WebSocket } from 'ws';
|
||||
import * as env from '@app/environment';
|
||||
|
||||
let server: NestFastifyApplication<RawServerDefault>;
|
||||
@@ -46,7 +47,8 @@ void am(
|
||||
|
||||
const cacheable = new CacheableLookup();
|
||||
|
||||
Object.assign(global, { WebSocket: require('ws') });
|
||||
|
||||
Object.assign(global, { WebSocket });
|
||||
// Ensure all DNS lookups are cached for their TTL
|
||||
cacheable.install(http.globalAgent);
|
||||
cacheable.install(https.globalAgent);
|
||||
|
||||
@@ -5,7 +5,7 @@ import { startAppListening } from '@app/store/listeners/listener-middleware';
|
||||
import { loadSingleStateFile } from '@app/store/modules/emhttp';
|
||||
import { StateFileKey } from '@app/store/types';
|
||||
import { isAnyOf } from '@reduxjs/toolkit';
|
||||
import { isEqual } from 'lodash';
|
||||
import { isEqual } from 'lodash-es';
|
||||
|
||||
export const enableArrayEventListener = () =>
|
||||
startAppListening({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { startAppListening } from '@app/store/listeners/listener-middleware';
|
||||
import { getDiff } from 'json-difference';
|
||||
import { isEqual } from 'lodash';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { logger } from '@app/core/log';
|
||||
import {
|
||||
type ConfigType,
|
||||
|
||||
@@ -22,7 +22,7 @@ import { getWriteableConfig } from '@app/core/utils/files/config-file-normalizer
|
||||
import { writeFileSync } from 'fs';
|
||||
import { safelySerializeObjectToIni } from '@app/core/utils/files/safe-ini-serializer';
|
||||
import { PUBSUB_CHANNEL, pubsub } from '@app/core/pubsub';
|
||||
import { isEqual } from 'lodash';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { setupRemoteAccessThunk } from '@app/store/actions/setup-remote-access';
|
||||
|
||||
export type SliceState = {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { createSlice } from '@reduxjs/toolkit';
|
||||
import { join, resolve as resolvePath } from 'path';
|
||||
|
||||
const initialState = {
|
||||
core: __dirname,
|
||||
core: import.meta.dirname,
|
||||
'unraid-api-base': '/usr/local/bin/unraid-api/' as const,
|
||||
'unraid-data': resolvePath(
|
||||
process.env.PATHS_UNRAID_DATA ?? ('/boot/config/plugins/dynamix.my.servers/data/' as const)
|
||||
|
||||
@@ -6,7 +6,7 @@ import { syncInfoApps } from '@app/store/sync/info-apps-sync';
|
||||
import { setupConfigPathWatch } from '@app/store/watch/config-watch';
|
||||
import { NODE_ENV } from '@app/environment';
|
||||
import { writeFileSync } from 'fs';
|
||||
import { isEqual } from 'lodash';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { join } from 'path';
|
||||
|
||||
export const startStoreSync = async () => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { logger } from '@app/core/log';
|
||||
import { PUBSUB_CHANNEL, pubsub } from '@app/core/pubsub';
|
||||
import { store } from '@app/store';
|
||||
import { DaemonConnectionStatus, type StoreSubscriptionHandler } from '@app/store/types';
|
||||
import { isEqual } from 'lodash';
|
||||
import { isEqual } from 'lodash-es';
|
||||
|
||||
type InfoAppsEvent = {
|
||||
info: {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { updateDockerState } from '@app/store/modules/docker';
|
||||
import { getDockerContainers } from '@app/core/modules/index';
|
||||
import { docker } from '@app/core/utils/index';
|
||||
import DockerEE from 'docker-event-emitter';
|
||||
import { debounce } from 'lodash';
|
||||
import { debounce } from 'lodash-es';
|
||||
|
||||
const updateContainerCache = async () => {
|
||||
try {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
export function notNull<T>(value: T): value is NonNullable<T> {
|
||||
return value !== null;
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
import { graphqlLogger } from '@app/core/log';
|
||||
|
||||
type Subscription = {
|
||||
total: number;
|
||||
channels: string[];
|
||||
};
|
||||
|
||||
const subscriptions: Record<string, Subscription> = {};
|
||||
|
||||
/**
|
||||
* Return current ws connection count.
|
||||
*/
|
||||
export const getWsConnectionCount = () => Object.values(subscriptions).filter(subscription => subscription.total >= 1).length;
|
||||
|
||||
/**
|
||||
* Return current ws connection count in channel.
|
||||
*/
|
||||
export const getWsConnectionCountInChannel = (channel: string) => Object.values(subscriptions).filter(subscription => subscription.channels.includes(channel)).length;
|
||||
|
||||
export const hasSubscribedToChannel = (id: string, channel: string) => {
|
||||
graphqlLogger.debug('Subscribing to %s', channel);
|
||||
|
||||
// Setup initial object
|
||||
if (subscriptions[id] === undefined) {
|
||||
subscriptions[id] = {
|
||||
total: 1,
|
||||
channels: [channel],
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
subscriptions[id].total++;
|
||||
subscriptions[id].channels.push(channel);
|
||||
};
|
||||
+3
-2
@@ -14,11 +14,12 @@
|
||||
"paths": {
|
||||
"@app/*": [
|
||||
"./src/*"
|
||||
]
|
||||
],
|
||||
},
|
||||
"skipLibCheck": true,
|
||||
"target": "ESNext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
|
||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
||||
"module": "ESNext", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
||||
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
"allowJs": false, /* Allow javascript files to be compiled. */
|
||||
"outDir": "./dist", /* Redirect output structure to the directory. */
|
||||
"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||
|
||||
Reference in New Issue
Block a user