diff --git a/web/store/unraidApi.ts b/web/store/unraidApi.ts index 10ab6aaf4..859e15061 100644 --- a/web/store/unraidApi.ts +++ b/web/store/unraidApi.ts @@ -1,20 +1,33 @@ -// @ts-expect-error - ignore that it can't find any declarations -import { from, ApolloClient, createHttpLink, InMemoryCache, split } from '@apollo/client/core/core.cjs'; -import { onError } from '@apollo/client/link/error'; -import { RetryLink } from '@apollo/client/link/retry'; -import { GraphQLWsLink } from '@apollo/client/link/subscriptions'; -import { getMainDefinition } from '@apollo/client/utilities'; -import { ArrowPathIcon, CogIcon } from '@heroicons/vue/24/solid'; -import { provideApolloClient } from '@vue/apollo-composable'; +import { + from, + ApolloClient, + createHttpLink, + InMemoryCache, + split, + // @ts-expect-error - CommonJS doesn't have type definitions +} from "@apollo/client/core/core.cjs"; +import { + type ApolloClient as ApolloClientType, + type InMemoryCache as InMemoryCacheType, +} from "@apollo/client/core"; +import { onError } from "@apollo/client/link/error"; +import { RetryLink } from "@apollo/client/link/retry"; +import { GraphQLWsLink } from "@apollo/client/link/subscriptions"; +import { getMainDefinition } from "@apollo/client/utilities"; +import { ArrowPathIcon, CogIcon } from "@heroicons/vue/24/solid"; +import { provideApolloClient } from "@vue/apollo-composable"; // import { logErrorMessages } from '@vue/apollo-util'; -import { createClient } from 'graphql-ws'; -import { defineStore, createPinia, setActivePinia } from 'pinia'; -import type { UserProfileLink } from '~/types/userProfile'; +import { createClient } from "graphql-ws"; +import { defineStore, createPinia, setActivePinia } from "pinia"; +import type { UserProfileLink } from "~/types/userProfile"; -import { WebguiUnraidApiCommand } from '~/composables/services/webgui'; -import { WEBGUI_GRAPHQL, WEBGUI_SETTINGS_MANAGMENT_ACCESS } from '~/helpers/urls'; -import { useErrorsStore } from '~/store/errors'; -import { useServerStore } from '~/store/server'; +import { WebguiUnraidApiCommand } from "~/composables/services/webgui"; +import { + WEBGUI_GRAPHQL, + WEBGUI_SETTINGS_MANAGMENT_ACCESS, +} from "~/helpers/urls"; +import { useErrorsStore } from "~/store/errors"; +import { useServerStore } from "~/store/server"; /** * @see https://stackoverflow.com/questions/73476371/using-pinia-with-vue-js-web-components @@ -22,40 +35,46 @@ import { useServerStore } from '~/store/server'; */ setActivePinia(createPinia()); -const ERROR_CORS_403 = 'The CORS policy for this site does not allow access from the specified Origin'; +const ERROR_CORS_403 = + "The CORS policy for this site does not allow access from the specified Origin"; const httpEndpoint = WEBGUI_GRAPHQL; -const wsEndpoint = new URL(WEBGUI_GRAPHQL.toString().replace('http', 'ws')); +const wsEndpoint = new URL(WEBGUI_GRAPHQL.toString().replace("http", "ws")); -export const useUnraidApiStore = defineStore('unraidApi', () => { +export const useUnraidApiStore = defineStore("unraidApi", () => { const errorsStore = useErrorsStore(); const serverStore = useServerStore(); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const unraidApiClient = ref>(); + const unraidApiClient = ref | null>(null); watch(unraidApiClient, (newVal) => { if (newVal) { const apiResponse = serverStore.fetchServerFromApi(); if (apiResponse) { // we have a response, so we're online - unraidApiStatus.value = 'online'; + unraidApiStatus.value = "online"; } } }); // const unraidApiErrors = ref([]); - const unraidApiStatus = ref<'connecting' | 'offline' | 'online' | 'restarting'>('offline'); + const unraidApiStatus = ref< + "connecting" | "offline" | "online" | "restarting" + >("offline"); const prioritizeCorsError = ref(false); // Ensures we don't overwrite this specific error message with a non-descriptive network error message const unraidApiRestartAction = computed((): UserProfileLink | undefined => { const { connectPluginInstalled, stateDataError } = serverStore; - if (unraidApiStatus.value !== 'offline' || !connectPluginInstalled || stateDataError) { + if ( + unraidApiStatus.value !== "offline" || + !connectPluginInstalled || + stateDataError + ) { return undefined; } return { click: () => restartUnraidApiClient(), emphasize: true, icon: ArrowPathIcon, - text: 'Restart unraid-api', + text: "Restart unraid-api", }; }); @@ -64,9 +83,9 @@ export const useUnraidApiStore = defineStore('unraidApi', () => { */ const createApolloClient = () => { // return; // @todo remove - unraidApiStatus.value = 'connecting'; + unraidApiStatus.value = "connecting"; - const headers = { 'x-api-key': serverStore.apiKey }; + const headers = { "x-api-key": serverStore.apiKey }; const httpLink = createHttpLink({ uri: httpEndpoint.toString(), @@ -79,7 +98,7 @@ export const useUnraidApiStore = defineStore('unraidApi', () => { connectionParams: () => ({ headers, }), - }), + }) ); // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -87,28 +106,33 @@ export const useUnraidApiStore = defineStore('unraidApi', () => { if (graphQLErrors) { // eslint-disable-next-line @typescript-eslint/no-explicit-any graphQLErrors.map((error: any) => { - console.error('[GraphQL error]', error); - const errorMsg = error.error && error.error.message ? error.error.message : error.message; - if (errorMsg && errorMsg.includes('offline')) { - unraidApiStatus.value = 'offline'; + console.error("[GraphQL error]", error); + const errorMsg = + error.error && error.error.message + ? error.error.message + : error.message; + if (errorMsg && errorMsg.includes("offline")) { + unraidApiStatus.value = "offline"; // attempt to automatically restart the unraid-api - if (unraidApiRestartAction) { restartUnraidApiClient(); } + if (unraidApiRestartAction) { + restartUnraidApiClient(); + } } if (errorMsg && errorMsg.includes(ERROR_CORS_403)) { prioritizeCorsError.value = true; const msg = `

The CORS policy for the unraid-api does not allow access from the specified origin.

If you are using a reverse proxy, you need to copy your origin ${window.location.origin} and paste it into the "Extra Origins" list in the Connect settings.

`; errorsStore.setError({ - heading: 'Unraid API • CORS Error', + heading: "Unraid API • CORS Error", message: msg, - level: 'error', - ref: 'unraidApiCorsError', - type: 'unraidApiGQL', + level: "error", + ref: "unraidApiCorsError", + type: "unraidApiGQL", actions: [ { href: `${WEBGUI_SETTINGS_MANAGMENT_ACCESS.toString()}#extraOriginsSettings`, icon: CogIcon, - text: 'Go to Connect Settings', - } + text: "Go to Connect Settings", + }, ], }); } @@ -119,8 +143,11 @@ export const useUnraidApiStore = defineStore('unraidApi', () => { if (networkError && !prioritizeCorsError) { console.error(`[Network error]: ${networkError}`); const msg = networkError.message ? networkError.message : networkError; - if (typeof msg === 'string' && msg.includes('Unexpected token < in JSON at position 0')) { - return 'Unraid API • CORS Error'; + if ( + typeof msg === "string" && + msg.includes("Unexpected token < in JSON at position 0") + ) { + return "Unraid API • CORS Error"; } return msg; } @@ -143,36 +170,33 @@ export const useUnraidApiStore = defineStore('unraidApi', () => { interface Definintion { kind: string; operation?: string; - }; + } const splitLinks = split( // eslint-disable-next-line @typescript-eslint/no-explicit-any ({ query }: any) => { const definition: Definintion = getMainDefinition(query); return ( - definition.kind === 'OperationDefinition' && - definition.operation === 'subscription' + definition.kind === "OperationDefinition" && + definition.operation === "subscription" ); }, wsLink, - httpLink, + httpLink ); /** * @todo as we add retries, determine which we'll need * https://www.apollographql.com/docs/react/api/link/introduction/#additive-composition * https://www.apollographql.com/docs/react/api/link/introduction/#directional-composition */ - const additiveLink = from([ - errorLink, - retryLink, - splitLinks, - ]); + const additiveLink = from([errorLink, retryLink, splitLinks]); - unraidApiClient.value = new ApolloClient({ + const apolloClient = new ApolloClient({ link: additiveLink, cache: new InMemoryCache(), - }); + }) as ApolloClientType; + unraidApiClient.value = apolloClient; - provideApolloClient(unraidApiClient.value); + provideApolloClient(apolloClient); }; /** * Automatically called when an apiKey is unset in the serverStore @@ -187,14 +211,14 @@ export const useUnraidApiStore = defineStore('unraidApi', () => { // (wsLink.value as any).subscriptionClient.close(); // needed if we start using subscriptions } unraidApiClient.value = undefined; - unraidApiStatus.value = 'offline'; + unraidApiStatus.value = "offline"; }; /** * Can both start and restart the unraid-api depending on it's current status */ const restartUnraidApiClient = async () => { - const command = unraidApiStatus.value === 'offline' ? 'start' : 'restart'; - unraidApiStatus.value = 'restarting'; + const command = unraidApiStatus.value === "offline" ? "start" : "restart"; + unraidApiStatus.value = "restarting"; try { await WebguiUnraidApiCommand({ csrf_token: serverStore.csrf, @@ -206,18 +230,18 @@ export const useUnraidApiStore = defineStore('unraidApi', () => { } }, 5000); } catch (error) { - let errorMessage = 'Unknown error'; - if (typeof error === 'string') { + let errorMessage = "Unknown error"; + if (typeof error === "string") { errorMessage = error.toUpperCase(); } else if (error instanceof Error) { errorMessage = error.message; } errorsStore.setError({ - heading: 'Error: unraid-api restart', + heading: "Error: unraid-api restart", message: errorMessage, - level: 'error', - ref: 'restartUnraidApiClient', - type: 'request', + level: "error", + ref: "restartUnraidApiClient", + type: "request", }); } };