fix: extract callbacks to library (#1280)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Chores**
  - Updated environment configuration to enhance secure handling.
  - Integrated a shared dependency for consistent callback management.

- **Refactor**
  - Streamlined callback actions management for improved performance.
- Upgraded the operating system version from a beta release to stable
(7.0.0), delivering enhanced reliability.
- Centralized callback actions store usage across components for better
state management.
- Removed deprecated callback management code to improve clarity and
maintainability.

- **New Features**
- Introduced a new redirect component to enhance user navigation
experience.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Eli Bosley <ekbosley@gmail.com>
Co-authored-by: Zack Spear <hi@zackspear.com>
This commit is contained in:
Pujit Mehrotra
2025-03-28 16:11:29 -04:00
committed by GitHub
parent 7fb78494cb
commit 2266139742
17 changed files with 230 additions and 374 deletions

130
pnpm-lock.yaml generated
View File

@@ -698,6 +698,9 @@ importers:
'@pinia/nuxt':
specifier: ^0.10.0
version: 0.10.1(magicast@0.3.5)(pinia@3.0.1(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2)))
'@unraid/shared-callbacks':
specifier: ^1.0.1
version: 1.0.1(@vueuse/core@13.0.0(vue@3.5.13(typescript@5.8.2)))
'@unraid/ui':
specifier: link:../unraid-ui
version: link:../unraid-ui
@@ -3769,8 +3772,14 @@ packages:
'@unraid/libvirt@1.1.3':
resolution: {integrity: sha512-aZNHkwgQ/0e+5BE7i3Ru4GC3Ev8fEUlnU0wmTcuSbpN0r74rMpiGwzA/4cqIJU8X+Kj//I80pkUufzXzHmMWwQ==}
engines: {node: '>=14'}
cpu: [x64, arm64]
os: [linux, darwin]
'@unraid/shared-callbacks@1.0.1':
resolution: {integrity: sha512-dBUsfCShYzSVHjRMSVXhfu81GKJqLTKKSbfGcKAFScI2Po+GL3ITcI5gaGFryiIzfR2mlIsRCgul+1y8nYNrzA==}
peerDependencies:
'@vueuse/core': ^10.9.0
'@unraid/tailwind-rem-to-rem@1.1.0':
resolution: {integrity: sha512-lc5tqdSs5zwBStlC18lK+pg+iX0/i/JtO8qWOqHNT5KHt66Ba6nwDr+mfKekQq7Bsi8noXMBQJDB5b2J/OmDsw==}
peerDependencies:
@@ -9317,6 +9326,7 @@ packages:
engines: {node: '>=0.6.0', teleport: '>=0.2.0'}
deprecated: |-
You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.
(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)
qs@6.13.0:
@@ -11745,7 +11755,7 @@ snapshots:
'@babel/traverse': 7.26.10
'@babel/types': 7.26.10
convert-source-map: 2.0.0
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
@@ -12120,7 +12130,7 @@ snapshots:
'@babel/parser': 7.27.0
'@babel/template': 7.26.9
'@babel/types': 7.26.10
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
globals: 11.12.0
transitivePeerDependencies:
- supports-color
@@ -12132,7 +12142,7 @@ snapshots:
'@babel/parser': 7.26.8
'@babel/template': 7.26.8
'@babel/types': 7.26.8
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
globals: 11.12.0
transitivePeerDependencies:
- supports-color
@@ -12450,7 +12460,7 @@ snapshots:
'@eslint/config-array@0.19.2':
dependencies:
'@eslint/object-schema': 2.1.6
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
minimatch: 3.1.2
transitivePeerDependencies:
- supports-color
@@ -12464,7 +12474,7 @@ snapshots:
bundle-require: 5.1.0(esbuild@0.25.1)
cac: 6.7.14
chokidar: 4.0.3
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
esbuild: 0.25.1
eslint: 9.23.0(jiti@2.4.2)
find-up: 7.0.0
@@ -12487,7 +12497,7 @@ snapshots:
'@eslint/eslintrc@3.3.1':
dependencies:
ajv: 6.12.6
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
espree: 10.3.0
globals: 14.0.0
ignore: 5.3.2
@@ -13058,12 +13068,12 @@ snapshots:
'@types/js-yaml': 4.0.9
'@whatwg-node/fetch': 0.10.3
chalk: 4.1.2
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
dotenv: 16.4.7
graphql: 16.10.0
graphql-request: 6.1.0(graphql@16.10.0)
http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.6(supports-color@9.4.0)
https-proxy-agent: 7.0.6
jose: 5.10.0
js-yaml: 4.1.0
lodash: 4.17.21
@@ -13308,7 +13318,7 @@ snapshots:
'@koa/router@12.0.2':
dependencies:
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
http-errors: 2.0.0
koa-compose: 4.1.0
methods: 1.1.2
@@ -13318,7 +13328,7 @@ snapshots:
'@kwsites/file-exists@1.1.1':
dependencies:
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
@@ -13359,7 +13369,7 @@ snapshots:
dependencies:
consola: 3.4.2
detect-libc: 2.0.3
https-proxy-agent: 7.0.6(supports-color@9.4.0)
https-proxy-agent: 7.0.6
node-fetch: 2.7.0
nopt: 8.1.0
semver: 7.7.1
@@ -14088,7 +14098,7 @@ snapshots:
'@pm2/pm2-version-check@1.0.4':
dependencies:
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
@@ -14968,7 +14978,7 @@ snapshots:
'@typescript-eslint/types': 8.28.0
'@typescript-eslint/typescript-estree': 8.28.0(typescript@5.8.2)
'@typescript-eslint/visitor-keys': 8.28.0
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
eslint: 9.23.0(jiti@2.4.2)
typescript: 5.8.2
transitivePeerDependencies:
@@ -14983,7 +14993,7 @@ snapshots:
dependencies:
'@typescript-eslint/typescript-estree': 8.28.0(typescript@5.8.2)
'@typescript-eslint/utils': 8.28.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
eslint: 9.23.0(jiti@2.4.2)
ts-api-utils: 2.1.0(typescript@5.8.2)
typescript: 5.8.2
@@ -14998,7 +15008,7 @@ snapshots:
dependencies:
'@typescript-eslint/types': 8.28.0
'@typescript-eslint/visitor-keys': 8.28.0
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
fast-glob: 3.3.3
is-glob: 4.0.3
minimatch: 9.0.5
@@ -15050,6 +15060,11 @@ snapshots:
tsx: 4.19.3
xml2js: 0.6.2
'@unraid/shared-callbacks@1.0.1(@vueuse/core@13.0.0(vue@3.5.13(typescript@5.8.2)))':
dependencies:
'@vueuse/core': 13.0.0(vue@3.5.13(typescript@5.8.2))
crypto-js: 4.2.0
'@unraid/tailwind-rem-to-rem@1.1.0(tailwindcss@3.4.17)':
dependencies:
tailwindcss: 3.4.17
@@ -15131,7 +15146,7 @@ snapshots:
dependencies:
'@ampproject/remapping': 2.3.0
'@bcoe/v8-coverage': 1.0.2
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
istanbul-lib-coverage: 3.2.2
istanbul-lib-report: 3.0.1
istanbul-lib-source-maps: 5.0.6
@@ -17164,14 +17179,14 @@ snapshots:
docker-event-emitter@0.3.0(dockerode@3.3.5):
dependencies:
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
dockerode: 3.3.5
transitivePeerDependencies:
- supports-color
docker-modem@3.0.8:
dependencies:
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
readable-stream: 3.6.2
split-ca: 1.0.1
ssh2: 1.16.0
@@ -17480,7 +17495,7 @@ snapshots:
esbuild-register@3.6.0(esbuild@0.25.1):
dependencies:
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
esbuild: 0.25.1
transitivePeerDependencies:
- supports-color
@@ -17645,7 +17660,7 @@ snapshots:
dependencies:
'@types/doctrine': 0.0.9
'@typescript-eslint/utils': 8.28.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
doctrine: 3.0.0
eslint: 9.23.0(jiti@2.4.2)
eslint-import-resolver-node: 0.3.9
@@ -17693,7 +17708,7 @@ snapshots:
'@es-joy/jsdoccomment': 0.49.0
are-docs-informative: 0.0.2
comment-parser: 1.4.1
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
escape-string-regexp: 4.0.0
eslint: 9.23.0(jiti@2.4.2)
espree: 10.3.0
@@ -17819,7 +17834,7 @@ snapshots:
ajv: 6.12.6
chalk: 4.1.2
cross-spawn: 7.0.6
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
escape-string-regexp: 4.0.0
eslint-scope: 8.3.0
eslint-visitor-keys: 4.2.0
@@ -18361,7 +18376,7 @@ snapshots:
dependencies:
basic-ftp: 5.0.5
data-uri-to-buffer: 6.0.2
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
@@ -18824,7 +18839,7 @@ snapshots:
http-proxy-agent@7.0.2:
dependencies:
agent-base: 7.1.3
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
@@ -18868,6 +18883,13 @@ snapshots:
quick-lru: 5.1.1
resolve-alpn: 1.2.1
https-proxy-agent@7.0.6:
dependencies:
agent-base: 7.1.3
debug: 4.4.0(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
https-proxy-agent@7.0.6(supports-color@9.4.0):
dependencies:
agent-base: 7.1.3
@@ -18918,7 +18940,7 @@ snapshots:
importx@0.4.4:
dependencies:
bundle-require: 5.1.0(esbuild@0.23.1)
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
esbuild: 0.23.1
jiti: 2.0.0-beta.3
jiti-v1: jiti@1.21.7
@@ -19012,7 +19034,7 @@ snapshots:
dependencies:
'@ioredis/commands': 1.2.0
cluster-key-slot: 1.1.2
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
denque: 2.1.0
lodash.defaults: 4.2.0
lodash.isarguments: 3.1.0
@@ -19299,7 +19321,7 @@ snapshots:
istanbul-lib-source-maps@5.0.6:
dependencies:
'@jridgewell/trace-mapping': 0.3.25
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
istanbul-lib-coverage: 3.2.2
transitivePeerDependencies:
- supports-color
@@ -19386,7 +19408,7 @@ snapshots:
form-data: 4.0.2
html-encoding-sniffer: 4.0.0
http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.6(supports-color@9.4.0)
https-proxy-agent: 7.0.6
is-potential-custom-element-name: 1.0.1
nwsapi: 2.2.16
parse5: 7.2.1
@@ -19501,7 +19523,7 @@ snapshots:
koa-send@5.0.1:
dependencies:
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
http-errors: 1.8.1
resolve-path: 1.4.0
transitivePeerDependencies:
@@ -19521,7 +19543,7 @@ snapshots:
content-disposition: 0.5.4
content-type: 1.0.5
cookies: 0.9.1
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
delegates: 1.0.0
depd: 2.0.0
destroy: 1.2.0
@@ -20636,10 +20658,10 @@ snapshots:
dependencies:
'@tootallnate/quickjs-emscripten': 0.23.0
agent-base: 7.1.3
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
get-uri: 6.0.4
http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.6(supports-color@9.4.0)
https-proxy-agent: 7.0.6
pac-resolver: 7.0.1
socks-proxy-agent: 8.0.5
transitivePeerDependencies:
@@ -20903,7 +20925,7 @@ snapshots:
pm2-axon-rpc@0.7.1:
dependencies:
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
@@ -20911,7 +20933,7 @@ snapshots:
dependencies:
amp: 0.3.1
amp-message: 0.1.2
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
escape-string-regexp: 4.0.0
transitivePeerDependencies:
- supports-color
@@ -20928,7 +20950,7 @@ snapshots:
pm2-sysmonit@1.2.8:
dependencies:
async: 3.2.6
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
pidusage: 2.0.21
systeminformation: 5.25.11
tx2: 1.0.5
@@ -20950,7 +20972,7 @@ snapshots:
commander: 2.15.1
croner: 4.1.97
dayjs: 1.11.13
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
enquirer: 2.3.6
eventemitter2: 5.0.1
fclone: 1.0.11
@@ -20989,7 +21011,7 @@ snapshots:
portfinder@1.0.35:
dependencies:
async: 3.2.6
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
@@ -21194,7 +21216,7 @@ snapshots:
postcss-styl@0.12.3:
dependencies:
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
fast-diff: 1.3.0
lodash.sortedlastindex: 4.1.0
postcss: 8.5.3
@@ -21289,9 +21311,9 @@ snapshots:
proxy-agent@6.4.0:
dependencies:
agent-base: 7.1.3
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.6(supports-color@9.4.0)
https-proxy-agent: 7.0.6
lru-cache: 7.18.3
pac-proxy-agent: 7.1.0
proxy-from-env: 1.1.0
@@ -21678,7 +21700,7 @@ snapshots:
require-in-the-middle@5.2.0:
dependencies:
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
module-details-from-path: 1.0.3
resolve: 1.22.10
transitivePeerDependencies:
@@ -22082,7 +22104,7 @@ snapshots:
dependencies:
'@kwsites/file-exists': 1.1.1
'@kwsites/promise-deferred': 1.1.1
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
@@ -22140,7 +22162,7 @@ snapshots:
socks-proxy-agent@8.0.5:
dependencies:
agent-base: 7.1.3
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
socks: 2.8.4
transitivePeerDependencies:
- supports-color
@@ -22396,7 +22418,7 @@ snapshots:
stylus@0.57.0:
dependencies:
css: 3.0.0
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
glob: 7.2.3
safer-buffer: 2.1.2
sax: 1.2.4
@@ -23125,7 +23147,7 @@ snapshots:
vite-node@3.0.9(@types/node@22.13.13)(jiti@2.4.2)(stylus@0.57.0)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0):
dependencies:
cac: 6.7.14
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
es-module-lexer: 1.6.0
pathe: 2.0.3
vite: 6.2.3(@types/node@22.13.13)(jiti@2.4.2)(stylus@0.57.0)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0)
@@ -23167,7 +23189,7 @@ snapshots:
'@microsoft/api-extractor': 7.43.0(@types/node@22.13.13)
'@rollup/pluginutils': 5.1.4(rollup@4.37.0)
'@vue/language-core': 1.8.27(typescript@5.8.2)
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
kolorist: 1.8.0
magic-string: 0.30.17
typescript: 5.8.2
@@ -23183,7 +23205,7 @@ snapshots:
dependencies:
'@antfu/utils': 0.7.10
'@rollup/pluginutils': 5.1.4(rollup@4.37.0)
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
error-stack-parser-es: 0.1.5
fs-extra: 11.3.0
open: 10.1.0
@@ -23198,7 +23220,7 @@ snapshots:
vite-plugin-inspect@11.0.0(@nuxt/kit@3.16.1(magicast@0.3.5))(vite@6.2.3(@types/node@22.13.13)(jiti@2.4.2)(stylus@0.57.0)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0)):
dependencies:
ansis: 3.17.0
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
error-stack-parser-es: 1.0.5
ohash: 2.0.11
open: 10.1.0
@@ -23216,7 +23238,7 @@ snapshots:
dependencies:
'@rollup/pluginutils': 4.2.1
chalk: 4.1.2
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
vite: 6.2.3(@types/node@22.13.13)(jiti@2.4.2)(stylus@0.57.0)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0)
optionalDependencies:
'@swc/core': 1.11.13(@swc/helpers@0.5.15)
@@ -23269,7 +23291,7 @@ snapshots:
vite-plugin-vuetify@2.1.0(vite@6.2.3(@types/node@22.13.13)(jiti@2.4.2)(stylus@0.57.0)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0))(vue@3.5.13(typescript@5.8.2))(vuetify@3.7.18):
dependencies:
'@vuetify/loader-shared': 2.1.0(vue@3.5.13(typescript@5.8.2))(vuetify@3.7.18)
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
upath: 2.0.1
vite: 6.2.3(@types/node@22.13.13)(jiti@2.4.2)(stylus@0.57.0)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0)
vue: 3.5.13(typescript@5.8.2)
@@ -23279,7 +23301,7 @@ snapshots:
vite-tsconfig-paths@5.1.4(typescript@5.8.2)(vite@6.2.3(@types/node@22.13.13)(jiti@2.4.2)(stylus@0.57.0)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0)):
dependencies:
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
globrex: 0.1.2
tsconfck: 3.1.5(typescript@5.8.2)
optionalDependencies:
@@ -23312,7 +23334,7 @@ snapshots:
'@vitest/spy': 3.0.9
'@vitest/utils': 3.0.9
chai: 5.2.0
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
expect-type: 1.2.0
magic-string: 0.30.17
pathe: 2.0.3
@@ -23395,7 +23417,7 @@ snapshots:
vue-eslint-parser@10.1.1(eslint@9.23.0(jiti@2.4.2)):
dependencies:
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
eslint: 9.23.0(jiti@2.4.2)
eslint-scope: 8.3.0
eslint-visitor-keys: 4.2.0
@@ -23408,7 +23430,7 @@ snapshots:
vue-eslint-parser@9.4.3(eslint@9.23.0(jiti@2.4.2)):
dependencies:
debug: 4.4.0(supports-color@9.4.0)
debug: 4.4.0(supports-color@5.5.0)
eslint: 9.23.0(jiti@2.4.2)
eslint-scope: 7.2.2
eslint-visitor-keys: 3.4.3

View File

@@ -2,7 +2,7 @@ VITE_ACCOUNT=http://localhost:5555
VITE_CONNECT=https://connect.myunraid.net
VITE_UNRAID_NET=https://unraid.ddev.site
VITE_OS_RELEASES="https://releases.unraid.net/os"
VITE_CALLBACK_KEY=aNotSoSecretKeyUsedToObfuscateQueryParams
VITE_CALLBACK_KEY=Uyv2o8e*FiQe8VeLekTqyX6Z*8XonB
VITE_ALLOW_CONSOLE_LOGS=true
# Base font size in pixels for Tailwind CSS. Used by the tailwind-rem-to-rem plugin to scale rem values.
# This lets us use rem's in our css instead of pixels while remaining webgui-compatible without additional hacks.

View File

@@ -108,7 +108,7 @@ switch (state) {
// const connectPluginInstalled = 'dynamix.unraid.net.staging.plg';
const connectPluginInstalled = 'dynamix.unraid.net.staging.plg';
const osVersion = '7.0.0-beta.2.10';
const osVersion = '7.0.0';
const osVersionBranch = 'stable';
// const parsedRegExp = regExp ? dayjs(regExp).format('YYYY-MM-DD') : undefined;

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import { useCallbackStore } from '~/store/callbackActions';
import { useCallbackActionsStore } from '~/store/callbackActions';
const callbackStore = useCallbackStore();
const callbackStore = useCallbackActionsStore();
onBeforeMount(() => {
callbackStore.watcher();

View File

@@ -7,7 +7,7 @@ import { devConfig } from '~/helpers/env';
import type { Server } from '~/types/server';
import { useCallbackActionsStore, useCallbackStore } from '~/store/callbackActions';
import { useCallbackActionsStore } from '~/store/callbackActions';
import { useServerStore } from '~/store/server';
import { useThemeStore } from '~/store/theme';
@@ -18,10 +18,10 @@ const props = defineProps<Props>();
const { t } = useI18n();
const callbackStore = useCallbackStore();
const callbackStore = useCallbackActionsStore();
const serverStore = useServerStore();
const { callbackData } = storeToRefs(useCallbackActionsStore());
const { callbackData } = storeToRefs(callbackStore);
const { name, description, guid, keyfile, lanIp, connectPluginInstalled } = storeToRefs(serverStore);
const { bannerGradient, theme } = storeToRefs(useThemeStore());

View File

@@ -85,6 +85,7 @@
"@jsonforms/vue-vuetify": "^3.5.1",
"@nuxtjs/color-mode": "^3.5.2",
"@pinia/nuxt": "^0.10.0",
"@unraid/shared-callbacks": "^1.0.1",
"@unraid/ui": "link:../unraid-ui",
"@vue/apollo-composable": "^4.2.1",
"@vueuse/components": "^13.0.0",

View File

@@ -4,7 +4,7 @@ import { BrandButton, BrandLogo, Toaster } from '@unraid/ui';
import { useDummyServerStore } from '~/_data/serverState';
import AES from 'crypto-js/aes';
import type { SendPayloads } from '~/store/callback';
import type { SendPayloads } from '@unraid/shared-callbacks';
import LogViewerCe from '~/components/Logs/LogViewer.ce.vue';
import SsoButtonCe from '~/components/SsoButton.ce.vue';

42
web/pages/redirect.vue Normal file
View File

@@ -0,0 +1,42 @@
<script setup lang="ts">
const parseRedirectTarget = (target: string | null) => {
if (target && target !== '/') {
// parse target and ensure it is a bare path with no query parameters
console.log(target);
return '/';
}
return '/';
};
const getRedirectUrl = () => {
const search = new URLSearchParams(window.location.search);
const targetRoute = parseRedirectTarget(search.get('target'));
if (search.has('data') && (search.size === 1 || search.size === 2)) {
return `${window.location.origin}${targetRoute}?data=${encodeURIComponent(search.get('data')!)}`;
}
return `${window.location.origin}${targetRoute}`;
};
onMounted(() => {
setTimeout(() => {
const textElement = document.getElementById('text');
if (textElement) {
textElement.style.display = 'block';
}
}, 750);
window.location.href = getRedirectUrl();
});
</script>
<template>
<div>
<div
id="text"
style="text-align: center; margin-top: calc(100vh - 75%); display: none; font-family: sans-serif"
>
<h1>Redirecting...</h1>
<h2><a :href="getRedirectUrl()">Click here if you are not redirected automatically</a></h2>
</div>
</div>
</template>

View File

@@ -3,13 +3,13 @@ import { logErrorMessages } from '@vue/apollo-util';
import { defineStore, createPinia, setActivePinia } from 'pinia';
import { CONNECT_SIGN_IN, CONNECT_SIGN_OUT } from './account.fragment';
import { useCallbackStore } from '~/store/callbackActions';
import { useCallbackActionsStore } from '~/store/callbackActions';
import { useErrorsStore } from '~/store/errors';
import { useReplaceRenewStore } from '~/store/replaceRenew';
import { useServerStore } from '~/store/server';
import { useUnraidApiStore } from '~/store/unraidApi';
import { ACCOUNT_CALLBACK } from '~/helpers/urls';
import type { ExternalSignIn, ExternalSignOut } from '~/store/callback';
import type { ExternalSignIn, ExternalSignOut } from '@unraid/shared-callbacks';
/**
* @see https://stackoverflow.com/questions/73476371/using-pinia-with-vue-js-web-components
* @see https://github.com/vuejs/pinia/discussions/1085
@@ -23,7 +23,7 @@ export interface ConnectSignInMutationPayload {
}
export const useAccountStore = defineStore('account', () => {
const callbackStore = useCallbackStore();
const callbackStore = useCallbackActionsStore();
const errorsStore = useErrorsStore();
const replaceRenewStore = useReplaceRenewStore();
const serverStore = useServerStore();

View File

@@ -1,230 +0,0 @@
/**
* This file is used to handle callbacks from the server.
* It is used in the following apps:
* - auth
* - craft-unraid
* - connect @todo
* - connect-components
*/
import AES from 'crypto-js/aes';
import Utf8 from 'crypto-js/enc-utf8';
import { defineStore, createPinia, setActivePinia } from 'pinia';
export type SignIn = 'signIn';
export type SignOut = 'signOut';
export type OemSignOut = 'oemSignOut';
export type Troubleshoot = 'troubleshoot';
export type Recover = 'recover';
export type Replace = 'replace';
export type TrialExtend = 'trialExtend';
export type TrialStart = 'trialStart';
export type Purchase = 'purchase';
export type Redeem = 'redeem';
export type Renew = 'renew';
export type Upgrade = 'upgrade';
export type UpdateOs = 'updateOs';
export type DowngradeOs = 'downgradeOs';
export type Manage = 'manage';
export type MyKeys = 'myKeys';
export type LinkKey = 'linkKey';
export type Activate = 'activate';
export type AccountActionTypes = Troubleshoot | SignIn | SignOut | OemSignOut | Manage | MyKeys | LinkKey;
export type AccountKeyActionTypes = Recover | Replace | TrialExtend | TrialStart | UpdateOs | DowngradeOs;
export type PurchaseActionTypes = Purchase | Redeem | Renew | Upgrade | Activate;
export type ServerActionTypes = AccountActionTypes | AccountKeyActionTypes | PurchaseActionTypes;
export type ServerState = 'BASIC'
| 'PLUS'
| 'PRO'
| 'TRIAL'
| 'EEXPIRED'
| 'ENOKEYFILE'
| 'EGUID'
| 'EGUID1'
| 'ETRIAL'
| 'ENOKEYFILE2'
| 'ENOKEYFILE1'
| 'ENOFLASH'
| 'ENOFLASH1'
| 'ENOFLASH2'
| 'ENOFLASH3'
| 'ENOFLASH4'
| 'ENOFLASH5'
| 'ENOFLASH6'
| 'ENOFLASH7'
| 'EBLACKLISTED'
| 'EBLACKLISTED1'
| 'EBLACKLISTED2'
| 'ENOCONN'
| 'STARTER'
| 'UNLEASHED'
| 'LIFETIME'
| 'STALE'
| undefined;
export interface ServerData {
description?: string;
deviceCount?: number;
expireTime?: number;
flashProduct?: string;
flashVendor?: string;
guid?: string;
keyfile?: string;
locale?: string;
name?: string;
osVersion?: string;
osVersionBranch?: 'stable' | 'next' | 'preview' | 'test';
registered: boolean;
regExp?: number;
regUpdatesExpired?: boolean;
regGen?: number;
regGuid?: string;
regTy?: string;
state: ServerState;
wanFQDN?: string;
}
export interface UserInfo {
'custom:ips_id'?: string;
email?: string;
email_verifed?: 'true' | 'false';
preferred_username?: string;
sub?: string;
username?: string;
/**
* @param identities {string} JSON string containing @type Identity[]
*/
identities?: string;
/**
* @param cognito:groups {string[]} JSON string containing @type string[]
*
* Will contain all groups for the signed in user, used for determining which branch to use
* @example ["download-preview", "unraidPOOLID_Google"]
*/
'cognito:groups'?: string[];
}
export interface ExternalSignIn {
type: SignIn;
apiKey: string;
user: UserInfo;
}
export interface ExternalSignOut {
type: SignOut | OemSignOut;
}
export interface ExternalKeyActions {
type: PurchaseActionTypes | AccountKeyActionTypes;
keyUrl: string;
}
export interface ExternalUpdateOsAction {
type: DowngradeOs | UpdateOs;
sha256: string;
}
export interface ServerPayload {
type: ServerActionTypes;
server: ServerData;
}
export interface ServerTroubleshoot {
type: Troubleshoot;
server: ServerData;
}
export type ExternalActions = ExternalSignIn | ExternalSignOut | ExternalKeyActions | ExternalUpdateOsAction;
export type UpcActions = ServerPayload | ServerTroubleshoot;
export type SendPayloads = ExternalActions[] | UpcActions[];
/**
* Payload containing all actions that are sent from account.unraid.net to the server
*/
export interface ExternalPayload {
type: 'forUpc';
actions: ExternalActions[];
sender: string;
}
/**
* Payload containing all actions that are sent from a server to account.unraid.net
*/
export interface UpcPayload {
actions: UpcActions[];
sender: string;
type: 'fromUpc';
}
export type QueryPayloads = ExternalPayload | UpcPayload;
export interface CallbackActionsStore {
saveCallbackData: (decryptedData: QueryPayloads) => void;
encryptionKey: string;
sendType: 'fromUpc' | 'forUpc';
}
/**
* @see https://stackoverflow.com/questions/73476371/using-pinia-with-vue-js-web-components
* @see https://github.com/vuejs/pinia/discussions/1085
*/
setActivePinia(createPinia());
export const useCallbackStoreGeneric = (
useCallbackActions: () => CallbackActionsStore
) =>
defineStore('callback', () => {
const callbackActions = useCallbackActions();
const send = (url: string, payload: SendPayloads, redirectType?: 'newTab' | 'replace', sendType?: string) => {
console.debug('[callback.send]');
const stringifiedData = JSON.stringify({
actions: [...payload],
sender: window.location.href.replace('/Tools/Update', '/Tools'),
type: sendType ?? callbackActions.sendType,
});
const encryptedMessage = AES.encrypt(
stringifiedData,
callbackActions.encryptionKey,
).toString();
/**
* Build and go to url
*/
const destinationUrl = new URL(url.replace('/Tools/Update', '/Tools'));
destinationUrl.searchParams.set('data', encodeURI(encryptedMessage));
console.debug('[callback.send]', encryptedMessage, destinationUrl);
if (redirectType === 'newTab') { // helpful when webgui is in an iframe and callbacks need to be opened in a new tab
window.open(destinationUrl.toString(), '_blank');
return;
}
if (redirectType === 'replace') { // helpful when autoredirecting and we want to replace the current url to prevent back button issues with auto redirect loops
window.location.replace(destinationUrl.toString());
return;
}
window.location.href = destinationUrl.toString();
};
const watcher = () => {
console.debug('[callback.watcher]');
const currentUrl = new URL(window.location.toString());
const callbackValue = decodeURI(currentUrl.searchParams.get('data') ?? '');
console.debug('[callback.watcher]', { callbackValue });
if (!callbackValue) {
return console.debug('[callback.watcher] no callback to handle');
}
const decryptedMessage = AES.decrypt(callbackValue, callbackActions.encryptionKey);
const decryptedData: QueryPayloads = JSON.parse(decryptedMessage.toString(Utf8));
console.debug('[callback.watcher]', decryptedMessage, decryptedData);
// Parse the data and perform actions
callbackActions.saveCallbackData(decryptedData);
};
return {
send,
watcher,
};
});

View File

@@ -1,4 +1,15 @@
import { defineStore } from 'pinia';
import { createPinia, defineStore, setActivePinia } from 'pinia';
import { useCallback } from '@unraid/shared-callbacks';
import type {
ExternalActions,
ExternalKeyActions,
ExternalSignIn,
ExternalSignOut,
ExternalUpdateOsAction,
QueryPayloads,
} from '@unraid/shared-callbacks';
import { addPreventClose, removePreventClose } from '~/composables/preventClose';
import { useAccountStore } from '~/store/account';
@@ -6,17 +17,15 @@ import { useInstallKeyStore } from '~/store/installKey';
import { useServerStore } from '~/store/server';
import { useUpdateOsStore } from '~/store/updateOs';
import { useUpdateOsActionsStore } from '~/store/updateOsActions';
import {
useCallbackStoreGeneric,
type CallbackActionsStore,
type ExternalKeyActions,
type ExternalSignIn,
type ExternalSignOut,
type ExternalUpdateOsAction,
type QueryPayloads,
} from '~/store/callback';
type CallbackStatus = 'closing' | 'error' | 'loading' | 'ready' | 'success';
setActivePinia(createPinia());
export const useCallbackActionsStore = defineStore('callbackActions', () => {
const { send, watcher: providedWatcher } = useCallback({
encryptionKey: import.meta.env.VITE_CALLBACK_KEY,
});
const accountStore = useAccountStore();
const installKeyStore = useInstallKeyStore();
const serverStore = useServerStore();
@@ -24,15 +33,18 @@ export const useCallbackActionsStore = defineStore('callbackActions', () => {
const updateOsStore = useUpdateOsStore(); // if we remove this line, the store things break…
const updateOsActionsStore = useUpdateOsActionsStore();
type CallbackStatus = 'closing' | 'error' | 'loading' | 'ready' | 'success';
const callbackStatus = ref<CallbackStatus>('ready');
const callbackData = ref<QueryPayloads>();
const callbackError = ref();
const saveCallbackData = (
decryptedData?: QueryPayloads,
) => {
const watcher = () => {
const result = providedWatcher();
if (result) {
saveCallbackData(result);
}
};
const saveCallbackData = (decryptedData?: QueryPayloads) => {
if (decryptedData) {
callbackData.value = decryptedData;
}
@@ -54,9 +66,15 @@ export const useCallbackActionsStore = defineStore('callbackActions', () => {
'renew',
'upgrade',
];
const redirectToCallbackType = () => {
console.debug('[redirectToCallbackType]');
if (!callbackData.value || !callbackData.value.type || callbackData.value.type !== 'forUpc' || !callbackData.value.actions?.length) {
if (
!callbackData.value ||
!callbackData.value.type ||
callbackData.value.type !== 'forUpc' ||
!callbackData.value.actions?.length
) {
callbackError.value = 'Callback redirect type not present or incorrect';
callbackStatus.value = 'ready'; // default status
return console.error('[redirectToCallbackType]', callbackError.value);
@@ -65,56 +83,64 @@ export const useCallbackActionsStore = defineStore('callbackActions', () => {
callbackStatus.value = 'loading';
// Parse the data and perform actions
callbackData.value.actions.forEach(async (action, index, array) => {
console.debug('[redirectToCallbackType]', { action, index, array });
callbackData.value.actions.forEach(
async (action: ExternalActions, index: number, array: ExternalActions[]) => {
console.debug('[redirectToCallbackType]', { action, index, array });
if (actionTypesWithKey.includes(action.type)) {
await installKeyStore.install(action as ExternalKeyActions);
}
if (actionTypesWithKey.includes(action.type)) {
await installKeyStore.install(action as ExternalKeyActions);
}
if (action.type === 'signIn' && action?.user) {
accountStore.setAccountAction(action as ExternalSignIn);
await accountStore.setConnectSignInPayload({
apiKey: action?.apiKey ?? '',
email: action.user?.email ?? '',
preferred_username: action.user?.preferred_username ?? '',
});
}
if (action.type === 'signIn' && action?.user) {
accountStore.setAccountAction(action as ExternalSignIn);
await accountStore.setConnectSignInPayload({
apiKey: action?.apiKey ?? '',
email: action.user?.email ?? '',
preferred_username: action.user?.preferred_username ?? '',
});
}
if (action.type === 'signOut' || action.type === 'oemSignOut') {
accountStore.setAccountAction(action as ExternalSignOut);
await accountStore.setQueueConnectSignOut(true);
}
if (action.type === 'signOut' || action.type === 'oemSignOut') {
accountStore.setAccountAction(action as ExternalSignOut);
await accountStore.setQueueConnectSignOut(true);
}
if (action.type === 'updateOs' || action.type === 'downgradeOs') {
updateOsActionsStore.setUpdateOsAction(action as ExternalUpdateOsAction);
await updateOsActionsStore.actOnUpdateOsAction(action.type === 'downgradeOs');
if (action.type === 'updateOs' || action.type === 'downgradeOs') {
updateOsActionsStore.setUpdateOsAction(action as ExternalUpdateOsAction);
await updateOsActionsStore.actOnUpdateOsAction(action.type === 'downgradeOs');
if (array.length === 1) { // only 1 action, skip refresh server state
console.debug('[redirectToCallbackType] updateOs done');
// removing query string relase is set so users can't refresh the page and go through the same actions
window.history.replaceState(null, '', window.location.pathname);
return;
if (array.length === 1) {
// only 1 action, skip refresh server state
console.debug('[redirectToCallbackType] updateOs done');
// removing query string relase is set so users can't refresh the page and go through the same actions
window.history.replaceState(null, '', window.location.pathname);
return;
}
}
if (array.length === index + 1) {
// all actions have run
await serverStore.refreshServerState();
}
}
if (array.length === (index + 1)) { // all actions have run
await serverStore.refreshServerState();
// callbackStatus.value = 'done';
}
});
);
};
// Wait until we have a refreshServerStateStatus value to determine callbackStatus
const refreshServerStateStatus = computed(() => serverStore.refreshServerStateStatus);
watchEffect(() => {
if (callbackData.value?.actions && refreshServerStateStatus.value === 'done') {
if (callbackData.value.actions.length > 1) {
// if we have more than 1 action it means there was a key install and an account action so both need to be successful
const allSuccess = accountStore.accountActionStatus === 'success' && installKeyStore.keyInstallStatus === 'success';
const allSuccess =
accountStore.accountActionStatus === 'success' &&
installKeyStore.keyInstallStatus === 'success';
callbackStatus.value = allSuccess ? 'success' : 'error';
} else {
// only 1 action needs to be successful
const oneSuccess = accountStore.accountActionStatus === 'success' || installKeyStore.keyInstallStatus === 'success';
const oneSuccess =
accountStore.accountActionStatus === 'success' ||
installKeyStore.keyInstallStatus === 'success';
callbackStatus.value = oneSuccess ? 'success' : 'error';
}
}
@@ -124,7 +150,10 @@ export const useCallbackActionsStore = defineStore('callbackActions', () => {
}
});
const setCallbackStatus = (status: CallbackStatus) => { callbackStatus.value = status; };
const setCallbackStatus = (status: CallbackStatus) => {
callbackStatus.value = status;
};
watch(callbackStatus, (newVal, oldVal) => {
if (newVal === 'loading') {
addPreventClose();
@@ -144,10 +173,10 @@ export const useCallbackActionsStore = defineStore('callbackActions', () => {
redirectToCallbackType,
saveCallbackData,
setCallbackStatus,
send,
watcher,
// helpers
sendType: 'fromUpc',
encryptionKey: import.meta.env.VITE_CALLBACK_KEY,
};
});
export const useCallbackStore = useCallbackStoreGeneric(useCallbackActionsStore as unknown as () => CallbackActionsStore);

View File

@@ -1,7 +1,7 @@
import { defineStore, createPinia, setActivePinia } from 'pinia';
import { WebguiInstallKey } from '~/composables/services/webgui';
import { useErrorsStore } from '~/store/errors';
import type { ExternalKeyActions } from '~/store/callback';
import type { ExternalKeyActions } from '@unraid/shared-callbacks';
/**
* @see https://stackoverflow.com/questions/73476371/using-pinia-with-vue-js-web-components
* @see https://github.com/vuejs/pinia/discussions/1085

View File

@@ -1,7 +1,7 @@
import { defineStore, createPinia, setActivePinia } from 'pinia';
import { PURCHASE_CALLBACK } from '~/helpers/urls';
import { useCallbackStore } from '~/store/callbackActions';
import { useCallbackActionsStore } from '~/store/callbackActions';
import { useServerStore } from '~/store/server';
/**
@@ -11,7 +11,7 @@ import { useServerStore } from '~/store/server';
setActivePinia(createPinia());
export const usePurchaseStore = defineStore('purchase', () => {
const callbackStore = useCallbackStore();
const callbackStore = useCallbackActionsStore();
const serverStore = useServerStore();
const activate = () => {

View File

@@ -16,17 +16,10 @@ import {
import { BrandLoading } from '@unraid/ui';
import type { BadgeProps } from '@unraid/ui';
import type {
// type KeyLatestResponse,
ValidateGuidResponse,
} from '~/composables/services/keyServer';
import type { ValidateGuidResponse } from '~/composables/services/keyServer';
import type { WretchError } from 'wretch';
import {
// keyLatest,
validateGuid,
} from '~/composables/services/keyServer';
// import { useCallbackStore } from '~/store/callbackActions';
import { validateGuid } from '~/composables/services/keyServer';
import { useServerStore } from '~/store/server';
/**
@@ -49,7 +42,6 @@ const BrandLoadingIcon = () => h(BrandLoading, { variant: 'white' });
export const REPLACE_CHECK_LOCAL_STORAGE_KEY = 'unraidReplaceCheck';
export const useReplaceRenewStore = defineStore('replaceRenewCheck', () => {
// const callbackStore = useCallbackStore();
const serverStore = useServerStore();
const guid = computed(() => serverStore.guid);

View File

@@ -6,7 +6,7 @@ import { startTrial, type StartTrialResponse } from '~/composables/services/keyS
import { useCallbackActionsStore } from '~/store/callbackActions';
import { useDropdownStore } from '~/store/dropdown';
import { useServerStore } from '~/store/server';
import type { ExternalPayload, TrialExtend, TrialStart } from '~/store/callback';
import type { ExternalPayload, TrialExtend, TrialStart } from '@unraid/shared-callbacks';
/**
* @see https://stackoverflow.com/questions/73476371/using-pinia-with-vue-js-web-components

View File

@@ -11,7 +11,7 @@ import { useAccountStore } from '~/store/account';
import { useServerStore } from '~/store/server';
import { useUpdateOsStore } from '~/store/updateOs';
import type { ExternalUpdateOsAction } from '~/store/callback';
import type { ExternalUpdateOsAction } from '@unraid/shared-callbacks';
import type { UserProfileLink } from '~/types/userProfile';
/**

View File

@@ -1,18 +1,18 @@
import { Markdown } from '@/helpers/markdown';
import { request } from '~/composables/services/request';
import { DOCS_RELEASE_NOTES } from '~/helpers/urls';
import { useCallbackStore } from '~/store/callbackActions';
// import { useServerStore } from '~/store/server';
import type { ServerUpdateOsResponse } from '~/types/server';
import { baseUrl } from 'marked-base-url';
import { defineStore } from 'pinia';
import prerelease from 'semver/functions/prerelease';
import { computed, ref, watch } from 'vue';
import { defineStore } from 'pinia';
import { Markdown } from '@/helpers/markdown';
import { DOCS_RELEASE_NOTES } from '~/helpers/urls';
import { baseUrl } from 'marked-base-url';
import prerelease from 'semver/functions/prerelease';
import type { ServerUpdateOsResponse } from '~/types/server';
import { request } from '~/composables/services/request';
import { useCallbackActionsStore } from '~/store/callbackActions';
export const useUpdateOsChangelogStore = defineStore('updateOsChangelog', () => {
const callbackStore = useCallbackStore();
// const serverStore = useServerStore();
// const osVersionBranch = computed(() => serverStore.osVersionBranch);
const callbackStore = useCallbackActionsStore();
const releaseForUpdate = ref<ServerUpdateOsResponse | null>(null);
watch(releaseForUpdate, async (newVal, oldVal) => {