From 226613974258f15d39932de94316a54aec2e29d2 Mon Sep 17 00:00:00 2001 From: Pujit Mehrotra Date: Fri, 28 Mar 2025 16:11:29 -0400 Subject: [PATCH] fix: extract callbacks to library (#1280) ## 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. --------- Co-authored-by: Eli Bosley Co-authored-by: Zack Spear --- pnpm-lock.yaml | 130 +++++++++------ web/.env.example | 2 +- web/_data/serverState.ts | 2 +- web/components/CallbackHandler.ce.vue | 4 +- web/components/UserProfile.ce.vue | 6 +- web/package.json | 1 + web/pages/index.vue | 2 +- web/pages/redirect.vue | 42 +++++ web/store/account.ts | 6 +- web/store/callback.ts | 230 -------------------------- web/store/callbackActions.ts | 133 +++++++++------ web/store/installKey.ts | 2 +- web/store/purchase.ts | 4 +- web/store/replaceRenew.ts | 12 +- web/store/trial.ts | 2 +- web/store/updateOsActions.ts | 2 +- web/store/updateOsChangelog.ts | 24 +-- 17 files changed, 230 insertions(+), 374 deletions(-) create mode 100644 web/pages/redirect.vue delete mode 100644 web/store/callback.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9d349f5e5..2e51cc7b8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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 diff --git a/web/.env.example b/web/.env.example index adb0fc96b..69dbf7e82 100644 --- a/web/.env.example +++ b/web/.env.example @@ -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. diff --git a/web/_data/serverState.ts b/web/_data/serverState.ts index f2700a511..a7ec6a2cd 100644 --- a/web/_data/serverState.ts +++ b/web/_data/serverState.ts @@ -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; diff --git a/web/components/CallbackHandler.ce.vue b/web/components/CallbackHandler.ce.vue index 8f4da7780..e780c6084 100644 --- a/web/components/CallbackHandler.ce.vue +++ b/web/components/CallbackHandler.ce.vue @@ -1,7 +1,7 @@ + + diff --git a/web/store/account.ts b/web/store/account.ts index 54cb72b50..c64b3a666 100644 --- a/web/store/account.ts +++ b/web/store/account.ts @@ -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(); diff --git a/web/store/callback.ts b/web/store/callback.ts deleted file mode 100644 index dfec3c984..000000000 --- a/web/store/callback.ts +++ /dev/null @@ -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, - }; - }); diff --git a/web/store/callbackActions.ts b/web/store/callbackActions.ts index 075d3466d..e7b8d5b1c 100644 --- a/web/store/callbackActions.ts +++ b/web/store/callbackActions.ts @@ -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('ready'); - const callbackData = ref(); 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); diff --git a/web/store/installKey.ts b/web/store/installKey.ts index ae5db1cc5..10a0c92e1 100644 --- a/web/store/installKey.ts +++ b/web/store/installKey.ts @@ -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 diff --git a/web/store/purchase.ts b/web/store/purchase.ts index e1c9fceed..625d4be26 100644 --- a/web/store/purchase.ts +++ b/web/store/purchase.ts @@ -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 = () => { diff --git a/web/store/replaceRenew.ts b/web/store/replaceRenew.ts index 06b3a3ba5..908f34f2a 100644 --- a/web/store/replaceRenew.ts +++ b/web/store/replaceRenew.ts @@ -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); diff --git a/web/store/trial.ts b/web/store/trial.ts index 3d1186630..43c4f8f57 100644 --- a/web/store/trial.ts +++ b/web/store/trial.ts @@ -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 diff --git a/web/store/updateOsActions.ts b/web/store/updateOsActions.ts index 292fa8aad..6b8119794 100644 --- a/web/store/updateOsActions.ts +++ b/web/store/updateOsActions.ts @@ -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'; /** diff --git a/web/store/updateOsChangelog.ts b/web/store/updateOsChangelog.ts index 23572b48b..4bd1ab5cd 100644 --- a/web/store/updateOsChangelog.ts +++ b/web/store/updateOsChangelog.ts @@ -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(null); watch(releaseForUpdate, async (newVal, oldVal) => {