feat: settings through the API (#867)

* feat: api settings fully working
* refactor: nuxt config ConnectSettings

---------

Co-authored-by: Zack Spear <hi@zackspear.com>
This commit is contained in:
Eli Bosley
2024-07-03 13:38:09 -04:00
committed by GitHub
parent e9ff33d263
commit 29afe9b9e8
19 changed files with 442 additions and 66 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
[api]
version="3.4.0"
version="3.5.2+20f10951"
extraOrigins="https://google.com,https://test.com"
[local]
[notifier]
+3 -2
View File
@@ -1,5 +1,5 @@
[api]
version="3.4.0"
version="3.5.2+20f10951"
extraOrigins="https://google.com,https://test.com"
[local]
[notifier]
@@ -21,4 +21,5 @@ dynamicRemoteAccessType="DISABLED"
[upc]
apikey="unupc_fab6ff6ffe51040595c6d9ffb63a353ba16cc2ad7d93f813a2e80a5810"
[connectionStatus]
minigraph="PRE_INIT"
minigraph="ERROR_RETRYING"
upnpStatus="Success: UPNP Lease Renewed [4/24/2024 5:04:54 PM] Public Port [59138] Local Port [443]"
+1 -1
View File
@@ -68,7 +68,7 @@ const getRemoteAccessUrlsForAllowedOrigins = (
return [];
};
const getExtraOrigins = (): string[] => {
export const getExtraOrigins = (): string[] => {
const { extraOrigins } = getters.config().api;
if (extraOrigins) {
return extraOrigins
+10 -1
View File
@@ -2,7 +2,7 @@
import * as Types from '@app/graphql/generated/api/types';
import { z } from 'zod'
import { AllowedOriginInput, ApiKey, ApiKeyResponse, ArrayType, ArrayCapacity, ArrayDisk, ArrayDiskFsColor, ArrayDiskStatus, ArrayDiskType, ArrayPendingState, ArrayState, Baseboard, Capacity, Case, Cloud, CloudResponse, Config, ConfigErrorState, ConnectSignInInput, ConnectUserInfoInput, ContainerHostConfig, ContainerMount, ContainerPort, ContainerPortType, ContainerState, Devices, Disk, DiskFsType, DiskInterfaceType, DiskPartition, DiskSmartStatus, Display, DockerContainer, DockerNetwork, Flash, Gpu, Importance, Info, InfoApps, InfoCpu, InfoMemory, KeyFile, Me, MemoryFormFactor, MemoryLayout, MemoryType, MinigraphStatus, MinigraphqlResponse, Mount, Network, Notification, NotificationFilter, NotificationInput, NotificationType, Os, Owner, ParityCheck, Partition, Pci, ProfileModel, Registration, RegistrationState, RelayResponse, Server, ServerStatus, Service, SetupRemoteAccessInput, Share, System, Temperature, Theme, UnassignedDevice, Uptime, Usb, User, UserAccount, Vars, Versions, VmDomain, VmState, Vms, WAN_ACCESS_TYPE, WAN_FORWARD_TYPE, Welcome, addApiKeyInput, addUserInput, arrayDiskInput, authenticateInput, deleteUserInput, mdState, registrationType, updateApikeyInput, usersInput } from '@app/graphql/generated/api/types'
import { AllowedOriginInput, ApiKey, ApiKeyResponse, ArrayType, ArrayCapacity, ArrayDisk, ArrayDiskFsColor, ArrayDiskStatus, ArrayDiskType, ArrayPendingState, ArrayState, Baseboard, Capacity, Case, Cloud, CloudResponse, Config, ConfigErrorState, ConnectSignInInput, ConnectUserInfoInput, ContainerHostConfig, ContainerMount, ContainerPort, ContainerPortType, ContainerState, Devices, Disk, DiskFsType, DiskInterfaceType, DiskPartition, DiskSmartStatus, Display, DockerContainer, DockerNetwork, Flash, Gpu, Importance, Info, InfoApps, InfoCpu, InfoMemory, KeyFile, Me, MemoryFormFactor, MemoryLayout, MemoryType, MinigraphStatus, MinigraphqlResponse, Mount, Network, Notification, NotificationFilter, NotificationInput, NotificationType, Os, Owner, ParityCheck, Partition, Pci, ProfileModel, Registration, RegistrationState, RelayResponse, RemoteAccess, Server, ServerStatus, Service, SetupRemoteAccessInput, Share, System, Temperature, Theme, UnassignedDevice, Uptime, Usb, User, UserAccount, Vars, Versions, VmDomain, VmState, Vms, WAN_ACCESS_TYPE, WAN_FORWARD_TYPE, Welcome, addApiKeyInput, addUserInput, arrayDiskInput, authenticateInput, deleteUserInput, mdState, registrationType, updateApikeyInput, usersInput } from '@app/graphql/generated/api/types'
import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
type Properties<T> = Required<{
@@ -702,6 +702,15 @@ export function RelayResponseSchema(): z.ZodObject<Properties<RelayResponse>> {
})
}
export function RemoteAccessSchema(): z.ZodObject<Properties<RemoteAccess>> {
return z.object({
__typename: z.literal('RemoteAccess').optional(),
accessType: WAN_ACCESS_TYPESchema,
forwardType: WAN_FORWARD_TYPESchema.nullish(),
port: z.number().nullish()
})
}
export function ServerSchema(): z.ZodObject<Properties<Server>> {
return z.object({
__typename: z.literal('Server').optional(),
+21
View File
@@ -874,6 +874,7 @@ export type Query = {
dockerNetwork: DockerNetwork;
/** All Docker networks */
dockerNetworks: Array<Maybe<DockerNetwork>>;
extraAllowedOrigins: Array<Scalars['String']['output']>;
flash?: Maybe<Flash>;
info?: Maybe<Info>;
/** Current user account */
@@ -883,6 +884,7 @@ export type Query = {
owner?: Maybe<Owner>;
parityHistory?: Maybe<Array<Maybe<ParityCheck>>>;
registration?: Maybe<Registration>;
remoteAccess: RemoteAccess;
server?: Maybe<Server>;
servers: Array<Server>;
/** Network Shares */
@@ -990,6 +992,13 @@ export type RelayResponse = {
timeout?: Maybe<Scalars['String']['output']>;
};
export type RemoteAccess = {
__typename?: 'RemoteAccess';
accessType: WAN_ACCESS_TYPE;
forwardType?: Maybe<WAN_FORWARD_TYPE>;
port?: Maybe<Scalars['Port']['output']>;
};
export type Server = {
__typename?: 'Server';
apikey: Scalars['String']['output'];
@@ -1646,6 +1655,7 @@ export type ResolversTypes = ResolversObject<{
Registration: ResolverTypeWrapper<Registration>;
RegistrationState: RegistrationState;
RelayResponse: ResolverTypeWrapper<RelayResponse>;
RemoteAccess: ResolverTypeWrapper<RemoteAccess>;
Server: ResolverTypeWrapper<Server>;
ServerStatus: ServerStatus;
Service: ResolverTypeWrapper<Service>;
@@ -1739,6 +1749,7 @@ export type ResolversParentTypes = ResolversObject<{
Query: {};
Registration: Registration;
RelayResponse: RelayResponse;
RemoteAccess: RemoteAccess;
Server: Server;
Service: Service;
SetupRemoteAccessInput: SetupRemoteAccessInput;
@@ -2312,6 +2323,7 @@ export type QueryResolvers<ContextType = Context, ParentType extends ResolversPa
dockerContainers?: Resolver<Array<ResolversTypes['DockerContainer']>, ParentType, ContextType, Partial<QuerydockerContainersArgs>>;
dockerNetwork?: Resolver<ResolversTypes['DockerNetwork'], ParentType, ContextType, RequireFields<QuerydockerNetworkArgs, 'id'>>;
dockerNetworks?: Resolver<Array<Maybe<ResolversTypes['DockerNetwork']>>, ParentType, ContextType, Partial<QuerydockerNetworksArgs>>;
extraAllowedOrigins?: Resolver<Array<ResolversTypes['String']>, ParentType, ContextType>;
flash?: Resolver<Maybe<ResolversTypes['Flash']>, ParentType, ContextType>;
info?: Resolver<Maybe<ResolversTypes['Info']>, ParentType, ContextType>;
me?: Resolver<Maybe<ResolversTypes['Me']>, ParentType, ContextType>;
@@ -2320,6 +2332,7 @@ export type QueryResolvers<ContextType = Context, ParentType extends ResolversPa
owner?: Resolver<Maybe<ResolversTypes['Owner']>, ParentType, ContextType>;
parityHistory?: Resolver<Maybe<Array<Maybe<ResolversTypes['ParityCheck']>>>, ParentType, ContextType>;
registration?: Resolver<Maybe<ResolversTypes['Registration']>, ParentType, ContextType>;
remoteAccess?: Resolver<ResolversTypes['RemoteAccess'], ParentType, ContextType>;
server?: Resolver<Maybe<ResolversTypes['Server']>, ParentType, ContextType>;
servers?: Resolver<Array<ResolversTypes['Server']>, ParentType, ContextType>;
shares?: Resolver<Maybe<Array<Maybe<ResolversTypes['Share']>>>, ParentType, ContextType>;
@@ -2347,6 +2360,13 @@ export type RelayResponseResolvers<ContextType = Context, ParentType extends Res
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export type RemoteAccessResolvers<ContextType = Context, ParentType extends ResolversParentTypes['RemoteAccess'] = ResolversParentTypes['RemoteAccess']> = ResolversObject<{
accessType?: Resolver<ResolversTypes['WAN_ACCESS_TYPE'], ParentType, ContextType>;
forwardType?: Resolver<Maybe<ResolversTypes['WAN_FORWARD_TYPE']>, ParentType, ContextType>;
port?: Resolver<Maybe<ResolversTypes['Port']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export type ServerResolvers<ContextType = Context, ParentType extends ResolversParentTypes['Server'] = ResolversParentTypes['Server']> = ResolversObject<{
apikey?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
guid?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
@@ -2755,6 +2775,7 @@ export type Resolvers<ContextType = Context> = ResolversObject<{
Query?: QueryResolvers<ContextType>;
Registration?: RegistrationResolvers<ContextType>;
RelayResponse?: RelayResponseResolvers<ContextType>;
RemoteAccess?: RemoteAccessResolvers<ContextType>;
Server?: ServerResolvers<ContextType>;
Service?: ServiceResolvers<ContextType>;
Share?: ShareResolvers<ContextType>;
@@ -27,12 +27,23 @@ enum WAN_FORWARD_TYPE {
STATIC
}
type RemoteAccess {
accessType: WAN_ACCESS_TYPE!
forwardType: WAN_FORWARD_TYPE
port: Port
}
input SetupRemoteAccessInput {
accessType: WAN_ACCESS_TYPE!
forwardType: WAN_FORWARD_TYPE
port: Port
}
type Query {
remoteAccess: RemoteAccess!
extraAllowedOrigins: [String!]!
}
type Mutation {
connectSignIn(input: ConnectSignInInput!): Boolean!
connectSignOut: Boolean!
@@ -1,15 +1,22 @@
import { getAllowedOrigins } from '@app/common/allowed-origins';
import {
getAllowedOrigins,
getExtraOrigins,
} from '@app/common/allowed-origins';
import {
WAN_ACCESS_TYPE,
WAN_FORWARD_TYPE,
type ConnectSignInInput,
type SetupRemoteAccessInput,
} from '@app/graphql/generated/api/types';
import type { Cloud } from '@app/graphql/generated/api/types';
import type { Cloud, RemoteAccess } from '@app/graphql/generated/api/types';
import { connectSignIn } from '@app/graphql/resolvers/mutation/connect/connect-sign-in';
import { checkApi } from '@app/graphql/resolvers/query/cloud/check-api';
import { checkCloud } from '@app/graphql/resolvers/query/cloud/check-cloud';
import { checkMinigraphql } from '@app/graphql/resolvers/query/cloud/check-minigraphql';
import { DynamicRemoteAccessType } from '@app/remoteAccess/types';
import { setupRemoteAccessThunk } from '@app/store/actions/setup-remote-access';
import { store } from '@app/store/index';
import { getters, store } from '@app/store/index';
import { logoutUser } from '@app/store/modules/config';
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
import { UseRoles } from 'nest-access-control';
@@ -45,6 +52,44 @@ export class CloudResolver {
};
}
@Query()
@UseRoles({
resource: 'connect',
action: 'read',
possession: 'own',
})
public async remoteAccess(): Promise<RemoteAccess> {
const hasWanAccess = getters.config().remote.wanaccess === 'yes';
const dynamicRemoteAccessSettings: RemoteAccess = {
accessType: hasWanAccess
? getters.config().remote.dynamicRemoteAccessType !==
DynamicRemoteAccessType.DISABLED
? WAN_ACCESS_TYPE.DYNAMIC
: WAN_ACCESS_TYPE.ALWAYS
: WAN_ACCESS_TYPE.DISABLED,
forwardType: getters.config().remote.upnpEnabled
? WAN_FORWARD_TYPE.UPNP
: WAN_FORWARD_TYPE.STATIC,
port: getters.config().remote.wanport
? Number(getters.config().remote.wanport)
: null,
};
return dynamicRemoteAccessSettings;
}
@Query()
@UseRoles({
resource: 'connect',
action: 'read',
possession: 'own',
})
public async extraAllowedOrigins(): Promise<Array<string>> {
const extraOrigins = getExtraOrigins();
return extraOrigins;
}
@Mutation()
@UseRoles({
resource: 'connect',
+2 -1
View File
@@ -2,4 +2,5 @@ VITE_ACCOUNT=https://localhost:8008
VITE_CONNECT=https://connect.myunraid.net
VITE_UNRAID_NET=https://unraid.ddev.site
VITE_CALLBACK_KEY=aNotSoSecretKeyUsedToObfuscateQueryParams
VITE_ALLOW_CONSOLE_LOGS=false
VITE_ALLOW_CONSOLE_LOGS=false
VITE_WEBGUI=http://localhost
@@ -0,0 +1,47 @@
<script lang="ts" setup>
import { useUnraidApiSettingsStore } from '~/store/unraidApiSettings';
const apiSettingsStore = useUnraidApiSettingsStore();
const originsText = ref<string>('');
const errors = ref<string[]>([]);
onMounted(async () => {
const allowedOriginsSettings = await apiSettingsStore.getAllowedOrigins();
originsText.value = allowedOriginsSettings.join(', ');
});
const origins = computed<string[]>(() => {
console.log('originsText.value: ' + originsText.value);
const newOrigins: string[] = [];
if (originsText.value) {
originsText.value.split(',').forEach((origin) => {
try {
const newUrl = new URL(origin.trim());
newOrigins.push(newUrl.toString());
} catch (e) {
errors.value.push(`Invalid origin: ${origin}`);
}
});
}
return newOrigins;
});
const setAllowedOrigins = () => {
apiSettingsStore.setAllowedOrigins(origins.value);
};
</script>
<template>
<div class="flex flex-col">
<h2>Setup Allowed Origins</h2>
<input v-model="originsText" type="text" placeholder="Input Comma Separated List of URLs">
<button type="button" @click="setAllowedOrigins()">
Set Allowed Origins
</button>
<div v-for="(error, index) of errors" :key="index">
<p>{{ error }}</p>
</div>
</div>
</template>
@@ -0,0 +1,17 @@
<script lang="ts" setup>
import 'tailwindcss/tailwind.css';
import '~/assets/main.css';
// import { useI18n } from 'vue-i18n';
// const { t } = useI18n();
</script>
<template>
<AuthCe />
<!-- @todo: flashback up -->
<WanIpCheckCe />
<ConnectSettingsRemoteAccess />
<ConnectSettingsAllowedOrigins />
<DownloadApiLogsCe />
</template>
@@ -0,0 +1,60 @@
<script lang="ts" setup>
import { WAN_ACCESS_TYPE, WAN_FORWARD_TYPE } from '~/composables/gql/graphql';
import { useUnraidApiSettingsStore } from '~/store/unraidApiSettings';
const apiSettingsStore = useUnraidApiSettingsStore();
const accessType = ref<WAN_ACCESS_TYPE>(WAN_ACCESS_TYPE.Disabled);
const forwardType = ref<WAN_FORWARD_TYPE | null>(null);
const port = ref<number | null>(null);
onMounted(async () => {
const remoteAccessSettings = await apiSettingsStore.getRemoteAccess();
accessType.value =
remoteAccessSettings?.accessType ?? WAN_ACCESS_TYPE.Disabled;
forwardType.value = remoteAccessSettings?.forwardType ?? null;
port.value = remoteAccessSettings?.port ?? null;
});
const setRemoteAccess = () => {
apiSettingsStore.setupRemoteAccess({
accessType: accessType.value,
...(forwardType.value ? { forwardType: forwardType.value } : {}),
...(port.value ? { port: port.value } : {}),
});
};
watch(accessType, (newVal) => {
if (newVal !== WAN_ACCESS_TYPE.Disabled) {
forwardType.value = WAN_FORWARD_TYPE.Static;
}
});
</script>
<template>
<div class="flex flex-col">
<h2>Setup Remote Access</h2>
<label for="forwardType">Forward Type</label>
<select id="forwardType" v-model="accessType">
<option v-for="(val, index) in Object.values(WAN_ACCESS_TYPE)" :key="index" :value="val">
{{ val }}
</option>
</select>
<template v-if="accessType !== WAN_ACCESS_TYPE.Disabled">
<label for="forwardType">Forward Type</label>
<select id="forwardType" v-model="forwardType">
<option v-for="(val, index) in Object.values(WAN_FORWARD_TYPE)" :key="index" :value="val">
{{ val }}
</option>
</select>
</template>
<template v-if="forwardType === WAN_FORWARD_TYPE.Static && accessType !== WAN_ACCESS_TYPE.Disabled">
<label for="port">Port</label>
<input id="port" v-model="port" type="number">
</template>
<button @click="setRemoteAccess">
Save
</button>
</div>
</template>
+20
View File
@@ -17,6 +17,10 @@ const documents = {
"\n mutation SignOut {\n connectSignOut\n }\n": types.SignOutDocument,
"\n fragment PartialCloud on 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": types.PartialCloudFragmentDoc,
"\n query serverState {\n cloud {\n ...PartialCloud\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 updateExpiration\n }\n vars {\n regGen\n regState\n configError\n configValid\n }\n }\n": types.serverStateDocument,
"\n query getExtraAllowedOrigins {\n extraAllowedOrigins\n }\n": types.getExtraAllowedOriginsDocument,
"\n query getRemoteAccess {\n remoteAccess {\n accessType\n forwardType\n port\n }\n }\n": types.getRemoteAccessDocument,
"\n mutation setAdditionalAllowedOrigins($input: AllowedOriginInput!) {\n setAdditionalAllowedOrigins(input: $input)\n }\n": types.setAdditionalAllowedOriginsDocument,
"\n mutation setupRemoteAccess($input: SetupRemoteAccessInput!) {\n setupRemoteAccess(input: $input)\n }\n": types.setupRemoteAccessDocument,
};
/**
@@ -49,6 +53,22 @@ export function graphql(source: "\n fragment PartialCloud on Cloud {\n error
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n query serverState {\n cloud {\n ...PartialCloud\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 updateExpiration\n }\n vars {\n regGen\n regState\n configError\n configValid\n }\n }\n"): (typeof documents)["\n query serverState {\n cloud {\n ...PartialCloud\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 updateExpiration\n }\n vars {\n regGen\n regState\n configError\n configValid\n }\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 query getExtraAllowedOrigins {\n extraAllowedOrigins\n }\n"): (typeof documents)["\n query getExtraAllowedOrigins {\n extraAllowedOrigins\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 query getRemoteAccess {\n remoteAccess {\n accessType\n forwardType\n port\n }\n }\n"): (typeof documents)["\n query getRemoteAccess {\n remoteAccess {\n accessType\n forwardType\n port\n }\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 setAdditionalAllowedOrigins($input: AllowedOriginInput!) {\n setAdditionalAllowedOrigins(input: $input)\n }\n"): (typeof documents)["\n mutation setAdditionalAllowedOrigins($input: AllowedOriginInput!) {\n setAdditionalAllowedOrigins(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 setupRemoteAccess($input: SetupRemoteAccessInput!) {\n setupRemoteAccess(input: $input)\n }\n"): (typeof documents)["\n mutation setupRemoteAccess($input: SetupRemoteAccessInput!) {\n setupRemoteAccess(input: $input)\n }\n"];
export function graphql(source: string) {
return (documents as any)[source] ?? {};
+38 -1
View File
@@ -878,6 +878,7 @@ export type Query = {
dockerNetwork: DockerNetwork;
/** All Docker networks */
dockerNetworks: Array<Maybe<DockerNetwork>>;
extraAllowedOrigins: Array<Scalars['String']['output']>;
flash?: Maybe<Flash>;
info?: Maybe<Info>;
/** Current user account */
@@ -887,6 +888,7 @@ export type Query = {
owner?: Maybe<Owner>;
parityHistory?: Maybe<Array<Maybe<ParityCheck>>>;
registration?: Maybe<Registration>;
remoteAccess: RemoteAccess;
server?: Maybe<Server>;
servers: Array<Server>;
/** Network Shares */
@@ -994,6 +996,13 @@ export type RelayResponse = {
timeout?: Maybe<Scalars['String']['output']>;
};
export type RemoteAccess = {
__typename?: 'RemoteAccess';
accessType: WAN_ACCESS_TYPE;
forwardType?: Maybe<WAN_FORWARD_TYPE>;
port?: Maybe<Scalars['Port']['output']>;
};
export type Server = {
__typename?: 'Server';
apikey: Scalars['String']['output'];
@@ -1523,7 +1532,35 @@ export type serverStateQuery = { __typename?: 'Query', cloud?: (
& { ' $fragmentRefs'?: { 'PartialCloudFragment': PartialCloudFragment } }
) | 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, updateExpiration?: 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 type getExtraAllowedOriginsQueryVariables = Exact<{ [key: string]: never; }>;
export type getExtraAllowedOriginsQuery = { __typename?: 'Query', extraAllowedOrigins: Array<string> };
export type getRemoteAccessQueryVariables = Exact<{ [key: string]: never; }>;
export type getRemoteAccessQuery = { __typename?: 'Query', remoteAccess: { __typename?: 'RemoteAccess', accessType: WAN_ACCESS_TYPE, forwardType?: WAN_FORWARD_TYPE | null, port?: number | null } };
export type setAdditionalAllowedOriginsMutationVariables = Exact<{
input: AllowedOriginInput;
}>;
export type setAdditionalAllowedOriginsMutation = { __typename?: 'Mutation', setAdditionalAllowedOrigins: Array<string> };
export type setupRemoteAccessMutationVariables = Exact<{
input: SetupRemoteAccessInput;
}>;
export type setupRemoteAccessMutation = { __typename?: 'Mutation', setupRemoteAccess: boolean };
export const PartialCloudFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PartialCloud"},"typeCondition":{"kind":"NamedType","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"}}]}}]}}]} as unknown as DocumentNode<PartialCloudFragment, unknown>;
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":"FragmentSpread","name":{"kind":"Name","value":"PartialCloud"}}]}},{"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":"updateExpiration"}}]}},{"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"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PartialCloud"},"typeCondition":{"kind":"NamedType","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"}}]}}]}}]} as unknown as DocumentNode<serverStateQuery, serverStateQueryVariables>;
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":"FragmentSpread","name":{"kind":"Name","value":"PartialCloud"}}]}},{"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":"updateExpiration"}}]}},{"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"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PartialCloud"},"typeCondition":{"kind":"NamedType","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"}}]}}]}}]} as unknown as DocumentNode<serverStateQuery, serverStateQueryVariables>;
export const getExtraAllowedOriginsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"getExtraAllowedOrigins"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"extraAllowedOrigins"}}]}}]} as unknown as DocumentNode<getExtraAllowedOriginsQuery, getExtraAllowedOriginsQueryVariables>;
export const getRemoteAccessDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"getRemoteAccess"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"remoteAccess"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"accessType"}},{"kind":"Field","name":{"kind":"Name","value":"forwardType"}},{"kind":"Field","name":{"kind":"Name","value":"port"}}]}}]}}]} as unknown as DocumentNode<getRemoteAccessQuery, getRemoteAccessQueryVariables>;
export const setAdditionalAllowedOriginsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"setAdditionalAllowedOrigins"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"AllowedOriginInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"setAdditionalAllowedOrigins"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode<setAdditionalAllowedOriginsMutation, setAdditionalAllowedOriginsMutationVariables>;
export const setupRemoteAccessDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"setupRemoteAccess"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SetupRemoteAccessInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"setupRemoteAccess"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode<setupRemoteAccessMutation, setupRemoteAccessMutationVariables>;
+5
View File
@@ -54,6 +54,7 @@ export default defineNuxtConfig({
],
components: [
{ path: '~/components/Brand', prefix: 'Brand' },
{ path: '~/components/ConnectSettings', prefix: 'ConnectSettings' },
{ path: '~/components/Ui', prefix: 'Ui' },
{ path: '~/components/UserProfile', prefix: 'Upc' },
{ path: '~/components/UpdateOs', prefix: 'UpdateOs' },
@@ -88,6 +89,10 @@ export default defineNuxtConfig({
name: 'UnraidAuth',
path: '@/components/Auth.ce',
},
{
name: 'UnraidConnectSettings',
path: '@/components/ConnectSettings/ConnectSettings.ce',
},
{
name: 'UnraidDownloadApiLogs',
path: '@/components/DownloadApiLogs.ce',
+12 -5
View File
@@ -72,21 +72,28 @@ onMounted(() => {
<UserProfileCe :server="serverState" />
</header>
<hr class="border-black dark:border-white">
<h3 class="text-lg font-semibold font-mono">
ConnectSettingsCe
</h3>
<ConnectSettingsCe />
<hr class="border-black dark:border-white">
<!-- <h3 class="text-lg font-semibold font-mono">
DownloadApiLogsCe
</h3>
<DownloadApiLogsCe />
<hr class="border-black dark:border-white">
<h3 class="text-lg font-semibold font-mono">
<hr class="border-black dark:border-white"> -->
<!-- <h3 class="text-lg font-semibold font-mono">
AuthCe
</h3>
<AuthCe />
<hr class="border-black dark:border-white">
<h3 class="text-lg font-semibold font-mono">
<hr class="border-black dark:border-white"> -->
<!-- <h3 class="text-lg font-semibold font-mono">
WanIpCheckCe
</h3>
<WanIpCheckCe php-wan-ip="47.184.85.45" />
<hr class="border-black dark:border-white">
<hr class="border-black dark:border-white"> -->
<h3 class="text-lg font-semibold font-mono">
UpdateOsCe
</h3>
+13 -2
View File
@@ -9,7 +9,9 @@ onBeforeMount(() => {
<template>
<client-only>
<unraid-i18n-host class="flex flex-col gap-6 p-6 mx-auto text-black bg-white dark:text-white dark:bg-black">
<unraid-i18n-host
class="flex flex-col gap-6 p-6 mx-auto text-black bg-white dark:text-white dark:bg-black"
>
<h2 class="text-xl font-semibold font-mono">
Web Components
</h2>
@@ -25,6 +27,12 @@ onBeforeMount(() => {
</div>
<unraid-user-profile :server="JSON.stringify(serverState)" />
</header>
<hr class="border-black dark:border-white" >
<h3 class="text-lg font-semibold font-mono">
ConnectSettingsCe
</h3>
<ConnectSettingsCe />
<hr class="border-black dark:border-white">
<h3 class="text-lg font-semibold font-mono">
DownloadApiLogsCe
@@ -54,7 +62,10 @@ onBeforeMount(() => {
<h3 class="text-lg font-semibold font-mono">
DowngradeOsCe
</h3>
<unraid-downgrade-os restore-release-date="2022-10-10" restore-version="6.11.2" />
<unraid-downgrade-os
restore-release-date="2022-10-10"
restore-version="6.11.2"
/>
<hr class="border-black dark:border-white">
<h3 class="text-lg font-semibold font-mono">
RegistrationCe
+49 -49
View File
@@ -9,25 +9,25 @@ import {
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";
} from '@apollo/client';
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 { 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";
} 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
@@ -36,12 +36,12 @@ 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";
'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();
const unraidApiClient = ref<ApolloClientType<InMemoryCacheType> | null>(null);
@@ -50,21 +50,21 @@ export const useUnraidApiStore = defineStore("unraidApi", () => {
const apiResponse = serverStore.fetchServerFromApi();
if (apiResponse) {
// we have a response, so we're online
unraidApiStatus.value = "online";
unraidApiStatus.value = 'online';
}
}
});
// const unraidApiErrors = ref<any[]>([]);
const unraidApiStatus = ref<
"connecting" | "offline" | "online" | "restarting"
>("offline");
'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" ||
unraidApiStatus.value !== 'offline' ||
!connectPluginInstalled ||
stateDataError
) {
@@ -74,7 +74,7 @@ export const useUnraidApiStore = defineStore("unraidApi", () => {
click: () => restartUnraidApiClient(),
emphasize: true,
icon: ArrowPathIcon,
text: "Restart unraid-api",
text: 'Restart unraid-api',
};
});
@@ -83,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(),
@@ -106,13 +106,13 @@ 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);
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";
if (errorMsg && errorMsg.includes('offline')) {
unraidApiStatus.value = 'offline';
// attempt to automatically restart the unraid-api
if (unraidApiRestartAction) {
restartUnraidApiClient();
@@ -122,16 +122,16 @@ export const useUnraidApiStore = defineStore("unraidApi", () => {
prioritizeCorsError.value = true;
const msg = `<p>The CORS policy for the unraid-api does not allow access from the specified origin.</p><p>If you are using a reverse proxy, you need to copy your origin <strong class="font-mono"><em>${window.location.origin}</em></strong> and paste it into the "Extra Origins" list in the Connect settings.</p>`;
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',
},
],
});
@@ -144,10 +144,10 @@ export const useUnraidApiStore = defineStore("unraidApi", () => {
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")
typeof msg === 'string' &&
msg.includes('Unexpected token < in JSON at position 0')
) {
return "Unraid API • CORS Error";
return 'Unraid API • CORS Error';
}
return msg;
}
@@ -176,8 +176,8 @@ export const useUnraidApiStore = defineStore("unraidApi", () => {
({ query }: any) => {
const definition: Definintion = getMainDefinition(query);
return (
definition.kind === "OperationDefinition" &&
definition.operation === "subscription"
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
wsLink,
@@ -190,13 +190,13 @@ export const useUnraidApiStore = defineStore("unraidApi", () => {
*/
const additiveLink = from([errorLink, retryLink, splitLinks]);
const apolloClient = new ApolloClient({
const client: ApolloClientType<InMemoryCacheType> = new ApolloClient({
link: additiveLink,
cache: new InMemoryCache(),
}) as ApolloClientType<InMemoryCacheType>;
unraidApiClient.value = apolloClient;
});
unraidApiClient.value = client;
provideApolloClient(apolloClient);
provideApolloClient(client);
};
/**
* Automatically called when an apiKey is unset in the serverStore
@@ -217,8 +217,8 @@ export const useUnraidApiStore = defineStore("unraidApi", () => {
* 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,
@@ -230,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',
});
}
};
+29
View File
@@ -0,0 +1,29 @@
import { graphql } from '~/composables/gql/gql';
export const GET_ALLOWED_ORIGINS = graphql(/* GraphQL */ `
query getExtraAllowedOrigins {
extraAllowedOrigins
}
`);
export const GET_REMOTE_ACCESS = graphql(/* GraphQL */ `
query getRemoteAccess {
remoteAccess {
accessType
forwardType
port
}
}
`);
export const SET_ADDITIONAL_ALLOWED_ORIGINS = graphql(/* GraphQL */ `
mutation setAdditionalAllowedOrigins($input: AllowedOriginInput!) {
setAdditionalAllowedOrigins(input: $input)
}
`);
export const SETUP_REMOTE_ACCESS = graphql(/* GraphQL */ `
mutation setupRemoteAccess($input: SetupRemoteAccessInput!) {
setupRemoteAccess(input: $input)
}
`);
+55
View File
@@ -0,0 +1,55 @@
import type { SetupRemoteAccessInput } from '~/composables/gql/graphql';
import { useUnraidApiStore } from '~/store/unraidApi';
import {
GET_ALLOWED_ORIGINS,
GET_REMOTE_ACCESS,
SETUP_REMOTE_ACCESS,
SET_ADDITIONAL_ALLOWED_ORIGINS,
} from '~/store/unraidApiSettings.fragment';
export const useUnraidApiSettingsStore = defineStore(
'unraidApiSettings',
() => {
const { unraidApiClient } = toRefs(useUnraidApiStore());
const getAllowedOrigins = async () => {
const origins = await unraidApiClient.value?.query({
query: GET_ALLOWED_ORIGINS,
});
return origins?.data?.extraAllowedOrigins ?? [];
};
const setAllowedOrigins = async (origins: string[]) => {
const newOrigins = await unraidApiClient.value?.mutate({
mutation: SET_ADDITIONAL_ALLOWED_ORIGINS,
variables: { input: { origins } },
});
return newOrigins?.data?.setAdditionalAllowedOrigins;
};
const getRemoteAccess = async () => {
const remoteAccess = await unraidApiClient.value?.query({
query: GET_REMOTE_ACCESS,
});
return remoteAccess?.data?.remoteAccess;
};
const setupRemoteAccess = async (input: SetupRemoteAccessInput) => {
const response = await unraidApiClient.value?.mutate({
mutation: SETUP_REMOTE_ACCESS,
variables: { input },
});
return response?.data?.setupRemoteAccess;
};
return {
getAllowedOrigins,
setAllowedOrigins,
getRemoteAccess,
setupRemoteAccess,
};
}
);