From 58665a4e983f863cd5b91c4b00c2c4b2bba61009 Mon Sep 17 00:00:00 2001 From: Eli Bosley Date: Fri, 25 Oct 2024 23:19:43 -0400 Subject: [PATCH] fix: trigger loading correctly --- .github/workflows/main.yml | 10 + web/helpers/create-apollo-client.ts | 7 +- web/store/server.ts | 1303 +++++++++++++++++---------- web/store/unraidApi.ts | 14 - 4 files changed, 820 insertions(+), 514 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index dde014570..d4c5a9f10 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -203,6 +203,16 @@ jobs: with: name: connect-files + - name: Write Changelog to Plugin XML + run: | + notes=$(cat << EOF + Pull Request Build: ${{ github.event.pull_request.number }} + $(git log -1 --pretty=%B) + EOF + ) + escapedNotes=$(sed -e 's/[&\\/]/\\&/g; s/$/\\/' -e '$s/\\$//' <<<"$notes") + sed -i -z -E "s/(.*)<\/CHANGES>/\n${escapedNotes}\n<\/CHANGES>/g" "dynamix.unraid.net.staging.plg" + - name: Copy other release files to pr-release run: | cp archive/dynamix.unraid.net.staging-*.txz pr-release/ diff --git a/web/helpers/create-apollo-client.ts b/web/helpers/create-apollo-client.ts index 9178f141c..adbd8a56b 100644 --- a/web/helpers/create-apollo-client.ts +++ b/web/helpers/create-apollo-client.ts @@ -15,10 +15,7 @@ import { createClient } from "graphql-ws"; import { WEBGUI_GRAPHQL } from "./urls"; import { createApolloCache } from "./apollo-cache"; import { ApolloLink, Observable } from "@apollo/client/core"; -import fs from "fs"; -import path from "path"; import { useServerStore } from "~/store/server"; -import { connect } from "http2"; const httpEndpoint = WEBGUI_GRAPHQL; const wsEndpoint = new URL(WEBGUI_GRAPHQL.toString().replace("http", "ws")); @@ -88,8 +85,8 @@ const retryLink = new RetryLink({ const disableClientLink = new ApolloLink((operation, forward) => { const serverStore = useServerStore(); const { connectPluginInstalled, guid} = toRefs(serverStore); - console.log("serverStore.connectPluginInstalled", connectPluginInstalled.value, guid.value); - if (!connectPluginInstalled.value) { + console.log("serverStore.connectPluginInstalled", connectPluginInstalled?.value, guid?.value); + if (!connectPluginInstalled?.value) { return new Observable((observer) => { console.warn("connectPluginInstalled is false, aborting request"); observer.complete(); diff --git a/web/store/server.ts b/web/store/server.ts index 09a3be1a7..9cc4acc49 100644 --- a/web/store/server.ts +++ b/web/store/server.ts @@ -1,9 +1,9 @@ /** * @todo Check OS and Connect Plugin versions against latest via API every session -*/ -import dayjs from 'dayjs'; -import { defineStore, createPinia, setActivePinia } from 'pinia'; -import prerelease from 'semver/functions/prerelease'; + */ +import dayjs from "dayjs"; +import { defineStore, createPinia, setActivePinia } from "pinia"; +import prerelease from "semver/functions/prerelease"; import { ArrowPathIcon, ArrowRightOnRectangleIcon, @@ -11,26 +11,30 @@ import { GlobeAltIcon, InformationCircleIcon, KeyIcon, - QuestionMarkCircleIcon -} from '@heroicons/vue/24/solid'; -import { useQuery } from '@vue/apollo-composable'; + QuestionMarkCircleIcon, +} from "@heroicons/vue/24/solid"; +import { useLazyQuery, useQuery } from "@vue/apollo-composable"; -import { SERVER_CLOUD_FRAGMENT, SERVER_STATE_QUERY } from './server.fragment'; -import { useFragment } from '~/composables/gql/fragment-masking'; -import { WebguiState, WebguiUpdateIgnore } from '~/composables/services/webgui'; +import { SERVER_CLOUD_FRAGMENT, SERVER_STATE_QUERY } from "./server.fragment"; +import { useFragment } from "~/composables/gql/fragment-masking"; +import { WebguiState, WebguiUpdateIgnore } from "~/composables/services/webgui"; import { WEBGUI_SETTINGS_MANAGMENT_ACCESS, WEBGUI_TOOLS_REGISTRATION, WEBGUI_TOOLS_UPDATE, -} from '~/helpers/urls'; -import { useAccountStore } from '~/store/account'; -import { useErrorsStore, type Error } from '~/store/errors'; -import { usePurchaseStore } from '~/store/purchase'; -import { useThemeStore, type Theme } from '~/store/theme'; -import { useUnraidApiStore } from '~/store/unraidApi'; +} from "~/helpers/urls"; +import { useAccountStore } from "~/store/account"; +import { useErrorsStore, type Error } from "~/store/errors"; +import { usePurchaseStore } from "~/store/purchase"; +import { useThemeStore, type Theme } from "~/store/theme"; +import { useUnraidApiStore } from "~/store/unraidApi"; -import type { ApolloQueryResult } from '@apollo/client/core/types'; -import type { Config, PartialCloudFragment, serverStateQuery } from '~/composables/gql/graphql'; +import type { ApolloQueryResult } from "@apollo/client/core/types"; +import type { + Config, + PartialCloudFragment, + serverStateQuery, +} from "~/composables/gql/graphql"; import type { Server, ServerAccountCallbackSendPayload, @@ -45,7 +49,7 @@ import type { ServerStateArray, ServerOsVersionBranch, ServerUpdateOsResponse, -} from '~/types/server'; +} from "~/types/server"; /** * @see https://stackoverflow.com/questions/73476371/using-pinia-with-vue-js-web-components @@ -53,7 +57,7 @@ import type { */ setActivePinia(createPinia()); -export const useServerStore = defineStore('server', () => { +export const useServerStore = defineStore("server", () => { const accountStore = useAccountStore(); const errorsStore = useErrorsStore(); const purchaseStore = usePurchaseStore(); @@ -62,48 +66,52 @@ export const useServerStore = defineStore('server', () => { /** * State */ - const apiKey = ref(''); - const apiVersion = ref(''); + const apiKey = ref(""); + const apiVersion = ref(""); const array = ref(); // helps to display warning next to array status - const arrayWarning = computed(() => !!(stateDataError.value || serverConfigError.value)); + const arrayWarning = computed( + () => !!(stateDataError.value || serverConfigError.value) + ); const computedArray = computed(() => { if (arrayWarning.value) { - if (array.value?.state === 'Stopped') { - return 'Stopped. The Array will not start until the above issue is resolved.'; + if (array.value?.state === "Stopped") { + return "Stopped. The Array will not start until the above issue is resolved."; } - return 'Started. If stopped, the Array will not restart until the above issue is resolved.'; + return "Started. If stopped, the Array will not restart until the above issue is resolved."; } return array.value?.state; }); - const avatar = ref(''); // @todo potentially move to a user store - const caseModel = ref(''); + const avatar = ref(""); // @todo potentially move to a user store + const caseModel = ref(""); const cloud = ref(); const config = ref(); - const connectPluginInstalled = ref(''); - const connectPluginVersion = ref(''); - const csrf = ref(''); // required to make requests to Unraid webgui + const connectPluginInstalled = ref(""); + const connectPluginVersion = ref(""); + const csrf = ref(""); // required to make requests to Unraid webgui const dateTimeFormat = ref(); - const description = ref(''); + const description = ref(""); const deviceCount = ref(0); - const email = ref(''); + const email = ref(""); const expireTime = ref(0); const flashBackupActivated = ref(false); - const flashProduct = ref(''); - const flashVendor = ref(''); - const guid = ref(''); + const flashProduct = ref(""); + const flashVendor = ref(""); + const guid = ref(""); const guidBlacklisted = ref(); const guidRegistered = ref(); const guidReplaceable = ref(); const inIframe = ref(window.self !== window.top); - const keyfile = ref(''); - const lanIp = ref(''); - const license = ref(''); - const locale = ref(''); - const name = ref(''); - const osVersion = ref(''); - const osVersionBranch = ref('stable'); - const rebootType = ref<'thirdPartyDriversDownloading' | 'downgrade' | 'update' | ''>(''); + const keyfile = ref(""); + const lanIp = ref(""); + const license = ref(""); + const locale = ref(""); + const name = ref(""); + const osVersion = ref(""); + const osVersionBranch = ref("stable"); + const rebootType = ref< + "thirdPartyDriversDownloading" | "downgrade" | "update" | "" + >(""); const rebootVersion = ref(); const registered = ref(); const regDevs = ref(0); // use computedRegDevs to ensure it includes Basic, Plus, and Pro @@ -113,57 +121,74 @@ export const useServerStore = defineStore('server', () => { } switch (regTy.value) { - case 'Starter': - case 'Basic': + case "Starter": + case "Basic": return 6; - case 'Plus': + case "Plus": return 12; - case 'Unleashed': - case 'Lifetime': - case 'Pro': - case 'Trial': + case "Unleashed": + case "Lifetime": + case "Pro": + case "Trial": return -1; // unlimited default: return 0; } }); const regGen = ref(0); - const regGuid = ref(''); + const regGuid = ref(""); const regTm = ref(0); - const regTo = ref(''); - const regTy = ref(''); + const regTo = ref(""); + const regTy = ref(""); const regExp = ref(0); - const parsedRegExp = computed(() => regExp.value ? dayjs(regExp.value).format('YYYY-MM-DD') : null); + const parsedRegExp = computed(() => + regExp.value ? dayjs(regExp.value).format("YYYY-MM-DD") : null + ); const regUpdatesExpired = computed(() => { - if (!regExp.value) { return false; } + if (!regExp.value) { + return false; + } const today = dayjs(); const parsedUpdateExpirationDate = dayjs(regExp.value); - return today.isAfter(parsedUpdateExpirationDate, 'day'); + return today.isAfter(parsedUpdateExpirationDate, "day"); }); - const site = ref(''); + const site = ref(""); const state = ref(); const theme = ref(); watch(theme, (newVal) => { - if (newVal) { themeStore.setTheme(newVal); } + if (newVal) { + themeStore.setTheme(newVal); + } }); const updateOsResponse = ref(); const updateOsIgnoredReleases = ref([]); const updateOsNotificationsEnabled = ref(false); const uptime = ref(0); - const username = ref(''); // @todo potentially move to a user store - const wanFQDN = ref(''); + const username = ref(""); // @todo potentially move to a user store + const wanFQDN = ref(""); const combinedKnownOrigins = ref([]); + const apiServerStateRefresh = + ref< + ( + variables?: Record | undefined + ) => Promise> | undefined + >(); - const apiServerStateRefresh = ref<(variables?: Record | undefined) => Promise> | undefined>(); /** * Getters */ - const isRemoteAccess = computed(() => wanFQDN.value || (site.value && site.value.includes('www.') && site.value.includes('unraid.net'))); + const isRemoteAccess = computed( + () => + wanFQDN.value || + (site.value && + site.value.includes("www.") && + site.value.includes("unraid.net")) + ); /** * @todo configure */ - const pluginOutdated = computed(():boolean => { + const pluginOutdated = computed((): boolean => { return false; }); @@ -172,7 +197,7 @@ export const useServerStore = defineStore('server', () => { return !hasPrerelease; }); // used to determine if we should look for stable or next releases - const server = computed(():Server => { + const server = computed((): Server => { return { apiKey: apiKey.value, apiVersion: apiVersion.value, @@ -212,81 +237,88 @@ export const useServerStore = defineStore('server', () => { }; }); - const serverPurchasePayload = computed((): ServerPurchaseCallbackSendPayload => { - /** @todo refactor out. Just parse state on craft site to determine */ - let keyTypeForPurchase: ServerKeyTypeForPurchase = 'Trial'; - switch (state.value) { - case 'BASIC': - keyTypeForPurchase = 'Basic'; - break; - case 'PLUS': - keyTypeForPurchase = 'Plus'; - break; - case 'PRO': - keyTypeForPurchase = 'Pro'; - break; - case 'STARTER': - keyTypeForPurchase = 'Starter'; - break; - case 'UNLEASHED': - keyTypeForPurchase = 'Unleashed'; - break; + const serverPurchasePayload = computed( + (): ServerPurchaseCallbackSendPayload => { + /** @todo refactor out. Just parse state on craft site to determine */ + let keyTypeForPurchase: ServerKeyTypeForPurchase = "Trial"; + switch (state.value) { + case "BASIC": + keyTypeForPurchase = "Basic"; + break; + case "PLUS": + keyTypeForPurchase = "Plus"; + break; + case "PRO": + keyTypeForPurchase = "Pro"; + break; + case "STARTER": + keyTypeForPurchase = "Starter"; + break; + case "UNLEASHED": + keyTypeForPurchase = "Unleashed"; + break; + } + const server = { + apiVersion: apiVersion.value, + connectPluginVersion: connectPluginVersion.value, + deviceCount: deviceCount.value, + email: email.value, + guid: guid.value, + inIframe: inIframe.value, + keyTypeForPurchase, + locale: locale.value, + osVersion: osVersion.value, + osVersionBranch: osVersionBranch.value, + registered: registered.value ?? false, + regExp: regExp.value, + regTy: regTy.value, + regUpdatesExpired: regUpdatesExpired.value, + state: state.value, + site: site.value, + }; + return server; } - const server = { - apiVersion: apiVersion.value, - connectPluginVersion: connectPluginVersion.value, - deviceCount: deviceCount.value, - email: email.value, - guid: guid.value, - inIframe: inIframe.value, - keyTypeForPurchase, - locale: locale.value, - osVersion: osVersion.value, - osVersionBranch: osVersionBranch.value, - registered: registered.value ?? false, - regExp: regExp.value, - regTy: regTy.value, - regUpdatesExpired: regUpdatesExpired.value, - state: state.value, - site: site.value, - }; - return server; - }); + ); - const serverAccountPayload = computed((): ServerAccountCallbackSendPayload => { - return { - apiVersion: apiVersion.value, - caseModel: caseModel.value, - connectPluginVersion: connectPluginVersion.value, - deviceCount: deviceCount.value, - description: description.value, - expireTime: expireTime.value, - flashBackupActivated: flashBackupActivated.value, - flashProduct: flashProduct.value, - flashVendor: flashVendor.value, - guid: guid.value, - inIframe: inIframe.value, - keyfile: keyfile.value, - lanIp: lanIp.value, - name: name.value, - osVersion: osVersion.value, - osVersionBranch: osVersionBranch.value, - rebootType: rebootType.value, - rebootVersion: rebootVersion.value, - registered: registered.value ?? false, - regGuid: regGuid.value, - regExp: regExp.value, - regTy: regTy.value, - regUpdatesExpired: regUpdatesExpired.value, - site: site.value, - state: state.value, - wanFQDN: wanFQDN.value, - }; - }); + const serverAccountPayload = computed( + (): ServerAccountCallbackSendPayload => { + return { + apiVersion: apiVersion.value, + caseModel: caseModel.value, + connectPluginVersion: connectPluginVersion.value, + deviceCount: deviceCount.value, + description: description.value, + expireTime: expireTime.value, + flashBackupActivated: flashBackupActivated.value, + flashProduct: flashProduct.value, + flashVendor: flashVendor.value, + guid: guid.value, + inIframe: inIframe.value, + keyfile: keyfile.value, + lanIp: lanIp.value, + name: name.value, + osVersion: osVersion.value, + osVersionBranch: osVersionBranch.value, + rebootType: rebootType.value, + rebootVersion: rebootVersion.value, + registered: registered.value ?? false, + regGuid: regGuid.value, + regExp: regExp.value, + regTy: regTy.value, + regUpdatesExpired: regUpdatesExpired.value, + site: site.value, + state: state.value, + wanFQDN: wanFQDN.value, + }; + } + ); const serverDebugPayload = computed((): Server => { const payload = { - apiKey: apiKey.value && typeof apiKey.value === 'string' ? `${apiKey.value.substring(0, 6)}__[REDACTED]` : '', // so we don't send full api key in email + apiKey: + apiKey.value && typeof apiKey.value === "string" + ? `${apiKey.value.substring(0, 6)}__[REDACTED]` + : "", // so we don't send full api key in email apiVersion: apiVersion.value, avatar: avatar.value, connectPluginInstalled: connectPluginInstalled.value, @@ -317,89 +349,113 @@ export const useServerStore = defineStore('server', () => { wanFQDN: wanFQDN.value, }; // remove any empty values from object - return Object.fromEntries(Object.entries(payload).filter(([_, v]) => v !== null && v !== undefined && v !== '')); + return Object.fromEntries( + Object.entries(payload).filter( + ([_, v]) => v !== null && v !== undefined && v !== "" + ) + ); }); const serverActionsDisable = computed(() => { - const disable = !!(connectPluginInstalled.value && (unraidApiStore.unraidApiStatus !== 'online' || unraidApiStore.prioritizeCorsError)); + const disable = !!( + connectPluginInstalled.value && + (unraidApiStore.unraidApiStatus !== "online" || + unraidApiStore.prioritizeCorsError) + ); return { disable, - title: disable ? 'Requires the local unraid-api to be running successfully' : '', + title: disable + ? "Requires the local unraid-api to be running successfully" + : "", }; }); const purchaseAction = computed((): ServerStateDataAction => { return { - click: () => { purchaseStore.purchase(); }, + click: () => { + purchaseStore.purchase(); + }, disabled: serverActionsDisable.value.disable, external: true, icon: KeyIcon, - name: 'purchase', - text: 'Purchase Key', + name: "purchase", + text: "Purchase Key", title: serverActionsDisable.value.title, }; }); const upgradeAction = computed((): ServerStateDataAction => { return { - click: () => { purchaseStore.upgrade(); }, + click: () => { + purchaseStore.upgrade(); + }, disabled: serverActionsDisable.value.disable, external: true, icon: KeyIcon, - name: 'upgrade', - text: 'Upgrade Key', + name: "upgrade", + text: "Upgrade Key", title: serverActionsDisable.value.title, }; }); const recoverAction = computed((): ServerStateDataAction => { return { - click: () => { accountStore.recover(); }, + click: () => { + accountStore.recover(); + }, disabled: serverActionsDisable.value.disable, external: true, icon: KeyIcon, - name: 'recover', - text: 'Recover Key', + name: "recover", + text: "Recover Key", title: serverActionsDisable.value.title, }; }); const redeemAction = computed((): ServerStateDataAction => { return { - click: () => { purchaseStore.redeem(); }, + click: () => { + purchaseStore.redeem(); + }, disabled: serverActionsDisable.value.disable, external: true, icon: KeyIcon, - name: 'redeem', - text: 'Redeem Activation Code', + name: "redeem", + text: "Redeem Activation Code", title: serverActionsDisable.value.title, }; }); const renewAction = computed((): ServerStateDataAction => { return { - click: () => { purchaseStore.renew(); }, + click: () => { + purchaseStore.renew(); + }, disabled: serverActionsDisable.value.disable, external: true, icon: KeyIcon, - name: 'renew', - text: 'Extend License to Enable OS Updates', + name: "renew", + text: "Extend License to Enable OS Updates", title: serverActionsDisable.value.title, }; }); const replaceAction = computed((): ServerStateDataAction => { return { - click: () => { accountStore.replace(); }, + click: () => { + accountStore.replace(); + }, external: true, icon: KeyIcon, - name: 'replace', - text: 'Replace Key', + name: "replace", + text: "Replace Key", }; }); const signInAction = computed((): ServerStateDataAction => { return { - click: () => { accountStore.signIn(); }, + click: () => { + accountStore.signIn(); + }, disabled: serverActionsDisable.value.disable, external: true, icon: GlobeAltIcon, - name: 'signIn', - text: 'Sign In with Unraid.net Account', + name: "signIn", + text: "Sign In with Unraid.net Account", title: serverActionsDisable.value.title, }; }); @@ -407,263 +463,340 @@ export const useServerStore = defineStore('server', () => { * The Sign Out action is a computed property because it depends on the state of the keyfile & unraid-api being online */ const signOutAction = computed((): ServerStateDataAction => { - const disabled: boolean = !keyfile.value || serverActionsDisable.value.disable; - let title = ''; + const disabled: boolean = + !keyfile.value || serverActionsDisable.value.disable; + let title = ""; if (!keyfile.value) { - title = 'Sign Out requires a keyfile'; + title = "Sign Out requires a keyfile"; } if (serverActionsDisable.value.disable) { title = serverActionsDisable.value.title; } return { - click: () => { accountStore.signOut(); }, + click: () => { + accountStore.signOut(); + }, disabled, external: true, icon: ArrowRightOnRectangleIcon, - name: 'signOut', - text: 'Sign Out of Unraid.net', + name: "signOut", + text: "Sign Out of Unraid.net", title, }; }); const trialExtendAction = computed((): ServerStateDataAction => { return { - click: () => { accountStore.trialExtend(); }, + click: () => { + accountStore.trialExtend(); + }, disabled: serverActionsDisable.value.disable, external: true, icon: KeyIcon, - name: 'trialExtend', - text: 'Extend Trial', + name: "trialExtend", + text: "Extend Trial", title: serverActionsDisable.value.title, }; }); const trialStartAction = computed((): ServerStateDataAction => { return { - click: () => { accountStore.trialStart(); }, + click: () => { + accountStore.trialStart(); + }, disabled: serverActionsDisable.value.disable, external: true, icon: KeyIcon, - name: 'trialStart', - text: 'Start Free 30 Day Trial', + name: "trialStart", + text: "Start Free 30 Day Trial", title: serverActionsDisable.value.title, }; }); - let messageEGUID = ''; - const stateData = computed(():ServerStateData => { + let messageEGUID = ""; + const stateData = computed((): ServerStateData => { switch (state.value) { - case 'ENOKEYFILE': + case "ENOKEYFILE": return { actions: [ - ...(!registered.value && connectPluginInstalled.value ? [signInAction.value] : []), - ...([trialStartAction.value, purchaseAction.value, redeemAction.value, recoverAction.value]), - ...(registered.value && connectPluginInstalled.value ? [signOutAction.value] : []), + ...(!registered.value && connectPluginInstalled.value + ? [signInAction.value] + : []), + ...[ + trialStartAction.value, + purchaseAction.value, + redeemAction.value, + recoverAction.value, + ], + ...(registered.value && connectPluginInstalled.value + ? [signOutAction.value] + : []), ], - humanReadable: 'No Keyfile', - heading: 'Let\'s Unleash Your Hardware', - message: '

Choose an option below, then use our Getting Started Guide to configure your array in less than 15 minutes.

', + humanReadable: "No Keyfile", + heading: "Let's Unleash Your Hardware", + message: + '

Choose an option below, then use our Getting Started Guide to configure your array in less than 15 minutes.

', }; - case 'TRIAL': + case "TRIAL": return { actions: [ - ...(!registered.value && connectPluginInstalled.value ? [signInAction.value] : []), - ...([purchaseAction.value, redeemAction.value]), - ...(registered.value && connectPluginInstalled.value ? [signOutAction.value] : []), + ...(!registered.value && connectPluginInstalled.value + ? [signInAction.value] + : []), + ...[purchaseAction.value, redeemAction.value], + ...(registered.value && connectPluginInstalled.value + ? [signOutAction.value] + : []), ], - humanReadable: 'Trial', - heading: 'Thank you for choosing Unraid OS!', - message: '

Your Trial key includes all the functionality and device support of an Unleashed key.

After your Trial has reached expiration, your server still functions normally until the next time you Stop the array or reboot your server.

At that point you may either purchase a license key or request a Trial extension.

', + humanReadable: "Trial", + heading: "Thank you for choosing Unraid OS!", + message: + "

Your Trial key includes all the functionality and device support of an Unleashed key.

After your Trial has reached expiration, your server still functions normally until the next time you Stop the array or reboot your server.

At that point you may either purchase a license key or request a Trial extension.

", }; - case 'EEXPIRED': + case "EEXPIRED": return { actions: [ - ...(!registered.value && connectPluginInstalled.value ? [signInAction.value] : []), - ...([purchaseAction.value, redeemAction.value]), + ...(!registered.value && connectPluginInstalled.value + ? [signInAction.value] + : []), + ...[purchaseAction.value, redeemAction.value], ...(trialExtensionEligible.value ? [trialExtendAction.value] : []), - ...(registered.value && connectPluginInstalled.value ? [signOutAction.value] : []), + ...(registered.value && connectPluginInstalled.value + ? [signOutAction.value] + : []), ], error: true, - humanReadable: 'Trial Expired', - heading: 'Your Trial has expired', + humanReadable: "Trial Expired", + heading: "Your Trial has expired", message: trialExtensionEligible.value - ? '

To continue using Unraid OS you may purchase a license key. Alternately, you may request a Trial extension.

' - : '

You have used all your Trial extensions. To continue using Unraid OS you may purchase a license key.

', + ? "

To continue using Unraid OS you may purchase a license key. Alternately, you may request a Trial extension.

" + : "

You have used all your Trial extensions. To continue using Unraid OS you may purchase a license key.

", }; - case 'BASIC': - case 'STARTER': + case "BASIC": + case "STARTER": return { actions: [ - ...(!registered.value && connectPluginInstalled.value ? [signInAction.value] : []), + ...(!registered.value && connectPluginInstalled.value + ? [signInAction.value] + : []), ...(regUpdatesExpired.value ? [renewAction.value] : []), - ...([upgradeAction.value]), - ...(registered.value && connectPluginInstalled.value ? [signOutAction.value] : []), + ...[upgradeAction.value], + ...(registered.value && connectPluginInstalled.value + ? [signOutAction.value] + : []), ], - humanReadable: state.value === 'BASIC' ? 'Basic' : 'Starter', - heading: 'Thank you for choosing Unraid OS!', - message: !registered.value && connectPluginInstalled.value - ? '

Register for Connect by signing in to your Unraid.net account

' - : guidRegistered.value - ? '

To support more storage devices as your server grows, click Upgrade Key.

' - : '', + humanReadable: state.value === "BASIC" ? "Basic" : "Starter", + heading: "Thank you for choosing Unraid OS!", + message: + !registered.value && connectPluginInstalled.value + ? "

Register for Connect by signing in to your Unraid.net account

" + : guidRegistered.value + ? "

To support more storage devices as your server grows, click Upgrade Key.

" + : "", }; - case 'PLUS': + case "PLUS": return { actions: [ - ...(!registered.value && connectPluginInstalled.value ? [signInAction.value] : []), - ...([upgradeAction.value]), - ...(registered.value && connectPluginInstalled.value ? [signOutAction.value] : []), + ...(!registered.value && connectPluginInstalled.value + ? [signInAction.value] + : []), + ...[upgradeAction.value], + ...(registered.value && connectPluginInstalled.value + ? [signOutAction.value] + : []), ], - humanReadable: 'Plus', - heading: 'Thank you for choosing Unraid OS!', - message: !registered.value && connectPluginInstalled.value - ? '

Register for Connect by signing in to your Unraid.net account

' - : guidRegistered.value - ? '

To support more storage devices as your server grows, click Upgrade Key.

' - : '', + humanReadable: "Plus", + heading: "Thank you for choosing Unraid OS!", + message: + !registered.value && connectPluginInstalled.value + ? "

Register for Connect by signing in to your Unraid.net account

" + : guidRegistered.value + ? "

To support more storage devices as your server grows, click Upgrade Key.

" + : "", }; - case 'PRO': - case 'LIFETIME': - case 'UNLEASHED': + case "PRO": + case "LIFETIME": + case "UNLEASHED": return { actions: [ - ...(!registered.value && connectPluginInstalled.value ? [signInAction.value] : []), + ...(!registered.value && connectPluginInstalled.value + ? [signInAction.value] + : []), ...(regUpdatesExpired.value ? [renewAction.value] : []), - ...(state.value === 'UNLEASHED' ? [upgradeAction.value] : []), - ...(registered.value && connectPluginInstalled.value ? [signOutAction.value] : []), + ...(state.value === "UNLEASHED" ? [upgradeAction.value] : []), + ...(registered.value && connectPluginInstalled.value + ? [signOutAction.value] + : []), ], - humanReadable: state.value === 'PRO' - ? 'Pro' - : (state.value === 'LIFETIME' ? 'Lifetime' : 'Unleashed'), - heading: 'Thank you for choosing Unraid OS!', - message: !registered.value && connectPluginInstalled.value - ? '

Register for Connect by signing in to your Unraid.net account

' - : '', + humanReadable: + state.value === "PRO" + ? "Pro" + : state.value === "LIFETIME" + ? "Lifetime" + : "Unleashed", + heading: "Thank you for choosing Unraid OS!", + message: + !registered.value && connectPluginInstalled.value + ? "

Register for Connect by signing in to your Unraid.net account

" + : "", }; - case 'EGUID': + case "EGUID": if (guidReplaceable.value) { - messageEGUID = '

Your Unraid registration key is ineligible for replacement as it has been replaced within the last 12 months.

'; + messageEGUID = + "

Your Unraid registration key is ineligible for replacement as it has been replaced within the last 12 months.

"; } else if (guidReplaceable.value === false && guidBlacklisted.value) { - messageEGUID = '

The license key file does not correspond to the USB Flash boot device. Please copy the correct key file to the /config directory on your USB Flash boot device or choose Purchase Key.

Your Unraid registration key is ineligible for replacement as it is blacklisted.

'; + messageEGUID = + "

The license key file does not correspond to the USB Flash boot device. Please copy the correct key file to the /config directory on your USB Flash boot device or choose Purchase Key.

Your Unraid registration key is ineligible for replacement as it is blacklisted.

"; } else if (guidReplaceable.value === false && !guidBlacklisted.value) { - messageEGUID = '

The license key file does not correspond to the USB Flash boot device. Please copy the correct key file to the /config directory on your USB Flash boot device or choose Purchase Key.

Your Unraid registration key is ineligible for replacement as it has been replaced within the last 12 months.

'; - } else { // basically guidReplaceable.value === null - messageEGUID = '

The license key file does not correspond to the USB Flash boot device. Please copy the correct key file to the /config directory on your USB Flash boot device.

You may also attempt to Purchase or Replace your key.

'; + messageEGUID = + "

The license key file does not correspond to the USB Flash boot device. Please copy the correct key file to the /config directory on your USB Flash boot device or choose Purchase Key.

Your Unraid registration key is ineligible for replacement as it has been replaced within the last 12 months.

"; + } else { + // basically guidReplaceable.value === null + messageEGUID = + "

The license key file does not correspond to the USB Flash boot device. Please copy the correct key file to the /config directory on your USB Flash boot device.

You may also attempt to Purchase or Replace your key.

"; } return { actions: [ - ...(!registered.value && connectPluginInstalled.value ? [signInAction.value] : []), - ...([replaceAction.value, purchaseAction.value, redeemAction.value]), - ...(registered.value && connectPluginInstalled.value ? [signOutAction.value] : []), + ...(!registered.value && connectPluginInstalled.value + ? [signInAction.value] + : []), + ...[replaceAction.value, purchaseAction.value, redeemAction.value], + ...(registered.value && connectPluginInstalled.value + ? [signOutAction.value] + : []), ], error: true, - humanReadable: 'Flash GUID Error', - heading: 'Registration key / USB Flash GUID mismatch', + humanReadable: "Flash GUID Error", + heading: "Registration key / USB Flash GUID mismatch", message: messageEGUID, }; - case 'EGUID1': + case "EGUID1": return { actions: [ - ...(!registered.value && connectPluginInstalled.value ? [signInAction.value] : []), - ...([purchaseAction.value, redeemAction.value]), - ...(registered.value && connectPluginInstalled.value ? [signOutAction.value] : []), + ...(!registered.value && connectPluginInstalled.value + ? [signInAction.value] + : []), + ...[purchaseAction.value, redeemAction.value], + ...(registered.value && connectPluginInstalled.value + ? [signOutAction.value] + : []), ], error: true, - humanReadable: 'Multiple License Keys Present', - heading: 'Multiple License Keys Present', - message: '

There are multiple license key files present on your USB flash device and none of them correspond to the USB Flash boot device. Please remove all key files, except the one you want to replace, from the /config directory on your USB Flash boot device.

Alternately you may purchase a license key for this USB flash device.

If you want to replace one of your license keys with a new key bound to this USB Flash device, please first remove all other key files first.

', + humanReadable: "Multiple License Keys Present", + heading: "Multiple License Keys Present", + message: + "

There are multiple license key files present on your USB flash device and none of them correspond to the USB Flash boot device. Please remove all key files, except the one you want to replace, from the /config directory on your USB Flash boot device.

Alternately you may purchase a license key for this USB flash device.

If you want to replace one of your license keys with a new key bound to this USB Flash device, please first remove all other key files first.

", // signInToFix: true, // @todo is this needed? }; - case 'ENOKEYFILE2': + case "ENOKEYFILE2": return { actions: [ - ...(!registered.value && connectPluginInstalled.value ? [signInAction.value] : []), - ...([recoverAction.value, purchaseAction.value, redeemAction.value]), + ...(!registered.value && connectPluginInstalled.value + ? [signInAction.value] + : []), + ...[recoverAction.value, purchaseAction.value, redeemAction.value], ...(registered.value ? [signOutAction.value] : []), ], error: true, - humanReadable: 'Missing key file', - heading: 'Missing key file', + humanReadable: "Missing key file", + heading: "Missing key file", message: connectPluginInstalled.value - ? '

Your license key file is corrupted or missing. The key file should be located in the /config directory on your USB Flash boot device.

You may attempt to recover your key with your Unraid.net account.

If this was an expired Trial installation, you may purchase a license key.

' - : '

Your license key file is corrupted or missing. The key file should be located in the /config directory on your USB Flash boot device.

If you do not have a backup copy of your license key file you may attempt to recover your key.

If this was an expired Trial installation, you may purchase a license key.

', + ? "

Your license key file is corrupted or missing. The key file should be located in the /config directory on your USB Flash boot device.

You may attempt to recover your key with your Unraid.net account.

If this was an expired Trial installation, you may purchase a license key.

" + : "

Your license key file is corrupted or missing. The key file should be located in the /config directory on your USB Flash boot device.

If you do not have a backup copy of your license key file you may attempt to recover your key.

If this was an expired Trial installation, you may purchase a license key.

", }; - case 'ETRIAL': + case "ETRIAL": return { actions: [ - ...(!registered.value && connectPluginInstalled.value ? [signInAction.value] : []), - ...([purchaseAction.value, redeemAction.value]), - ...(registered.value && connectPluginInstalled.value ? [signOutAction.value] : []), + ...(!registered.value && connectPluginInstalled.value + ? [signInAction.value] + : []), + ...[purchaseAction.value, redeemAction.value], + ...(registered.value && connectPluginInstalled.value + ? [signOutAction.value] + : []), ], error: true, - humanReadable: 'Invalid installation', - heading: 'Invalid installation', - message: '

It is not possible to use a Trial key with an existing Unraid OS installation.

You may purchase a license key corresponding to this USB Flash device to continue using this installation.

', + humanReadable: "Invalid installation", + heading: "Invalid installation", + message: + "

It is not possible to use a Trial key with an existing Unraid OS installation.

You may purchase a license key corresponding to this USB Flash device to continue using this installation.

", }; - case 'ENOKEYFILE1': + case "ENOKEYFILE1": return { actions: [ - ...(!registered.value && connectPluginInstalled.value ? [signInAction.value] : []), - ...([purchaseAction.value, redeemAction.value]), - ...(registered.value && connectPluginInstalled.value ? [signOutAction.value] : []), + ...(!registered.value && connectPluginInstalled.value + ? [signInAction.value] + : []), + ...[purchaseAction.value, redeemAction.value], + ...(registered.value && connectPluginInstalled.value + ? [signOutAction.value] + : []), ], error: true, - humanReadable: 'No Keyfile', - heading: 'No USB flash configuration data', - message: '

There is a problem with your USB Flash device

', + humanReadable: "No Keyfile", + heading: "No USB flash configuration data", + message: "

There is a problem with your USB Flash device

", }; - case 'ENOFLASH': - case 'ENOFLASH1': - case 'ENOFLASH2': - case 'ENOFLASH3': - case 'ENOFLASH4': - case 'ENOFLASH5': - case 'ENOFLASH6': - case 'ENOFLASH7': + case "ENOFLASH": + case "ENOFLASH1": + case "ENOFLASH2": + case "ENOFLASH3": + case "ENOFLASH4": + case "ENOFLASH5": + case "ENOFLASH6": + case "ENOFLASH7": return { error: true, - humanReadable: 'No Flash', - heading: 'Cannot access your USB Flash boot device', - message: '

There is a physical problem accessing your USB Flash boot device

', + humanReadable: "No Flash", + heading: "Cannot access your USB Flash boot device", + message: + "

There is a physical problem accessing your USB Flash boot device

", }; - case 'EBLACKLISTED': + case "EBLACKLISTED": return { error: true, - humanReadable: 'BLACKLISTED', - heading: 'Blacklisted USB Flash GUID', - message: '

This USB Flash boot device has been blacklisted. This can occur as a result of transferring your license key to a replacement USB Flash device, and you are currently booted from your old USB Flash device.

A USB Flash device may also be blacklisted if we discover the serial number is not unique – this is common with USB card readers.

', + humanReadable: "BLACKLISTED", + heading: "Blacklisted USB Flash GUID", + message: + "

This USB Flash boot device has been blacklisted. This can occur as a result of transferring your license key to a replacement USB Flash device, and you are currently booted from your old USB Flash device.

A USB Flash device may also be blacklisted if we discover the serial number is not unique – this is common with USB card readers.

", }; - case 'EBLACKLISTED1': + case "EBLACKLISTED1": return { error: true, - humanReadable: 'BLACKLISTED', - heading: 'USB Flash device error', - message: '

This USB Flash device has an invalid GUID. Please try a different USB Flash device

', + humanReadable: "BLACKLISTED", + heading: "USB Flash device error", + message: + "

This USB Flash device has an invalid GUID. Please try a different USB Flash device

", }; - case 'EBLACKLISTED2': + case "EBLACKLISTED2": return { error: true, - humanReadable: 'BLACKLISTED', - heading: 'USB Flash has no serial number', - message: '

This USB Flash boot device has been blacklisted. This can occur as a result of transferring your license key to a replacement USB Flash device, and you are currently booted from your old USB Flash device.

A USB Flash device may also be blacklisted if we discover the serial number is not unique – this is common with USB card readers.

', + humanReadable: "BLACKLISTED", + heading: "USB Flash has no serial number", + message: + "

This USB Flash boot device has been blacklisted. This can occur as a result of transferring your license key to a replacement USB Flash device, and you are currently booted from your old USB Flash device.

A USB Flash device may also be blacklisted if we discover the serial number is not unique – this is common with USB card readers.

", }; - case 'ENOCONN': + case "ENOCONN": return { error: true, - humanReadable: 'Trial Requires Internet Connection', - heading: 'Cannot validate Unraid Trial key', - message: '

Your Trial key requires an internet connection.

Please check Settings > Network

', + humanReadable: "Trial Requires Internet Connection", + heading: "Cannot validate Unraid Trial key", + message: + '

Your Trial key requires an internet connection.

Please check Settings > Network

', }; default: return { error: true, - humanReadable: 'Stale', - heading: 'Stale Server', - message: '

Please refresh the page to ensure you load your latest configuration

', + humanReadable: "Stale", + heading: "Stale Server", + message: + "

Please refresh the page to ensure you load your latest configuration

", }; } }); const stateDataError = computed((): Error | undefined => { - if (!stateData.value?.error) { return undefined; } + if (!stateData.value?.error) { + return undefined; + } return { actions: [ { @@ -674,34 +807,48 @@ export const useServerStore = defineStore('server', () => { }); }, icon: QuestionMarkCircleIcon, - text: 'Contact Support', + text: "Contact Support", }, ], debugServer: serverDebugPayload.value, - heading: stateData.value?.heading ?? '', - level: 'error', - message: stateData.value?.message ?? '', + heading: stateData.value?.heading ?? "", + level: "error", + message: stateData.value?.message ?? "", ref: `stateDataError__${state.value}`, - type: 'serverState', + type: "serverState", }; }); watch(stateDataError, (newVal, oldVal) => { - if (oldVal && oldVal.ref) { errorsStore.removeErrorByRef(oldVal.ref); } - if (newVal) { errorsStore.setError(newVal); } + if (oldVal && oldVal.ref) { + errorsStore.removeErrorByRef(oldVal.ref); + } + if (newVal) { + errorsStore.setError(newVal); + } }); - const authActionsNames = ['signIn', 'signOut']; + const authActionsNames = ["signIn", "signOut"]; // Extract sign in / out from actions so we can display seperately as needed const authAction = computed((): ServerStateDataAction | undefined => { - if (!stateData.value.actions) { return; } - return stateData.value.actions.find(action => authActionsNames.includes(action.name)); + if (!stateData.value.actions) { + return; + } + return stateData.value.actions.find((action) => + authActionsNames.includes(action.name) + ); }); // Remove sign in / out from actions so we can display them separately const keyActions = computed((): ServerStateDataAction[] | undefined => { - if (!stateData.value.actions) { return; } - return stateData.value.actions.filter(action => !authActionsNames.includes(action.name)); + if (!stateData.value.actions) { + return; + } + return stateData.value.actions.filter( + (action) => !authActionsNames.includes(action.name) + ); }); - const trialExtensionEligible = computed(() => !regGen.value || regGen.value < 2); + const trialExtensionEligible = computed( + () => !regGen.value || regGen.value < 2 + ); const serverConfigError = computed((): Error | undefined => { if (!config.value?.valid && config.value?.error) { @@ -715,125 +862,155 @@ export const useServerStore = defineStore('server', () => { // type: 'server', // }; //@ts-expect-error - causing a build-breaking type error, but seems plausible, so i kept it around - pujitm - case 'INELIGIBLE': + case "INELIGIBLE": return { - heading: 'Ineligible for OS Version', - level: 'error', - message: 'Your License Key does not support this OS Version. OS build date greater than key expiration. Please consider extending your registration key.', - actions: [{ - href: WEBGUI_TOOLS_REGISTRATION.toString(), - icon: CogIcon, - text: 'Learn More at Tools > Registration', - }], - ref: 'configError', - type: 'server', + heading: "Ineligible for OS Version", + level: "error", + message: + "Your License Key does not support this OS Version. OS build date greater than key expiration. Please consider extending your registration key.", + actions: [ + { + href: WEBGUI_TOOLS_REGISTRATION.toString(), + icon: CogIcon, + text: "Learn More at Tools > Registration", + }, + ], + ref: "configError", + type: "server", }; - case 'INVALID': + case "INVALID": return { - heading: 'Too Many Devices', - level: 'error', - message: 'You have exceeded the number of devices allowed for your license. Please remove a device to start the array, or upgrade your key to support more devices.', - ref: 'configError', - type: 'server', + heading: "Too Many Devices", + level: "error", + message: + "You have exceeded the number of devices allowed for your license. Please remove a device to start the array, or upgrade your key to support more devices.", + ref: "configError", + type: "server", }; - case 'NO_KEY_SERVER': + case "NO_KEY_SERVER": return { - heading: 'Check Network Connection', - level: 'error', - message: 'Unable to validate your trial key. Please check your network connection.', - ref: 'configError', - type: 'server', + heading: "Check Network Connection", + level: "error", + message: + "Unable to validate your trial key. Please check your network connection.", + ref: "configError", + type: "server", }; - case 'WITHDRAWN': + case "WITHDRAWN": return { - heading: 'OS Version Withdrawn', - level: 'error', - message: 'This OS release should not be run. OS Update Required.', - actions: [{ - href: WEBGUI_TOOLS_UPDATE.toString(), - icon: ArrowPathIcon, - text: 'Check for Update', - }], - ref: 'configError', - type: 'server', + heading: "OS Version Withdrawn", + level: "error", + message: "This OS release should not be run. OS Update Required.", + actions: [ + { + href: WEBGUI_TOOLS_UPDATE.toString(), + icon: ArrowPathIcon, + text: "Check for Update", + }, + ], + ref: "configError", + type: "server", }; } return undefined; } }); watch(serverConfigError, (newVal, oldVal) => { - if (oldVal && oldVal.ref) { errorsStore.removeErrorByRef(oldVal.ref); } - if (newVal) { errorsStore.setError(newVal); } + if (oldVal && oldVal.ref) { + errorsStore.removeErrorByRef(oldVal.ref); + } + if (newVal) { + errorsStore.setError(newVal); + } }); const tooManyDevices = computed((): boolean => { - return ((deviceCount.value !== 0 && computedRegDevs.value > 0 && deviceCount.value > computedRegDevs.value) || - (!config.value?.valid && config.value?.error === 'INVALID')); + return ( + (deviceCount.value !== 0 && + computedRegDevs.value > 0 && + deviceCount.value > computedRegDevs.value) || + (!config.value?.valid && config.value?.error === "INVALID") + ); }); const pluginInstallFailed = computed((): Error | undefined => { - if (connectPluginInstalled.value && connectPluginInstalled.value.includes('_installFailed')) { + if ( + connectPluginInstalled.value && + connectPluginInstalled.value.includes("_installFailed") + ) { return { actions: [ { external: true, - href: 'https://forums.unraid.net/topic/112073-my-servers-releases/#comment-1154449', + href: "https://forums.unraid.net/topic/112073-my-servers-releases/#comment-1154449", icon: InformationCircleIcon, - text: 'Learn More', + text: "Learn More", }, ], - heading: 'Unraid Connect Install Failed', - level: 'error', - message: 'Rebooting will likely solve this.', - ref: 'pluginInstallFailed', - type: 'server', + heading: "Unraid Connect Install Failed", + level: "error", + message: "Rebooting will likely solve this.", + ref: "pluginInstallFailed", + type: "server", }; } return undefined; }); watch(pluginInstallFailed, (newVal, oldVal) => { - if (oldVal && oldVal.ref) { errorsStore.removeErrorByRef(oldVal.ref); } - if (newVal) { errorsStore.setError(newVal); } + if (oldVal && oldVal.ref) { + errorsStore.removeErrorByRef(oldVal.ref); + } + if (newVal) { + errorsStore.setError(newVal); + } }); /** * Deprecation warning for [hash].unraid.net SSL certs. Deprecation started 2023-01-01 */ const deprecatedUnraidSSL = ref( - (window.location.hostname.includes('localhost') && window.location.port !== '4321' + window.location.hostname.includes("localhost") && + window.location.port !== "4321" ? { actions: [ { href: WEBGUI_SETTINGS_MANAGMENT_ACCESS.toString(), icon: CogIcon, - text: 'Go to Management Access Now', + text: "Go to Management Access Now", }, { external: true, - href: 'https://unraid.net/blog/ssl-certificate-update', + href: "https://unraid.net/blog/ssl-certificate-update", icon: InformationCircleIcon, - text: 'Learn More', + text: "Learn More", }, ], forumLink: true, - heading: 'SSL certificates for unraid.net deprecated', - level: 'error', - message: 'On January 1st, 2023 SSL certificates for unraid.net were deprecated. You MUST provision a new SSL certificate to use our new myunraid.net domain. You can do this on the Settings > Management Access page.', - ref: 'deprecatedUnraidSSL', - type: 'server', + heading: "SSL certificates for unraid.net deprecated", + level: "error", + message: + "On January 1st, 2023 SSL certificates for unraid.net were deprecated. You MUST provision a new SSL certificate to use our new myunraid.net domain. You can do this on the Settings > Management Access page.", + ref: "deprecatedUnraidSSL", + type: "server", } - : undefined)); + : undefined + ); watch(deprecatedUnraidSSL, (newVal, oldVal) => { - if (oldVal && oldVal.ref) { errorsStore.removeErrorByRef(oldVal.ref); } - if (newVal) { errorsStore.setError(newVal); } + if (oldVal && oldVal.ref) { + errorsStore.removeErrorByRef(oldVal.ref); + } + if (newVal) { + errorsStore.setError(newVal); + } }); const cloudError = computed((): Error | undefined => { // if we're not registered or we're in the process of signing out then the cloud error should be ignored - if (!registered.value || + if ( + !registered.value || !cloud.value?.error || - accountStore.accountActionType === 'signOut' || - accountStore.accountActionType === 'oemSignOut' + accountStore.accountActionType === "signOut" || + accountStore.accountActionType === "oemSignOut" ) { return; } @@ -848,20 +1025,24 @@ export const useServerStore = defineStore('server', () => { }); }, icon: QuestionMarkCircleIcon, - text: 'Contact Support', + text: "Contact Support", }, ], debugServer: serverDebugPayload.value, - heading: 'Unraid Connect Error', - level: 'error', - message: cloud.value?.error ?? '', - ref: 'cloudError', - type: 'unraidApiState', + heading: "Unraid Connect Error", + level: "error", + message: cloud.value?.error ?? "", + ref: "cloudError", + type: "unraidApiState", }; }); watch(cloudError, (newVal, oldVal) => { - if (oldVal && oldVal.ref) { errorsStore.removeErrorByRef(oldVal.ref); } - if (newVal) { errorsStore.setError(newVal); } + if (oldVal && oldVal.ref) { + errorsStore.removeErrorByRef(oldVal.ref); + } + if (newVal) { + errorsStore.setError(newVal); + } }); const serverErrors = computed(() => { @@ -878,52 +1059,142 @@ export const useServerStore = defineStore('server', () => { * Actions */ const setServer = (data: Server) => { - console.debug('[setServer]', data); - if (typeof data?.apiKey !== 'undefined') { apiKey.value = data.apiKey; } - if (typeof data?.array !== 'undefined') { array.value = data.array; } - if (typeof data?.apiVersion !== 'undefined') { apiVersion.value = data.apiVersion; } - if (typeof data?.avatar !== 'undefined') { avatar.value = data.avatar; } - if (typeof data?.caseModel !== 'undefined') { caseModel.value = data.caseModel; } - if (typeof data?.cloud !== 'undefined') { cloud.value = data.cloud; } - if (typeof data?.combinedKnownOrigins !== 'undefined') { combinedKnownOrigins.value = data.combinedKnownOrigins; } - if (typeof data?.config !== 'undefined') { config.value = data.config; } - if (typeof data?.connectPluginInstalled !== 'undefined') { connectPluginInstalled.value = data.connectPluginInstalled; } - if (typeof data?.connectPluginVersion !== 'undefined') { connectPluginVersion.value = data.connectPluginVersion; } - if (typeof data?.csrf !== 'undefined') { csrf.value = data.csrf; } - if (typeof data?.dateTimeFormat !== 'undefined') { dateTimeFormat.value = data.dateTimeFormat; } - if (typeof data?.description !== 'undefined') { description.value = data.description; } - if (typeof data?.deviceCount !== 'undefined') { deviceCount.value = data.deviceCount; } - if (typeof data?.email !== 'undefined') { email.value = data.email; } - if (typeof data?.expireTime !== 'undefined') { expireTime.value = data.expireTime; } - if (typeof data?.flashBackupActivated !== 'undefined') { flashBackupActivated.value = data.flashBackupActivated; } - if (typeof data?.flashProduct !== 'undefined') { flashProduct.value = data.flashProduct; } - if (typeof data?.flashVendor !== 'undefined') { flashVendor.value = data.flashVendor; } - if (typeof data?.guid !== 'undefined') { guid.value = data.guid; } - if (typeof data?.keyfile !== 'undefined') { keyfile.value = data.keyfile; } - if (typeof data?.lanIp !== 'undefined') { lanIp.value = data.lanIp; } - if (typeof data?.license !== 'undefined') { license.value = data.license; } - if (typeof data?.locale !== 'undefined') { locale.value = data.locale; } - if (typeof data?.name !== 'undefined') { name.value = data.name; } - if (typeof data?.osVersion !== 'undefined') { osVersion.value = data.osVersion; } - if (typeof data?.osVersionBranch !== 'undefined') { osVersionBranch.value = data.osVersionBranch; } - if (typeof data?.rebootType !== 'undefined') { rebootType.value = data.rebootType; } - if (typeof data?.rebootVersion !== 'undefined') { rebootVersion.value = data.rebootVersion; } - if (typeof data?.registered !== 'undefined') { registered.value = data.registered; } - if (typeof data?.regGen !== 'undefined') { regGen.value = data.regGen; } - if (typeof data?.regGuid !== 'undefined') { regGuid.value = data.regGuid; } - if (typeof data?.regTy !== 'undefined') { regTy.value = data.regTy; } - if (typeof data?.regExp !== 'undefined') { regExp.value = data.regExp; } - if (typeof data?.site !== 'undefined') { site.value = data.site; } - if (typeof data?.state !== 'undefined') { state.value = data.state; } - if (typeof data?.theme !== 'undefined') { theme.value = data.theme; } - if (typeof data?.updateOsIgnoredReleases !== 'undefined') { updateOsIgnoredReleases.value = data.updateOsIgnoredReleases; } - if (typeof data?.updateOsNotificationsEnabled !== 'undefined') { updateOsNotificationsEnabled.value = data.updateOsNotificationsEnabled; } - if (typeof data?.updateOsResponse !== 'undefined') { updateOsResponse.value = data.updateOsResponse; } - if (typeof data?.uptime !== 'undefined') { uptime.value = data.uptime; } - if (typeof data?.username !== 'undefined') { username.value = data.username; } - if (typeof data?.wanFQDN !== 'undefined') { wanFQDN.value = data.wanFQDN; } - if (typeof data?.regTm !== 'undefined') { regTm.value = data.regTm; } - if (typeof data?.regTo !== 'undefined') { regTo.value = data.regTo; } + console.debug("[setServer]", data); + if (typeof data?.apiKey !== "undefined") { + apiKey.value = data.apiKey; + } + if (typeof data?.array !== "undefined") { + array.value = data.array; + } + if (typeof data?.apiVersion !== "undefined") { + apiVersion.value = data.apiVersion; + } + if (typeof data?.avatar !== "undefined") { + avatar.value = data.avatar; + } + if (typeof data?.caseModel !== "undefined") { + caseModel.value = data.caseModel; + } + if (typeof data?.cloud !== "undefined") { + cloud.value = data.cloud; + } + if (typeof data?.combinedKnownOrigins !== "undefined") { + combinedKnownOrigins.value = data.combinedKnownOrigins; + } + if (typeof data?.config !== "undefined") { + config.value = data.config; + } + if (typeof data?.connectPluginInstalled !== "undefined") { + connectPluginInstalled.value = data.connectPluginInstalled; + } + if (typeof data?.connectPluginVersion !== "undefined") { + connectPluginVersion.value = data.connectPluginVersion; + } + if (typeof data?.csrf !== "undefined") { + csrf.value = data.csrf; + } + if (typeof data?.dateTimeFormat !== "undefined") { + dateTimeFormat.value = data.dateTimeFormat; + } + if (typeof data?.description !== "undefined") { + description.value = data.description; + } + if (typeof data?.deviceCount !== "undefined") { + deviceCount.value = data.deviceCount; + } + if (typeof data?.email !== "undefined") { + email.value = data.email; + } + if (typeof data?.expireTime !== "undefined") { + expireTime.value = data.expireTime; + } + if (typeof data?.flashBackupActivated !== "undefined") { + flashBackupActivated.value = data.flashBackupActivated; + } + if (typeof data?.flashProduct !== "undefined") { + flashProduct.value = data.flashProduct; + } + if (typeof data?.flashVendor !== "undefined") { + flashVendor.value = data.flashVendor; + } + if (typeof data?.guid !== "undefined") { + guid.value = data.guid; + } + if (typeof data?.keyfile !== "undefined") { + keyfile.value = data.keyfile; + } + if (typeof data?.lanIp !== "undefined") { + lanIp.value = data.lanIp; + } + if (typeof data?.license !== "undefined") { + license.value = data.license; + } + if (typeof data?.locale !== "undefined") { + locale.value = data.locale; + } + if (typeof data?.name !== "undefined") { + name.value = data.name; + } + if (typeof data?.osVersion !== "undefined") { + osVersion.value = data.osVersion; + } + if (typeof data?.osVersionBranch !== "undefined") { + osVersionBranch.value = data.osVersionBranch; + } + if (typeof data?.rebootType !== "undefined") { + rebootType.value = data.rebootType; + } + if (typeof data?.rebootVersion !== "undefined") { + rebootVersion.value = data.rebootVersion; + } + if (typeof data?.registered !== "undefined") { + registered.value = data.registered; + } + if (typeof data?.regGen !== "undefined") { + regGen.value = data.regGen; + } + if (typeof data?.regGuid !== "undefined") { + regGuid.value = data.regGuid; + } + if (typeof data?.regTy !== "undefined") { + regTy.value = data.regTy; + } + if (typeof data?.regExp !== "undefined") { + regExp.value = data.regExp; + } + if (typeof data?.site !== "undefined") { + site.value = data.site; + } + if (typeof data?.state !== "undefined") { + state.value = data.state; + } + if (typeof data?.theme !== "undefined") { + theme.value = data.theme; + } + if (typeof data?.updateOsIgnoredReleases !== "undefined") { + updateOsIgnoredReleases.value = data.updateOsIgnoredReleases; + } + if (typeof data?.updateOsNotificationsEnabled !== "undefined") { + updateOsNotificationsEnabled.value = data.updateOsNotificationsEnabled; + } + if (typeof data?.updateOsResponse !== "undefined") { + updateOsResponse.value = data.updateOsResponse; + } + if (typeof data?.uptime !== "undefined") { + uptime.value = data.uptime; + } + if (typeof data?.username !== "undefined") { + username.value = data.username; + } + if (typeof data?.wanFQDN !== "undefined") { + wanFQDN.value = data.wanFQDN; + } + if (typeof data?.regTm !== "undefined") { + regTm.value = data.regTm; + } + if (typeof data?.regTo !== "undefined") { + regTo.value = data.regTo; + } }; const setUpdateOsResponse = (response: ServerUpdateOsResponse) => { @@ -931,91 +1202,123 @@ export const useServerStore = defineStore('server', () => { }; const mutateServerStateFromApi = (data: serverStateQuery): Server => { - console.debug('mutateServerStateFromApi', data); + console.debug("mutateServerStateFromApi", data); const mutatedData: Server = { // if we get an owners obj back and the username is root we don't want to overwrite the values - ...(data.owner && data.owner.username !== 'root' + ...(data.owner && data.owner.username !== "root" ? { - // avatar: data.owner.avatar, - username: data.owner.username ?? '', + // avatar: data.owner.avatar, + username: data.owner.username ?? "", registered: true, } - : { // handles sign outs - // avatar: data.owner.avatar, - username: '', + : { + // handles sign outs + // avatar: data.owner.avatar, + username: "", registered: false, - } - ), - name: (data.info && data.info.os && data.info.os.hostname) ? data.info.os.hostname : undefined, - keyfile: (data.registration && data.registration.keyFile && data.registration.keyFile.contents) ? data.registration.keyFile.contents : undefined, - regGen: data.vars && data.vars.regGen ? parseInt(data.vars.regGen) : undefined, + }), + name: + data.info && data.info.os && data.info.os.hostname + ? data.info.os.hostname + : undefined, + keyfile: + data.registration && + data.registration.keyFile && + data.registration.keyFile.contents + ? data.registration.keyFile.contents + : undefined, + regGen: + data.vars && data.vars.regGen ? parseInt(data.vars.regGen) : undefined, state: data.vars && data.vars.regState ? data.vars.regState : undefined, config: data.config - ? { id: 'config', ...data.config } + ? { id: "config", ...data.config } : { - id: 'config', - error: data.vars && data.vars.configError ? data.vars.configError : undefined, - valid: data.vars && data.vars.configValid ? data.vars.configValid : true, + id: "config", + error: + data.vars && data.vars.configError + ? data.vars.configError + : undefined, + valid: + data.vars && data.vars.configValid ? data.vars.configValid : true, }, - expireTime: (data.registration && data.registration.expiration) ? parseInt(data.registration.expiration) : 0, - cloud: data.cloud ? useFragment(SERVER_CLOUD_FRAGMENT, data.cloud) : undefined, - regExp: (data.registration && data.registration.updateExpiration) ? Number(data.registration.updateExpiration) : undefined, + expireTime: + data.registration && data.registration.expiration + ? parseInt(data.registration.expiration) + : 0, + cloud: data.cloud + ? useFragment(SERVER_CLOUD_FRAGMENT, data.cloud) + : undefined, + regExp: + data.registration && data.registration.updateExpiration + ? Number(data.registration.updateExpiration) + : undefined, }; - console.debug('mutatedData', mutatedData); + console.debug("mutatedData", mutatedData); return mutatedData; }; - const fetchServerFromApi = () => { - const { result: resultServerState, refetch: refetchServerState } = useQuery(SERVER_STATE_QUERY, null, { - // pollInterval: 2500, - fetchPolicy: 'no-cache', - }); - const serverState = computed(() => resultServerState.value ?? null); - apiServerStateRefresh.value = refetchServerState; - watch(serverState, (value) => { - if (value) { - const mutatedServerStateResult = mutateServerStateFromApi(value); - setServer(mutatedServerStateResult); - } - }); - return resultServerState; - }; + const { + load, + refetch: refetchServerState, + onResult, + onError, + } = useLazyQuery(SERVER_STATE_QUERY); + + setTimeout(() => { + load(); + }, 500); + + onResult((result) => { + if (result.data) { + const { unraidApiStatus } = toRefs(useUnraidApiStore()); + unraidApiStatus.value = "online"; + apiServerStateRefresh.value = refetchServerState; + const mutatedServerStateResult = mutateServerStateFromApi(result.data); + setServer(mutatedServerStateResult); + } + }); + + onError((error) => { + console.error("[serverStateQuery] error", error); + const { unraidApiStatus } = toRefs(useUnraidApiStore()); + unraidApiStatus.value = "offline"; + }); const phpServerStateRefresh = async () => { try { - const stateResponse: Server = await WebguiState - .get() - .json(); + const stateResponse: Server = await WebguiState.get().json(); setServer(stateResponse); return stateResponse; } catch (error) { - console.error('[phpServerStateRefresh] error', error); + console.error("[phpServerStateRefresh] error", error); } }; let refreshCount = 0; const refreshLimit = 20; const refreshTimeout = 250; - const refreshServerStateStatus = ref<'done' | 'ready' | 'refreshing' | 'timeout'>('ready'); + const refreshServerStateStatus = ref< + "done" | "ready" | "refreshing" | "timeout" + >("ready"); const refreshServerState = async () => { // If we've reached the refresh limit, stop refreshing if (refreshCount >= refreshLimit) { - refreshServerStateStatus.value = 'timeout'; + refreshServerStateStatus.value = "timeout"; return false; } refreshCount++; - refreshServerStateStatus.value = 'refreshing'; + refreshServerStateStatus.value = "refreshing"; // Values to compare to response values should be set before the response is set const oldRegistered = registered.value; const oldState = state.value; const oldRegExp = regExp.value; - const fromApi = Boolean(apiServerStateRefresh?.value); + const fromApi = Boolean(apiServerStateRefresh.value); // Fetch the server state from the API or PHP const response = fromApi - ? await apiServerStateRefresh?.value?.() + ? await refetchServerState() : await phpServerStateRefresh(); if (!response) { return setTimeout(() => { @@ -1025,18 +1328,22 @@ export const useServerStore = defineStore('server', () => { // Extract the new values from the response const output: { - newRegistered: boolean, - newState: ServerState | ServerState | null, - newRegExp: number | null, + newRegistered: boolean; + newState: ServerState | ServerState | null; + newRegExp: number | null; } = { newRegistered: false, newState: null, newRegExp: null, - } - if ('data' in response) { - output.newRegistered = Boolean(response.data.owner && response.data.owner.username !== 'root'); + }; + if ("data" in response) { + output.newRegistered = Boolean( + response.data.owner && response.data.owner.username !== "root" + ); output.newState = response.data.vars?.regState ?? null; - output.newRegExp = Number(response.data.registration?.updateExpiration ?? 0); + output.newRegExp = Number( + response.data.registration?.updateExpiration ?? 0 + ); } else { output.newRegistered = Boolean(response.registered); output.newState = response.state; @@ -1049,7 +1356,7 @@ export const useServerStore = defineStore('server', () => { // If the registration status or state changed, stop refreshing if (registrationStatusChanged || stateChanged || regExpChanged) { - refreshServerStateStatus.value = 'done'; + refreshServerStateStatus.value = "done"; return true; } // If we haven't reached the refresh limit, try again @@ -1058,11 +1365,16 @@ export const useServerStore = defineStore('server', () => { }, refreshTimeout); }; - const filteredKeyActions = (filterType: 'by' | 'out', filters: string|ServerStateDataKeyActions[]): ServerStateDataAction[] | undefined => { - if (!stateData.value.actions) { return; } + const filteredKeyActions = ( + filterType: "by" | "out", + filters: string | ServerStateDataKeyActions[] + ): ServerStateDataAction[] | undefined => { + if (!stateData.value.actions) { + return; + } return stateData.value.actions.filter((action) => { - return filterType === 'out' + return filterType === "out" ? !filters.includes(action.name as ServerStateDataKeyActions) : filters.includes(action.name as ServerStateDataKeyActions); }); @@ -1074,35 +1386,37 @@ export const useServerStore = defineStore('server', () => { watchEffect(() => { if (rebootVersion.value) { - console.debug('[server.rebootVersion]', rebootVersion.value); + console.debug("[server.rebootVersion]", rebootVersion.value); } }); const updateOsIgnoreRelease = (release: string) => { updateOsIgnoredReleases.value.push(release); const response = WebguiUpdateIgnore({ - action: 'ignoreVersion', + action: "ignoreVersion", version: release, }); - console.debug('[updateOsIgnoreRelease] response', response); + console.debug("[updateOsIgnoreRelease] response", response); /** @todo when update check modal is displayed and there's no available updates, allow users to remove ignored releases from the list */ }; const updateOsRemoveIgnoredRelease = (release: string) => { - updateOsIgnoredReleases.value = updateOsIgnoredReleases.value.filter(r => r !== release); + updateOsIgnoredReleases.value = updateOsIgnoredReleases.value.filter( + (r) => r !== release + ); const response = WebguiUpdateIgnore({ - action: 'removeIgnoredVersion', + action: "removeIgnoredVersion", version: release, }); - console.debug('[updateOsRemoveIgnoredRelease] response', response); + console.debug("[updateOsRemoveIgnoredRelease] response", response); }; const updateOsRemoveAllIgnoredReleases = () => { updateOsIgnoredReleases.value = []; const response = WebguiUpdateIgnore({ - action: 'removeAllIgnored', + action: "removeAllIgnored", }); - console.debug('[updateOsRemoveAllIgnoredReleases] response', response); + console.debug("[updateOsRemoveAllIgnoredReleases] response", response); }; return { @@ -1172,7 +1486,6 @@ export const useServerStore = defineStore('server', () => { // actions setServer, setUpdateOsResponse, - fetchServerFromApi, refreshServerState, filteredKeyActions, setRebootVersion, diff --git a/web/store/unraidApi.ts b/web/store/unraidApi.ts index 85a6edace..3ef6b05f6 100644 --- a/web/store/unraidApi.ts +++ b/web/store/unraidApi.ts @@ -26,20 +26,6 @@ export const useUnraidApiStore = defineStore("unraidApi", () => { client ); - watch( - serverStore, - async (newVal) => { - if (!newVal.fetchServerFromApi) { - return; - } - const apiResponse = await newVal.fetchServerFromApi(); - if (apiResponse) { - // we have a response, so we're online - unraidApiStatus.value = "online"; - } - }, - { immediate: true } - ); // const unraidApiErrors = ref([]); const unraidApiStatus = ref< "connecting" | "offline" | "online" | "restarting"