refactor(web): sign in / out graph mutations

This commit is contained in:
Zack Spear
2023-08-31 17:07:10 -07:00
parent 320b181601
commit 17c7757bf6
8 changed files with 257 additions and 157 deletions

View File

@@ -3,16 +3,18 @@ import { TransitionRoot } from '@headlessui/vue';
import { storeToRefs } from 'pinia';
import { useDropdownStore } from '~/store/dropdown';
import { useErrorsStore } from '~/store/errors';
import { useServerStore } from '~/store/server';
defineProps<{ t: any; }>();
const dropdownStore = useDropdownStore();
const { dropdownVisible } = storeToRefs(dropdownStore);
const { errors } = storeToRefs(useErrorsStore());
const { connectPluginInstalled, registered, state, stateDataError } = storeToRefs(useServerStore());
const showDefaultContent = computed(() => !showLaunchpad.value);
const showLaunchpad = computed(() => state.value === 'ENOKEYFILE' || ((connectPluginInstalled.value && !registered.value) && !stateDataError.value));
const showLaunchpad = computed(() => state.value === 'ENOKEYFILE' || ((connectPluginInstalled.value && !registered.value) && !errors.value.length && !stateDataError.value));
</script>
<template>

View File

@@ -13,6 +13,8 @@ import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-
* Therefore it is highly recommended to use the babel or swc plugin for production.
*/
const documents = {
"\n mutation ConnectSignIn($input: ConnectSignInInput!) {\n connectSignIn(input: $input)\n }\n": types.ConnectSignInDocument,
"\n mutation SignOut {\n connectSignOut\n }\n": types.SignOutDocument,
"\n query serverState {\n cloud {\n error\n apiKey {\n valid\n error\n }\n cloud {\n status\n error\n }\n minigraphql {\n status\n error\n }\n relay {\n status\n error\n }\n }\n config {\n error\n valid\n }\n info {\n os {\n hostname\n }\n }\n owner {\n avatar\n username\n }\n registration {\n state\n expiration\n keyFile {\n contents\n }\n }\n vars {\n regGen\n regState\n configError\n configValid\n }\n }\n": types.serverStateDocument,
};
@@ -30,6 +32,14 @@ const documents = {
*/
export function graphql(source: string): unknown;
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n mutation ConnectSignIn($input: ConnectSignInInput!) {\n connectSignIn(input: $input)\n }\n"): (typeof documents)["\n mutation ConnectSignIn($input: ConnectSignInInput!) {\n connectSignIn(input: $input)\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n mutation SignOut {\n connectSignOut\n }\n"): (typeof documents)["\n mutation SignOut {\n connectSignOut\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/

View File

@@ -20,10 +20,16 @@ export type Scalars = {
JSON: { input: string; output: string; }
/** The `Long` scalar type represents 52-bit integers */
Long: { input: number; output: number; }
/** A field whose value is a valid TCP port within the range of 0 to 65535: https://en.wikipedia.org/wiki/Transmission_Control_Protocol#TCP_ports */
Port: { input: number; output: number; }
/** A field whose value is a generic Universally Unique Identifier: https://en.wikipedia.org/wiki/Universally_unique_identifier. */
UUID: { input: string; output: string; }
};
export type AllowedOriginInput = {
origins: Array<Scalars['String']['input']>;
};
export type ApiKey = {
__typename?: 'ApiKey';
description?: Maybe<Scalars['String']['output']>;
@@ -242,6 +248,20 @@ export enum ConfigErrorState {
Withdrawn = 'WITHDRAWN'
}
export type ConnectSignInInput = {
accessToken?: InputMaybe<Scalars['String']['input']>;
apiKey: Scalars['String']['input'];
idToken?: InputMaybe<Scalars['String']['input']>;
refreshToken?: InputMaybe<Scalars['String']['input']>;
userInfo?: InputMaybe<ConnectUserInfoInput>;
};
export type ConnectUserInfoInput = {
avatar?: InputMaybe<Scalars['String']['input']>;
email: Scalars['String']['input'];
preferred_username: Scalars['String']['input'];
};
export type ContainerHostConfig = {
__typename?: 'ContainerHostConfig';
networkMode: Scalars['String']['output'];
@@ -568,6 +588,8 @@ export type Mutation = {
/** Cancel parity check */
cancelParityCheck?: Maybe<Scalars['JSON']['output']>;
clearArrayDiskStatistics?: Maybe<Scalars['JSON']['output']>;
connectSignIn: Scalars['Boolean']['output'];
connectSignOut: Scalars['Boolean']['output'];
/** Delete a user */
deleteUser?: Maybe<User>;
/** Get an existing API key */
@@ -582,6 +604,8 @@ export type Mutation = {
/** Resume parity check */
resumeParityCheck?: Maybe<Scalars['JSON']['output']>;
sendNotification?: Maybe<Notification>;
setAdditionalAllowedOrigins: Array<Scalars['String']['output']>;
setupRemoteAccess: Scalars['Boolean']['output'];
shutdown?: Maybe<Scalars['String']['output']>;
/** Start array */
startArray?: Maybe<ArrayType>;
@@ -589,7 +613,6 @@ export type Mutation = {
startParityCheck?: Maybe<Scalars['JSON']['output']>;
/** Stop array */
stopArray?: Maybe<ArrayType>;
testMutation?: Maybe<Scalars['JSON']['output']>;
unmountArrayDisk?: Maybe<Disk>;
/** Update an existing API key */
updateApikey?: Maybe<ApiKey>;
@@ -627,6 +650,11 @@ export type MutationclearArrayDiskStatisticsArgs = {
};
export type MutationconnectSignInArgs = {
input: ConnectSignInInput;
};
export type MutationdeleteUserArgs = {
input: deleteUserInput;
};
@@ -659,14 +687,18 @@ export type MutationsendNotificationArgs = {
};
export type MutationstartParityCheckArgs = {
correct?: InputMaybe<Scalars['Boolean']['input']>;
export type MutationsetAdditionalAllowedOriginsArgs = {
input: AllowedOriginInput;
};
export type MutationtestMutationArgs = {
id: Scalars['String']['input'];
input?: InputMaybe<testMutationInput>;
export type MutationsetupRemoteAccessArgs = {
input: SetupRemoteAccessInput;
};
export type MutationstartParityCheckArgs = {
correct?: InputMaybe<Scalars['Boolean']['input']>;
};
@@ -711,6 +743,8 @@ export type Notification = {
export type NotificationFilter = {
importance?: InputMaybe<Importance>;
limit: Scalars['Int']['input'];
offset: Scalars['Int']['input'];
type?: InputMaybe<NotificationType>;
};
@@ -888,7 +922,6 @@ export type Query = {
servers: Array<Server>;
/** Network Shares */
shares?: Maybe<Array<Maybe<Share>>>;
testQuery?: Maybe<Scalars['JSON']['output']>;
twoFactor?: Maybe<TwoFactorWithToken>;
unassignedDevices?: Maybe<Array<Maybe<UnassignedDevice>>>;
/** User account */
@@ -930,7 +963,7 @@ export type QuerydockerNetworksArgs = {
export type QuerynotificationsArgs = {
filter?: InputMaybe<NotificationFilter>;
filter: NotificationFilter;
};
@@ -939,12 +972,6 @@ export type QueryserverArgs = {
};
export type QuerytestQueryArgs = {
id: Scalars['String']['input'];
input?: InputMaybe<testQueryInput>;
};
export type QueryuserArgs = {
id: Scalars['ID']['input'];
};
@@ -1053,6 +1080,12 @@ export type Service = {
version?: Maybe<Scalars['String']['output']>;
};
export type SetupRemoteAccessInput = {
accessType: WAN_ACCESS_TYPE;
forwardType?: InputMaybe<WAN_FORWARD_TYPE>;
port?: InputMaybe<Scalars['Port']['input']>;
};
/** Network Share */
export type Share = {
__typename?: 'Share';
@@ -1107,7 +1140,6 @@ export type Subscription = {
service?: Maybe<Array<Service>>;
share: Share;
shares?: Maybe<Array<Share>>;
testSubscription: Scalars['String']['output'];
twoFactor?: Maybe<TwoFactorWithoutToken>;
unassignedDevices?: Maybe<Array<UnassignedDevice>>;
user: User;
@@ -1505,6 +1537,17 @@ export type Vms = {
domain?: Maybe<Array<VmDomain>>;
};
export enum WAN_ACCESS_TYPE {
Always = 'ALWAYS',
Disabled = 'DISABLED',
Dynamic = 'DYNAMIC'
}
export enum WAN_FORWARD_TYPE {
Static = 'STATIC',
Upnp = 'UPNP'
}
export type Welcome = {
__typename?: 'Welcome';
message: Scalars['String']['output'];
@@ -1568,15 +1611,6 @@ export enum registrationType {
Trial = 'TRIAL'
}
export type testMutationInput = {
state: Scalars['String']['input'];
};
export type testQueryInput = {
optional?: InputMaybe<Scalars['Boolean']['input']>;
state: Scalars['String']['input'];
};
export type updateApikeyInput = {
description?: InputMaybe<Scalars['String']['input']>;
expiresAt: Scalars['Long']['input'];
@@ -1586,10 +1620,24 @@ export type usersInput = {
slim?: InputMaybe<Scalars['Boolean']['input']>;
};
export type ConnectSignInMutationVariables = Exact<{
input: ConnectSignInInput;
}>;
export type ConnectSignInMutation = { __typename?: 'Mutation', connectSignIn: boolean };
export type SignOutMutationVariables = Exact<{ [key: string]: never; }>;
export type SignOutMutation = { __typename?: 'Mutation', connectSignOut: boolean };
export type serverStateQueryVariables = Exact<{ [key: string]: never; }>;
export type serverStateQuery = { __typename?: 'Query', cloud?: { __typename?: 'Cloud', error?: string | null, apiKey: { __typename?: 'ApiKeyResponse', valid: boolean, error?: string | null }, cloud: { __typename?: 'CloudResponse', status: string, error?: string | null }, minigraphql: { __typename?: 'MinigraphqlResponse', status: MinigraphStatus, error?: string | null }, relay?: { __typename?: 'RelayResponse', status: string, error?: string | null } | null } | null, config: { __typename?: 'Config', error?: ConfigErrorState | null, valid?: boolean | null }, info?: { __typename?: 'Info', os?: { __typename?: 'Os', hostname?: string | null } | null } | null, owner?: { __typename?: 'Owner', avatar?: string | null, username?: string | null } | null, registration?: { __typename?: 'Registration', state?: RegistrationState | null, expiration?: string | null, keyFile?: { __typename?: 'KeyFile', contents?: string | null } | null } | null, vars?: { __typename?: 'Vars', regGen?: string | null, regState?: RegistrationState | null, configError?: ConfigErrorState | null, configValid?: boolean | null } | null };
export const ConnectSignInDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ConnectSignIn"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ConnectSignInInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"connectSignIn"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode<ConnectSignInMutation, ConnectSignInMutationVariables>;
export const SignOutDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SignOut"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"connectSignOut"}}]}}]} as unknown as DocumentNode<SignOutMutation, SignOutMutationVariables>;
export const serverStateDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"serverState"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"cloud"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"error"}},{"kind":"Field","name":{"kind":"Name","value":"apiKey"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"valid"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cloud"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}},{"kind":"Field","name":{"kind":"Name","value":"minigraphql"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}},{"kind":"Field","name":{"kind":"Name","value":"relay"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"config"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"error"}},{"kind":"Field","name":{"kind":"Name","value":"valid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"info"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"os"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hostname"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"owner"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"username"}}]}},{"kind":"Field","name":{"kind":"Name","value":"registration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"expiration"}},{"kind":"Field","name":{"kind":"Name","value":"keyFile"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"contents"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"vars"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"regGen"}},{"kind":"Field","name":{"kind":"Name","value":"regState"}},{"kind":"Field","name":{"kind":"Name","value":"configError"}},{"kind":"Field","name":{"kind":"Name","value":"configValid"}}]}}]}}]} as unknown as DocumentNode<serverStateQuery, serverStateQueryVariables>;

View File

@@ -0,0 +1,13 @@
import { graphql } from '~/composables/gql/gql';
export const CONNECT_SIGN_IN = graphql(/* GraphQL */`
mutation ConnectSignIn($input: ConnectSignInInput!) {
connectSignIn(input: $input)
}
`);
export const CONNECT_SIGN_OUT = graphql(/* GraphQL */`
mutation SignOut {
connectSignOut
}
`);

View File

@@ -1,8 +1,12 @@
import { useMutation } from '@vue/apollo-composable';
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 { useErrorsStore } from '~/store/errors';
import { useServerStore } from '~/store/server';
import { useUnraidApiStore } from '~/store/unraidApi';
import { WebguiUpdate } from '~/composables/services/webgui';
import { ACCOUNT_CALLBACK } from '~/helpers/urls';
import type { ExternalSignIn, ExternalSignOut } from '~/store/callback';
@@ -12,16 +16,50 @@ import type { ExternalSignIn, ExternalSignOut } from '~/store/callback';
*/
setActivePinia(createPinia());
export interface ConnectSignInMutationPayload {
apiKey: string;
email: string;
preferred_username: string;
}
export const useAccountStore = defineStore('account', () => {
const callbackStore = useCallbackStore();
const errorsStore = useErrorsStore();
const serverStore = useServerStore();
const unraidApiStore = useUnraidApiStore();
// State
const accountAction = ref<ExternalSignIn|ExternalSignOut>();
const accountAction = ref<ExternalSignIn | ExternalSignOut>();
const accountActionHide = ref<boolean>(false);
const accountActionStatus = ref<'failed' | 'ready' | 'success' | 'updating'>('ready');
/**
* Handling sign in / out via graph api
*/
const unraidApiClient = computed(() => unraidApiStore.unraidApiClient);
const connectSignInPayload = ref<ConnectSignInMutationPayload | undefined>();
const setConnectSignInPayload = (payload: ConnectSignInMutationPayload | undefined) => {
connectSignInPayload.value = payload;
};
const queueConnectSignOut = ref<boolean>(false);
const setQueueConnectSignOut = (data: boolean) => {
queueConnectSignOut.value = data;
};
watchEffect(() => {
if (unraidApiClient.value && connectSignInPayload.value) {
// connectSignInMutation();
setTimeout(() => {
connectSignInMutation();
}, 250);
}
if (unraidApiClient.value && queueConnectSignOut.value) {
// connectSignOutMutation();
setTimeout(() => {
connectSignOutMutation();
}, 250);
}
});
const username = ref<string>('');
// Getters
@@ -100,75 +138,94 @@ export const useAccountStore = defineStore('account', () => {
serverStore.inIframe,
);
};
/**
* @description Update myservers.cfg for both Sign In & Sign Out
* @note unraid-api requires apikey & token realted keys to be lowercase
*/
const updatePluginConfig = async (action: ExternalSignIn | ExternalSignOut) => {
// save any existing username before updating
if (serverStore.username) { username.value = serverStore.username; }
accountAction.value = action;
const connectSignInMutation = async () => {
if (!connectSignInPayload.value
|| (connectSignInPayload.value && (!connectSignInPayload.value.apiKey || !connectSignInPayload.value.email || !connectSignInPayload.value.preferred_username))
) {
accountActionStatus.value = 'failed';
return;
}
accountActionStatus.value = 'updating';
const { mutate: signInMutation, onDone, onError } = useMutation(CONNECT_SIGN_IN, {
variables: {
input: {
apiKey: connectSignInPayload.value.apiKey,
userInfo: {
email: connectSignInPayload.value.email,
preferred_username: connectSignInPayload.value.preferred_username,
}
}
}
});
if (!serverStore.registered && !accountAction.value.user) {
signInMutation();
onDone((res) => {
if (res.data?.connectSignIn) {
accountActionStatus.value = 'success';
setConnectSignInPayload(undefined); // reset
return;
}
accountActionStatus.value = 'failed';
errorsStore.setError({
heading: 'unraid-api failed to update Connect account configuration',
message: 'Sign In mutation unsuccessful',
level: 'error',
ref: 'connectSignInMutation',
type: 'account',
});
});
onError(error => {
logErrorMessages(error);
accountActionStatus.value = 'failed';
errorsStore.setError({
heading: 'unraid-api failed to update Connect account configuration',
message: error.message,
level: 'error',
ref: 'connectSignInMutation',
type: 'account',
});
});
};
const connectSignOutMutation = async () => {
accountActionStatus.value = 'updating';
// @todo is this still needed here with the change to a mutation?
if (!serverStore.registered && accountAction.value && !accountAction.value.user) {
accountActionHide.value = true;
accountActionStatus.value = 'success';
return;
}
try {
const userPayload = {
...(accountAction.value.user
? {
apikey: accountAction.value.apiKey,
// avatar: '',
email: accountAction.value.user?.email,
regWizTime: `${Date.now()}_${serverStore.guid}`, // set when signing in the first time and never unset for the sake of displaying Sign In/Up in the UPC without needing to validate guid every time
username: accountAction.value.user?.preferred_username,
}
: {
accesstoken: '',
apikey: '',
avatar: '',
email: '',
idtoken: '',
refreshtoken: '',
username: '',
}),
};
const response = await WebguiUpdate
.formUrl({
csrf_token: serverStore.csrf,
'#file': 'dynamix.my.servers/myservers.cfg',
'#section': 'remote',
...userPayload,
})
.post()
.res((res) => {
accountActionStatus.value = 'success';
})
.catch((err) => {
accountActionStatus.value = 'failed';
errorsStore.setError({
heading: 'Failed to update Connect account configuration',
message: err.message,
level: 'error',
ref: 'updatePluginConfig',
type: 'account',
});
});
return response;
} catch (err) {
const { mutate: signOutMutation, onDone, onError } = useMutation(CONNECT_SIGN_OUT);
signOutMutation();
onDone((res) => {
console.debug('[connectSignOutMutation]', res);
accountActionStatus.value = 'success';
setQueueConnectSignOut(false); // reset
});
onError(error => {
logErrorMessages(error);
accountActionStatus.value = 'failed';
errorsStore.setError({
heading: 'Failed to update Connect account configuration',
message: err.message,
message: error.message,
level: 'error',
ref: 'updatePluginConfig',
ref: 'connectSignOutMutation',
type: 'account',
});
}
});
};
const setAccountAction = (action: ExternalSignIn|ExternalSignOut) => {
console.debug('[setAccountAction]', { action });
accountAction.value = action;
};
return {
@@ -185,6 +242,8 @@ export const useAccountStore = defineStore('account', () => {
signOut,
trialExtend,
trialStart,
updatePluginConfig,
setAccountAction,
setConnectSignInPayload,
setQueueConnectSignOut,
};
});

View File

@@ -45,28 +45,46 @@ export const useCallbackActionsStore = defineStore('callbackActions', () => {
if (action?.keyUrl) {
await installKeyStore.install(action as ExternalKeyActions);
}
if (action?.user || action.type === 'signOut' || action.type === 'oemSignOut') {
await accountStore.updatePluginConfig(action);
if (action?.user || action.type === 'signIn') {
accountStore.setAccountAction(action);
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);
accountStore.setQueueConnectSignOut(true);
}
// all actions have run
if (array.length === (index + 1)) {
await serverStore.refreshServerState();
// callbackStatus.value = 'done';
if (array.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';
callbackStatus.value = allSuccess ? 'success' : 'error';
} else {
// only 1 action needs to be successful
const oneSuccess = accountStore.accountActionStatus === 'success' || installKeyStore.keyInstallStatus === 'success';
callbackStatus.value = oneSuccess ? 'success' : 'error';
}
}
});
};
// 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';
callbackStatus.value = allSuccess ? 'success' : 'error';
} else {
// only 1 action needs to be successful
const oneSuccess = accountStore.accountActionStatus === 'success' || installKeyStore.keyInstallStatus === 'success';
callbackStatus.value = oneSuccess ? 'success' : 'error';
}
}
/** @todo ensure timeout messaging is correct */
if (callbackData.value?.actions && refreshServerStateStatus.value === 'timeout') {
callbackStatus.value = 'error';
}
});
const setCallbackStatus = (status: CallbackStatus) => { callbackStatus.value = status; };
watch(callbackStatus, (newVal, oldVal) => {
if (newVal === 'loading') {
addPreventClose();

View File

@@ -50,7 +50,15 @@ export const useServerStore = defineStore('server', () => {
/**
* State
*/
const apiKey = ref<string>(''); // @todo potentially move to a user store
const apiKey = ref<string>('');
watch(apiKey, (newVal, oldVal) => {
if (newVal) {
return unraidApiStore.createApolloClient();
}
if (oldVal) {
return unraidApiStore.closeUnraidApiClient();
}
});
const apiVersion = ref<string>('');
const avatar = ref<string>(''); // @todo potentially move to a user store
const cloud = ref<ServerStateCloudStatus>();
@@ -536,38 +544,6 @@ export const useServerStore = defineStore('server', () => {
});
const trialExtensionEligible = computed(() => !regGen.value || regGen.value < 2);
const invalidApiKey = computed((): Error | undefined => {
// must be registered with plugin installed
if (!connectPluginInstalled.value || !registered.value) {
return undefined;
}
// Keeping separate from validApiKeyLength because we may want to add more checks. Cloud also help with debugging user error submissions.
if (apiKey.value.length !== 64) {
return {
heading: 'Invalid API Key',
level: 'error',
message: 'Please sign out then sign back in to refresh your API key.',
ref: 'invalidApiKeyLength',
type: 'server',
};
}
if (!apiKey.value.startsWith('unupc_')) {
return {
heading: 'Invalid API Key Format',
level: 'error',
message: 'Please sign out then sign back in to refresh your API key.',
ref: 'invalidApiKeyFormat',
type: 'server',
};
}
return undefined;
});
watch(invalidApiKey, (newVal, oldVal) => {
if (oldVal && oldVal.ref) { errorsStore.removeErrorByRef(oldVal.ref); }
if (newVal) { errorsStore.setError(newVal); }
});
const tooManyDevices = computed((): Error | undefined => {
if (!config.value?.valid && config.value?.error === 'INVALID') {
return {
@@ -643,7 +619,8 @@ export const useServerStore = defineStore('server', () => {
});
const cloudError = computed((): Error | undefined => {
if (!cloud.value?.error) { return undefined; }
// if we're not registered then the cloud error should be ignored
if (!registered.value || !cloud.value?.error) { return; }
return {
actions: [
{
@@ -660,7 +637,7 @@ export const useServerStore = defineStore('server', () => {
debugServer: serverDebugPayload.value,
heading: 'Unraid Connect Error',
level: 'error',
message: cloud.value.error,
message: cloud.value?.error ?? '',
ref: 'cloudError',
type: 'unraidApiState',
};
@@ -676,29 +653,9 @@ export const useServerStore = defineStore('server', () => {
tooManyDevices.value,
pluginInstallFailed.value,
deprecatedUnraidSSL.value,
invalidApiKey.value,
cloudError.value,
].filter(Boolean);
});
/**
* Determines whether or not we start or stop the apollo client for unraid-api
*/
const registeredWithValidApiKey = computed(() => registered.value && !invalidApiKey.value);
watch(registeredWithValidApiKey, (newVal, oldVal) => {
if (oldVal) {
return unraidApiStore.closeUnraidApiClient();
}
if (newVal) {
// if this is just after sign in, let's delay the start by a few seconds to give unraid-api time to update
if (accountStore.accountActionType === 'signIn') {
return setTimeout(() => {
unraidApiStore.createApolloClient();
}, 2000);
} else {
return unraidApiStore.createApolloClient();
}
}
});
/**
* Actions
*/
@@ -814,7 +771,7 @@ export const useServerStore = defineStore('server', () => {
}, refreshTimeout);
}
// Extract the new values from the response
const newRegistered = fromApi && response?.data ? !!response.data.owner.username : response.registered;
const newRegistered = fromApi && response?.data ? response.data.owner.username !== 'root' : response.registered;
const newState = fromApi && response?.data ? response.data.vars.regState : response.state;
// Compare the new values to the old values
const registrationStatusChanged = oldRegistered !== newRegistered;
@@ -858,12 +815,10 @@ export const useServerStore = defineStore('server', () => {
// getters
authAction,
deprecatedUnraidSSL,
invalidApiKey,
isRemoteAccess,
keyActions,
pluginInstallFailed,
pluginOutdated,
registeredWithValidApiKey,
server,
serverAccountPayload,
serverPurchasePayload,

View File

@@ -64,11 +64,6 @@ export const useUnraidApiStore = defineStore('unraidApi', () => {
* Automatically called when an apiKey is set in the serverStore
*/
const createApolloClient = () => {
// sign out imminent, skipping createApolloClient
if (accountStore.accountActionType === 'signOut') {
return;
}
unraidApiStatus.value = 'connecting';
const headers = { 'x-api-key': serverStore.apiKey };