feat: disable all legacy dashboard and network logic

This commit is contained in:
Eli Bosley
2024-08-20 14:44:40 -04:00
parent 6072387c37
commit 3a843b6e16
26 changed files with 990 additions and 278 deletions

View File

@@ -1,5 +1,5 @@
[api]
version="3.8.1+fc0d7f4a"
version="3.8.1+7405443f"
extraOrigins="https://google.com,https://test.com"
[local]
[notifier]

View File

@@ -1,5 +1,5 @@
[api]
version="3.8.1+fc0d7f4a"
version="3.8.1+7405443f"
extraOrigins="https://google.com,https://test.com"
[local]
[notifier]
@@ -16,9 +16,9 @@ regWizTime="1611175408732_0951-1653-3509-FBA155FA23C0"
idtoken=""
accesstoken=""
refreshtoken=""
allowedOrigins="/var/run/unraid-notifications.sock, /var/run/unraid-php.sock, /var/run/unraid-cli.sock, http://localhost:8080, https://localhost:4443, https://tower.local:4443, https://192.168.1.150:4443, https://tower:4443, https://192-168-1-150.thisisfourtyrandomcharacters012345678900.myunraid.net:4443, https://85-121-123-122.thisisfourtyrandomcharacters012345678900.myunraid.net:8443, https://10-252-0-1.hash.myunraid.net:4443, https://10-252-1-1.hash.myunraid.net:4443, https://10-253-3-1.hash.myunraid.net:4443, https://10-253-4-1.hash.myunraid.net:4443, https://10-253-5-1.hash.myunraid.net:4443, https://10-100-0-1.hash.myunraid.net:4443, https://10-123-1-2.hash.myunraid.net:4443, https://221-123-121-112.hash.myunraid.net:4443, https://google.com, https://test.com, https://connect.myunraid.net, https://connect-staging.myunraid.net, https://dev-my.myunraid.net:4000, https://studio.apollographql.com"
allowedOrigins="/var/run/unraid-notifications.sock, /var/run/unraid-php.sock, /var/run/unraid-cli.sock, http://localhost:8080, https://localhost:4443, https://tower.local:4443, https://192.168.1.150:4443, https://tower:4443, https://192-168-1-150.thisisfourtyrandomcharacters012345678900.myunraid.net:4443, https://85-121-123-122.thisisfourtyrandomcharacters012345678900.myunraid.net:8443, https://10-252-0-1.hash.myunraid.net:4443, https://10-252-1-1.hash.myunraid.net:4443, https://10-253-3-1.hash.myunraid.net:4443, https://10-253-4-1.hash.myunraid.net:4443, https://10-253-5-1.hash.myunraid.net:4443, https://10-100-0-1.hash.myunraid.net:4443, https://10-100-0-2.hash.myunraid.net:4443, https://10-123-1-2.hash.myunraid.net:4443, https://221-123-121-112.hash.myunraid.net:4443, https://google.com, https://test.com, https://connect.myunraid.net, https://connect-staging.myunraid.net, https://dev-my.myunraid.net:4000, https://studio.apollographql.com"
dynamicRemoteAccessType="DISABLED"
[upc]
apikey="unupc_fab6ff6ffe51040595c6d9ffb63a353ba16cc2ad7d93f813a2e80a5810"
[connectionStatus]
minigraph="PRE_INIT"
minigraph="CONNECTED"

View File

@@ -27,6 +27,7 @@ x-volumes: &volumes
networks:
mothership_default:
external: true
services:
dev:

View File

@@ -29,7 +29,7 @@
"build:docker": "./scripts/dc.sh run --rm builder",
"build-pkg": "./scripts/build.mjs",
"codegen": "MOTHERSHIP_GRAPHQL_LINK='https://staging.mothership.unraid.net/ws' graphql-codegen --config codegen.yml -r dotenv/config './.env.staging'",
"codegen:watch": "DOTENV_CONFIG_PATH='./.env.staging' graphql-codegen-esm --config codegen.yml --watch -r dotenv/config",
"codegen:watch": "DOTENV_CONFIG_PATH='./.env.staging' graphql-codegen --config codegen.yml --watch -r dotenv/config",
"codegen:local": "NODE_TLS_REJECT_UNAUTHORIZED=0 MOTHERSHIP_GRAPHQL_LINK='https://mothership.localhost/ws' graphql-codegen-esm --config codegen.yml --watch",
"tsc": "tsc --noEmit",
"lint": "DEBUG=eslint:cli-engine eslint . --config .eslintrc.cjs",

View File

@@ -0,0 +1,154 @@
import { ConnectListAllDomainsFlags } from '@vmngr/libvirt';
import { getHypervisor } from '@app/core/utils/vms/get-hypervisor';
import { getUnraidVersion } from '@app/common/dashboard/get-unraid-version';
import { bootTimestamp } from '@app/common/dashboard/boot-timestamp';
import { getters, store } from '@app/store';
import { API_VERSION } from '@app/environment';
import { DynamicRemoteAccessType } from '@app/remoteAccess/types';
import {
DashboardService,
Dashboard,
DashboardArray,
ArrayState,
} from '@app/graphql/generated/api/types';
import convert from 'convert';
import { getArrayData } from '@app/core/modules/array/get-array-data';
import { hostname } from 'os';
const getVmSummary = async (): Promise<Dashboard['vms']> => {
try {
const hypervisor = await getHypervisor();
if (!hypervisor) {
return {
installed: 0,
started: 0,
};
}
const activeDomains = (await hypervisor.connectListAllDomains(
ConnectListAllDomainsFlags.ACTIVE
)) as unknown[];
const inactiveDomains = (await hypervisor.connectListAllDomains(
ConnectListAllDomainsFlags.INACTIVE
)) as unknown[];
return {
installed: activeDomains.length + inactiveDomains.length,
started: activeDomains.length,
};
} catch {
return {
installed: 0,
started: 0,
};
}
};
const getDynamicRemoteAccessService = (): DashboardService | null => {
const { config, dynamicRemoteAccess } = store.getState();
const enabledStatus = config.remote.dynamicRemoteAccessType;
return {
name: 'dynamic-remote-access',
online: enabledStatus !== DynamicRemoteAccessType.DISABLED,
version: dynamicRemoteAccess.runningType,
uptime: {
timestamp: bootTimestamp.toISOString(),
},
};
};
const services = (): Dashboard['services'] => {
const dynamicRemoteAccess = getDynamicRemoteAccessService();
return [
{
name: 'unraid-api',
online: true,
uptime: {
timestamp: bootTimestamp.toISOString(),
},
version: API_VERSION,
},
...(dynamicRemoteAccess ? [dynamicRemoteAccess] : []),
];
};
const KBToB = (kb: number | string): number =>
convert(Number(kb), 'KB').to('B');
export const getArray = (): DashboardArray => {
const array = getArrayData();
if (!array) {
return {
state: ArrayState.STOPPED,
capacity: {
bytes: { free: 0, used: 0, total: 0 },
disks: { free: '0', used: '0', total: '0' },
kilobytes: { free: '0', used: '0', total: '0' },
},
};
}
return {
state: array.state ?? ArrayState.STOPPED,
capacity: array.capacity,
};
};
const getData = async (): Promise<Dashboard> => {
const emhttp = getters.emhttp();
const docker = getters.docker();
return {
id: hostname() ?? 'unraid',
vars: {
regState: emhttp.var.regState,
regTy: emhttp.var.regTy,
flashGuid: emhttp.var.flashGuid,
serverName: emhttp.var.name,
serverDescription: emhttp.var.comment,
},
apps: {
installed: docker.installed ?? 0,
started: docker.running ?? 0,
},
versions: {
unraid: await getUnraidVersion(),
},
os: {
hostname: emhttp.var.name,
uptime: bootTimestamp.toISOString(),
},
vms: await getVmSummary(),
array: getArray(),
services: services(),
display: {
case: {
url: '',
icon: '',
error: '',
base64: '',
},
},
config: emhttp.var.configValid
? { valid: true }
: {
valid: false,
error:
{
error: 'UNKNOWN_ERROR',
invalid: 'INVALID',
nokeyserver: 'NO_KEY_SERVER',
withdrawn: 'WITHDRAWN',
}[emhttp.var.configState] ?? 'UNKNOWN_ERROR',
},
};
};
/**
* Provides a way to get dashboard data from the GraphQL client without the need for publishing to mothership
* @returns Dashboard data
*/
export const dashboardDataServer = async (): Promise<Dashboard> => {
return await getData();
};

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, 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 { AccessUrl, AccessUrlInput, AllowedOriginInput, ApiKey, ApiKeyResponse, ArrayType, ArrayCapacity, ArrayCapacityBytes, ArrayDisk, ArrayDiskFsColor, ArrayDiskStatus, ArrayDiskType, ArrayPendingState, ArrayState, Baseboard, Capacity, Case, Cloud, CloudResponse, Config, ConfigErrorState, ConnectSignInInput, ConnectUserInfoInput, ContainerHostConfig, ContainerMount, ContainerPort, ContainerPortType, ContainerState, Dashboard, DashboardApps, DashboardArray, DashboardCase, DashboardConfig, DashboardDisplay, DashboardOs, DashboardService, DashboardServiceInput, DashboardServiceUptime, DashboardServiceUptimeInput, DashboardTwoFactor, DashboardTwoFactorLocal, DashboardTwoFactorRemote, DashboardVars, DashboardVersions, DashboardVms, 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, NetworkInput, Notification, NotificationFilter, NotificationInput, NotificationType, Os, Owner, ParityCheck, Partition, Pci, ProfileModel, Registration, RegistrationState, RelayResponse, RemoteAccess, Server, ServerStatus, Service, SetupRemoteAccessInput, Share, System, Temperature, Theme, URL_TYPE, 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<{
@@ -55,6 +55,8 @@ export const TemperatureSchema = z.nativeEnum(Temperature);
export const ThemeSchema = z.nativeEnum(Theme);
export const URL_TYPESchema = z.nativeEnum(URL_TYPE);
export const VmStateSchema = z.nativeEnum(VmState);
export const WAN_ACCESS_TYPESchema = z.nativeEnum(WAN_ACCESS_TYPE);
@@ -65,6 +67,25 @@ export const mdStateSchema = z.nativeEnum(mdState);
export const registrationTypeSchema = z.nativeEnum(registrationType);
export function AccessUrlSchema(): z.ZodObject<Properties<AccessUrl>> {
return z.object({
__typename: z.literal('AccessUrl').optional(),
ipv4: definedNonNullAnySchema.nullish(),
ipv6: definedNonNullAnySchema.nullish(),
name: z.string().nullish(),
type: URL_TYPESchema
})
}
export function AccessUrlInputSchema(): z.ZodObject<Properties<AccessUrlInput>> {
return z.object({
ipv4: definedNonNullAnySchema.nullish(),
ipv6: definedNonNullAnySchema.nullish(),
name: z.string().nullish(),
type: URL_TYPESchema
})
}
export function AllowedOriginInputSchema(): z.ZodObject<Properties<AllowedOriginInput>> {
return z.object({
origins: z.array(z.string())
@@ -107,11 +128,21 @@ export function ArrayTypeSchema(): z.ZodObject<Properties<ArrayType>> {
export function ArrayCapacitySchema(): z.ZodObject<Properties<ArrayCapacity>> {
return z.object({
__typename: z.literal('ArrayCapacity').optional(),
bytes: ArrayCapacityBytesSchema().nullish(),
disks: CapacitySchema(),
kilobytes: CapacitySchema()
})
}
export function ArrayCapacityBytesSchema(): z.ZodObject<Properties<ArrayCapacityBytes>> {
return z.object({
__typename: z.literal('ArrayCapacityBytes').optional(),
free: z.number().nullish(),
total: z.number().nullish(),
used: z.number().nullish()
})
}
export function ArrayDiskSchema(): z.ZodObject<Properties<ArrayDisk>> {
return z.object({
__typename: z.literal('ArrayDisk').optional(),
@@ -248,6 +279,155 @@ export function ContainerPortSchema(): z.ZodObject<Properties<ContainerPort>> {
})
}
export function DashboardSchema(): z.ZodObject<Properties<Dashboard>> {
return z.object({
__typename: z.literal('Dashboard').optional(),
apps: DashboardAppsSchema().nullish(),
array: DashboardArraySchema().nullish(),
config: DashboardConfigSchema().nullish(),
display: DashboardDisplaySchema().nullish(),
id: z.string(),
lastPublish: z.string().nullish(),
network: NetworkSchema().nullish(),
online: z.boolean().nullish(),
os: DashboardOsSchema().nullish(),
services: z.array(DashboardServiceSchema().nullable()).nullish(),
twoFactor: DashboardTwoFactorSchema().nullish(),
vars: DashboardVarsSchema().nullish(),
versions: DashboardVersionsSchema().nullish(),
vms: DashboardVmsSchema().nullish()
})
}
export function DashboardAppsSchema(): z.ZodObject<Properties<DashboardApps>> {
return z.object({
__typename: z.literal('DashboardApps').optional(),
installed: z.number().nullish(),
started: z.number().nullish()
})
}
export function DashboardArraySchema(): z.ZodObject<Properties<DashboardArray>> {
return z.object({
__typename: z.literal('DashboardArray').optional(),
capacity: ArrayCapacitySchema().nullish(),
state: z.string().nullish()
})
}
export function DashboardCaseSchema(): z.ZodObject<Properties<DashboardCase>> {
return z.object({
__typename: z.literal('DashboardCase').optional(),
base64: z.string().nullish(),
error: z.string().nullish(),
icon: z.string().nullish(),
url: z.string().nullish()
})
}
export function DashboardConfigSchema(): z.ZodObject<Properties<DashboardConfig>> {
return z.object({
__typename: z.literal('DashboardConfig').optional(),
error: z.string().nullish(),
valid: z.boolean().nullish()
})
}
export function DashboardDisplaySchema(): z.ZodObject<Properties<DashboardDisplay>> {
return z.object({
__typename: z.literal('DashboardDisplay').optional(),
case: DashboardCaseSchema().nullish()
})
}
export function DashboardOsSchema(): z.ZodObject<Properties<DashboardOs>> {
return z.object({
__typename: z.literal('DashboardOs').optional(),
hostname: z.string().nullish(),
uptime: z.string().nullish()
})
}
export function DashboardServiceSchema(): z.ZodObject<Properties<DashboardService>> {
return z.object({
__typename: z.literal('DashboardService').optional(),
name: z.string().nullish(),
online: z.boolean().nullish(),
uptime: DashboardServiceUptimeSchema().nullish(),
version: z.string().nullish()
})
}
export function DashboardServiceInputSchema(): z.ZodObject<Properties<DashboardServiceInput>> {
return z.object({
name: z.string(),
online: z.boolean(),
uptime: z.lazy(() => DashboardServiceUptimeInputSchema().nullish()),
version: z.string()
})
}
export function DashboardServiceUptimeSchema(): z.ZodObject<Properties<DashboardServiceUptime>> {
return z.object({
__typename: z.literal('DashboardServiceUptime').optional(),
timestamp: z.string().nullish()
})
}
export function DashboardServiceUptimeInputSchema(): z.ZodObject<Properties<DashboardServiceUptimeInput>> {
return z.object({
timestamp: z.string()
})
}
export function DashboardTwoFactorSchema(): z.ZodObject<Properties<DashboardTwoFactor>> {
return z.object({
__typename: z.literal('DashboardTwoFactor').optional(),
local: DashboardTwoFactorLocalSchema().nullish(),
remote: DashboardTwoFactorRemoteSchema().nullish()
})
}
export function DashboardTwoFactorLocalSchema(): z.ZodObject<Properties<DashboardTwoFactorLocal>> {
return z.object({
__typename: z.literal('DashboardTwoFactorLocal').optional(),
enabled: z.boolean().nullish()
})
}
export function DashboardTwoFactorRemoteSchema(): z.ZodObject<Properties<DashboardTwoFactorRemote>> {
return z.object({
__typename: z.literal('DashboardTwoFactorRemote').optional(),
enabled: z.boolean().nullish()
})
}
export function DashboardVarsSchema(): z.ZodObject<Properties<DashboardVars>> {
return z.object({
__typename: z.literal('DashboardVars').optional(),
flashGuid: z.string().nullish(),
regState: z.string().nullish(),
regTy: z.string().nullish(),
serverDescription: z.string().nullish(),
serverName: z.string().nullish()
})
}
export function DashboardVersionsSchema(): z.ZodObject<Properties<DashboardVersions>> {
return z.object({
__typename: z.literal('DashboardVersions').optional(),
unraid: z.string().nullish()
})
}
export function DashboardVmsSchema(): z.ZodObject<Properties<DashboardVms>> {
return z.object({
__typename: z.literal('DashboardVms').optional(),
installed: z.number().nullish(),
started: z.number().nullish()
})
}
export function DevicesSchema(): z.ZodObject<Properties<Devices>> {
return z.object({
__typename: z.literal('Devices').optional(),
@@ -503,6 +683,7 @@ export function MountSchema(): z.ZodObject<Properties<Mount>> {
export function NetworkSchema(): z.ZodObject<Properties<Network>> {
return z.object({
__typename: z.literal('Network').optional(),
accessUrls: z.array(AccessUrlSchema()).nullish(),
carrierChanges: z.string().nullish(),
duplex: z.string().nullish(),
iface: z.string().nullish(),
@@ -518,6 +699,12 @@ export function NetworkSchema(): z.ZodObject<Properties<Network>> {
})
}
export function NetworkInputSchema(): z.ZodObject<Properties<NetworkInput>> {
return z.object({
accessUrls: z.array(z.lazy(() => AccessUrlInputSchema()))
})
}
export function NotificationSchema(): z.ZodObject<Properties<Notification>> {
return z.object({
__typename: z.literal('Notification').optional(),

View File

@@ -20,9 +20,25 @@ export type Scalars = {
JSON: { input: { [key: string]: any }; output: { [key: string]: any }; }
Long: { input: number; output: number; }
Port: { input: number; output: number; }
URL: { input: URL; output: URL; }
UUID: { input: string; output: string; }
};
export type AccessUrl = {
__typename?: 'AccessUrl';
ipv4?: Maybe<Scalars['URL']['output']>;
ipv6?: Maybe<Scalars['URL']['output']>;
name?: Maybe<Scalars['String']['output']>;
type: URL_TYPE;
};
export type AccessUrlInput = {
ipv4?: InputMaybe<Scalars['URL']['input']>;
ipv6?: InputMaybe<Scalars['URL']['input']>;
name?: InputMaybe<Scalars['String']['input']>;
type: URL_TYPE;
};
export type AllowedOriginInput = {
origins: Array<Scalars['String']['input']>;
};
@@ -64,10 +80,18 @@ export type ArrayType = {
export type ArrayCapacity = {
__typename?: 'ArrayCapacity';
bytes?: Maybe<ArrayCapacityBytes>;
disks: Capacity;
kilobytes: Capacity;
};
export type ArrayCapacityBytes = {
__typename?: 'ArrayCapacityBytes';
free?: Maybe<Scalars['Long']['output']>;
total?: Maybe<Scalars['Long']['output']>;
used?: Maybe<Scalars['Long']['output']>;
};
export type ArrayDisk = {
__typename?: 'ArrayDisk';
/** User comment on disk */
@@ -294,6 +318,123 @@ export enum ContainerState {
RUNNING = 'RUNNING'
}
export type Dashboard = {
__typename?: 'Dashboard';
apps?: Maybe<DashboardApps>;
array?: Maybe<DashboardArray>;
config?: Maybe<DashboardConfig>;
display?: Maybe<DashboardDisplay>;
id: Scalars['ID']['output'];
lastPublish?: Maybe<Scalars['DateTime']['output']>;
network?: Maybe<Network>;
online?: Maybe<Scalars['Boolean']['output']>;
os?: Maybe<DashboardOs>;
services?: Maybe<Array<Maybe<DashboardService>>>;
twoFactor?: Maybe<DashboardTwoFactor>;
vars?: Maybe<DashboardVars>;
versions?: Maybe<DashboardVersions>;
vms?: Maybe<DashboardVms>;
};
export type DashboardApps = {
__typename?: 'DashboardApps';
installed?: Maybe<Scalars['Int']['output']>;
started?: Maybe<Scalars['Int']['output']>;
};
export type DashboardArray = {
__typename?: 'DashboardArray';
/** Current array capacity */
capacity?: Maybe<ArrayCapacity>;
/** Current array state */
state?: Maybe<Scalars['String']['output']>;
};
export type DashboardCase = {
__typename?: 'DashboardCase';
base64?: Maybe<Scalars['String']['output']>;
error?: Maybe<Scalars['String']['output']>;
icon?: Maybe<Scalars['String']['output']>;
url?: Maybe<Scalars['String']['output']>;
};
export type DashboardConfig = {
__typename?: 'DashboardConfig';
error?: Maybe<Scalars['String']['output']>;
valid?: Maybe<Scalars['Boolean']['output']>;
};
export type DashboardDisplay = {
__typename?: 'DashboardDisplay';
case?: Maybe<DashboardCase>;
};
export type DashboardOs = {
__typename?: 'DashboardOs';
hostname?: Maybe<Scalars['String']['output']>;
uptime?: Maybe<Scalars['DateTime']['output']>;
};
export type DashboardService = {
__typename?: 'DashboardService';
name?: Maybe<Scalars['String']['output']>;
online?: Maybe<Scalars['Boolean']['output']>;
uptime?: Maybe<DashboardServiceUptime>;
version?: Maybe<Scalars['String']['output']>;
};
export type DashboardServiceInput = {
name: Scalars['String']['input'];
online: Scalars['Boolean']['input'];
uptime?: InputMaybe<DashboardServiceUptimeInput>;
version: Scalars['String']['input'];
};
export type DashboardServiceUptime = {
__typename?: 'DashboardServiceUptime';
timestamp?: Maybe<Scalars['DateTime']['output']>;
};
export type DashboardServiceUptimeInput = {
timestamp: Scalars['DateTime']['input'];
};
export type DashboardTwoFactor = {
__typename?: 'DashboardTwoFactor';
local?: Maybe<DashboardTwoFactorLocal>;
remote?: Maybe<DashboardTwoFactorRemote>;
};
export type DashboardTwoFactorLocal = {
__typename?: 'DashboardTwoFactorLocal';
enabled?: Maybe<Scalars['Boolean']['output']>;
};
export type DashboardTwoFactorRemote = {
__typename?: 'DashboardTwoFactorRemote';
enabled?: Maybe<Scalars['Boolean']['output']>;
};
export type DashboardVars = {
__typename?: 'DashboardVars';
flashGuid?: Maybe<Scalars['String']['output']>;
regState?: Maybe<Scalars['String']['output']>;
regTy?: Maybe<Scalars['String']['output']>;
serverDescription?: Maybe<Scalars['String']['output']>;
serverName?: Maybe<Scalars['String']['output']>;
};
export type DashboardVersions = {
__typename?: 'DashboardVersions';
unraid?: Maybe<Scalars['String']['output']>;
};
export type DashboardVms = {
__typename?: 'DashboardVms';
installed?: Maybe<Scalars['Int']['output']>;
started?: Maybe<Scalars['Int']['output']>;
};
export type Devices = {
__typename?: 'Devices';
gpu?: Maybe<Array<Maybe<Gpu>>>;
@@ -588,7 +729,6 @@ export type Mutation = {
removeDiskFromArray?: Maybe<ArrayType>;
/** 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']>;
@@ -601,6 +741,7 @@ export type Mutation = {
unmountArrayDisk?: Maybe<Disk>;
/** Update an existing API key */
updateApikey?: Maybe<ApiKey>;
updateNetwork: Network;
};
@@ -657,11 +798,6 @@ export type MutationremoveDiskFromArrayArgs = {
};
export type MutationsendNotificationArgs = {
notification: NotificationInput;
};
export type MutationsetAdditionalAllowedOriginsArgs = {
input: AllowedOriginInput;
};
@@ -687,8 +823,14 @@ export type MutationupdateApikeyArgs = {
name: Scalars['String']['input'];
};
export type MutationupdateNetworkArgs = {
data: NetworkInput;
};
export type Network = {
__typename?: 'Network';
accessUrls?: Maybe<Array<AccessUrl>>;
carrierChanges?: Maybe<Scalars['String']['output']>;
duplex?: Maybe<Scalars['String']['output']>;
iface?: Maybe<Scalars['String']['output']>;
@@ -703,6 +845,10 @@ export type Network = {
type?: Maybe<Scalars['String']['output']>;
};
export type NetworkInput = {
accessUrls: Array<AccessUrlInput>;
};
export type Notification = {
__typename?: 'Notification';
description: Scalars['String']['output'];
@@ -886,6 +1032,8 @@ export type Query = {
registration?: Maybe<Registration>;
remoteAccess: RemoteAccess;
server?: Maybe<Server>;
/** Temporary Type to Enable Swapping Dashboard over in Connect without major changes */
serverDashboard: Dashboard;
servers: Array<Server>;
/** Network Shares */
shares?: Maybe<Array<Maybe<Share>>>;
@@ -1133,6 +1281,14 @@ export enum Theme {
WHITE = 'white'
}
export enum URL_TYPE {
DEFAULT = 'DEFAULT',
LAN = 'LAN',
MDNS = 'MDNS',
WAN = 'WAN',
WIREGUARD = 'WIREGUARD'
}
export type UnassignedDevice = {
__typename?: 'UnassignedDevice';
devlinks?: Maybe<Scalars['String']['output']>;
@@ -1582,11 +1738,14 @@ export type ResolversInterfaceTypes<RefType extends Record<string, unknown>> = R
/** Mapping between all available schema types and the resolvers types */
export type ResolversTypes = ResolversObject<{
AccessUrl: ResolverTypeWrapper<AccessUrl>;
AccessUrlInput: AccessUrlInput;
AllowedOriginInput: AllowedOriginInput;
ApiKey: ResolverTypeWrapper<ApiKey>;
ApiKeyResponse: ResolverTypeWrapper<ApiKeyResponse>;
Array: ResolverTypeWrapper<ArrayType>;
ArrayCapacity: ResolverTypeWrapper<ArrayCapacity>;
ArrayCapacityBytes: ResolverTypeWrapper<ArrayCapacityBytes>;
ArrayDisk: ResolverTypeWrapper<ArrayDisk>;
ArrayDiskFsColor: ArrayDiskFsColor;
ArrayDiskStatus: ArrayDiskStatus;
@@ -1608,6 +1767,23 @@ export type ResolversTypes = ResolversObject<{
ContainerPort: ResolverTypeWrapper<ContainerPort>;
ContainerPortType: ContainerPortType;
ContainerState: ContainerState;
Dashboard: ResolverTypeWrapper<Dashboard>;
DashboardApps: ResolverTypeWrapper<DashboardApps>;
DashboardArray: ResolverTypeWrapper<DashboardArray>;
DashboardCase: ResolverTypeWrapper<DashboardCase>;
DashboardConfig: ResolverTypeWrapper<DashboardConfig>;
DashboardDisplay: ResolverTypeWrapper<DashboardDisplay>;
DashboardOs: ResolverTypeWrapper<DashboardOs>;
DashboardService: ResolverTypeWrapper<DashboardService>;
DashboardServiceInput: DashboardServiceInput;
DashboardServiceUptime: ResolverTypeWrapper<DashboardServiceUptime>;
DashboardServiceUptimeInput: DashboardServiceUptimeInput;
DashboardTwoFactor: ResolverTypeWrapper<DashboardTwoFactor>;
DashboardTwoFactorLocal: ResolverTypeWrapper<DashboardTwoFactorLocal>;
DashboardTwoFactorRemote: ResolverTypeWrapper<DashboardTwoFactorRemote>;
DashboardVars: ResolverTypeWrapper<DashboardVars>;
DashboardVersions: ResolverTypeWrapper<DashboardVersions>;
DashboardVms: ResolverTypeWrapper<DashboardVms>;
DateTime: ResolverTypeWrapper<Scalars['DateTime']['output']>;
Devices: ResolverTypeWrapper<Devices>;
Disk: ResolverTypeWrapper<Disk>;
@@ -1640,6 +1816,7 @@ export type ResolversTypes = ResolversObject<{
Mount: ResolverTypeWrapper<Mount>;
Mutation: ResolverTypeWrapper<{}>;
Network: ResolverTypeWrapper<Network>;
NetworkInput: NetworkInput;
Notification: ResolverTypeWrapper<Notification>;
NotificationFilter: NotificationFilter;
NotificationInput: NotificationInput;
@@ -1666,6 +1843,8 @@ export type ResolversTypes = ResolversObject<{
System: ResolverTypeWrapper<System>;
Temperature: Temperature;
Theme: Theme;
URL: ResolverTypeWrapper<Scalars['URL']['output']>;
URL_TYPE: URL_TYPE;
UUID: ResolverTypeWrapper<Scalars['UUID']['output']>;
UnassignedDevice: ResolverTypeWrapper<UnassignedDevice>;
Uptime: ResolverTypeWrapper<Uptime>;
@@ -1693,11 +1872,14 @@ export type ResolversTypes = ResolversObject<{
/** Mapping between all available schema types and the resolvers parents */
export type ResolversParentTypes = ResolversObject<{
AccessUrl: AccessUrl;
AccessUrlInput: AccessUrlInput;
AllowedOriginInput: AllowedOriginInput;
ApiKey: ApiKey;
ApiKeyResponse: ApiKeyResponse;
Array: ArrayType;
ArrayCapacity: ArrayCapacity;
ArrayCapacityBytes: ArrayCapacityBytes;
ArrayDisk: ArrayDisk;
Baseboard: Baseboard;
Boolean: Scalars['Boolean']['output'];
@@ -1711,6 +1893,23 @@ export type ResolversParentTypes = ResolversObject<{
ContainerHostConfig: ContainerHostConfig;
ContainerMount: ContainerMount;
ContainerPort: ContainerPort;
Dashboard: Dashboard;
DashboardApps: DashboardApps;
DashboardArray: DashboardArray;
DashboardCase: DashboardCase;
DashboardConfig: DashboardConfig;
DashboardDisplay: DashboardDisplay;
DashboardOs: DashboardOs;
DashboardService: DashboardService;
DashboardServiceInput: DashboardServiceInput;
DashboardServiceUptime: DashboardServiceUptime;
DashboardServiceUptimeInput: DashboardServiceUptimeInput;
DashboardTwoFactor: DashboardTwoFactor;
DashboardTwoFactorLocal: DashboardTwoFactorLocal;
DashboardTwoFactorRemote: DashboardTwoFactorRemote;
DashboardVars: DashboardVars;
DashboardVersions: DashboardVersions;
DashboardVms: DashboardVms;
DateTime: Scalars['DateTime']['output'];
Devices: Devices;
Disk: Disk;
@@ -1736,6 +1935,7 @@ export type ResolversParentTypes = ResolversObject<{
Mount: Mount;
Mutation: {};
Network: Network;
NetworkInput: NetworkInput;
Notification: Notification;
NotificationFilter: NotificationFilter;
NotificationInput: NotificationInput;
@@ -1757,6 +1957,7 @@ export type ResolversParentTypes = ResolversObject<{
String: Scalars['String']['output'];
Subscription: {};
System: System;
URL: Scalars['URL']['output'];
UUID: Scalars['UUID']['output'];
UnassignedDevice: UnassignedDevice;
Uptime: Uptime;
@@ -1777,6 +1978,14 @@ export type ResolversParentTypes = ResolversObject<{
usersInput: usersInput;
}>;
export type AccessUrlResolvers<ContextType = Context, ParentType extends ResolversParentTypes['AccessUrl'] = ResolversParentTypes['AccessUrl']> = ResolversObject<{
ipv4?: Resolver<Maybe<ResolversTypes['URL']>, ParentType, ContextType>;
ipv6?: Resolver<Maybe<ResolversTypes['URL']>, ParentType, ContextType>;
name?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
type?: Resolver<ResolversTypes['URL_TYPE'], ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export type ApiKeyResolvers<ContextType = Context, ParentType extends ResolversParentTypes['ApiKey'] = ResolversParentTypes['ApiKey']> = ResolversObject<{
description?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
expiresAt?: Resolver<ResolversTypes['Long'], ParentType, ContextType>;
@@ -1805,11 +2014,19 @@ export type ArrayResolvers<ContextType = Context, ParentType extends ResolversPa
}>;
export type ArrayCapacityResolvers<ContextType = Context, ParentType extends ResolversParentTypes['ArrayCapacity'] = ResolversParentTypes['ArrayCapacity']> = ResolversObject<{
bytes?: Resolver<Maybe<ResolversTypes['ArrayCapacityBytes']>, ParentType, ContextType>;
disks?: Resolver<ResolversTypes['Capacity'], ParentType, ContextType>;
kilobytes?: Resolver<ResolversTypes['Capacity'], ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export type ArrayCapacityBytesResolvers<ContextType = Context, ParentType extends ResolversParentTypes['ArrayCapacityBytes'] = ResolversParentTypes['ArrayCapacityBytes']> = ResolversObject<{
free?: Resolver<Maybe<ResolversTypes['Long']>, ParentType, ContextType>;
total?: Resolver<Maybe<ResolversTypes['Long']>, ParentType, ContextType>;
used?: Resolver<Maybe<ResolversTypes['Long']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export type ArrayDiskResolvers<ContextType = Context, ParentType extends ResolversParentTypes['ArrayDisk'] = ResolversParentTypes['ArrayDisk']> = ResolversObject<{
comment?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
critical?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
@@ -1908,6 +2125,110 @@ export type ContainerPortResolvers<ContextType = Context, ParentType extends Res
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export type DashboardResolvers<ContextType = Context, ParentType extends ResolversParentTypes['Dashboard'] = ResolversParentTypes['Dashboard']> = ResolversObject<{
apps?: Resolver<Maybe<ResolversTypes['DashboardApps']>, ParentType, ContextType>;
array?: Resolver<Maybe<ResolversTypes['DashboardArray']>, ParentType, ContextType>;
config?: Resolver<Maybe<ResolversTypes['DashboardConfig']>, ParentType, ContextType>;
display?: Resolver<Maybe<ResolversTypes['DashboardDisplay']>, ParentType, ContextType>;
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
lastPublish?: Resolver<Maybe<ResolversTypes['DateTime']>, ParentType, ContextType>;
network?: Resolver<Maybe<ResolversTypes['Network']>, ParentType, ContextType>;
online?: Resolver<Maybe<ResolversTypes['Boolean']>, ParentType, ContextType>;
os?: Resolver<Maybe<ResolversTypes['DashboardOs']>, ParentType, ContextType>;
services?: Resolver<Maybe<Array<Maybe<ResolversTypes['DashboardService']>>>, ParentType, ContextType>;
twoFactor?: Resolver<Maybe<ResolversTypes['DashboardTwoFactor']>, ParentType, ContextType>;
vars?: Resolver<Maybe<ResolversTypes['DashboardVars']>, ParentType, ContextType>;
versions?: Resolver<Maybe<ResolversTypes['DashboardVersions']>, ParentType, ContextType>;
vms?: Resolver<Maybe<ResolversTypes['DashboardVms']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export type DashboardAppsResolvers<ContextType = Context, ParentType extends ResolversParentTypes['DashboardApps'] = ResolversParentTypes['DashboardApps']> = ResolversObject<{
installed?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
started?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export type DashboardArrayResolvers<ContextType = Context, ParentType extends ResolversParentTypes['DashboardArray'] = ResolversParentTypes['DashboardArray']> = ResolversObject<{
capacity?: Resolver<Maybe<ResolversTypes['ArrayCapacity']>, ParentType, ContextType>;
state?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export type DashboardCaseResolvers<ContextType = Context, ParentType extends ResolversParentTypes['DashboardCase'] = ResolversParentTypes['DashboardCase']> = ResolversObject<{
base64?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
error?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
icon?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
url?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export type DashboardConfigResolvers<ContextType = Context, ParentType extends ResolversParentTypes['DashboardConfig'] = ResolversParentTypes['DashboardConfig']> = ResolversObject<{
error?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
valid?: Resolver<Maybe<ResolversTypes['Boolean']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export type DashboardDisplayResolvers<ContextType = Context, ParentType extends ResolversParentTypes['DashboardDisplay'] = ResolversParentTypes['DashboardDisplay']> = ResolversObject<{
case?: Resolver<Maybe<ResolversTypes['DashboardCase']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export type DashboardOsResolvers<ContextType = Context, ParentType extends ResolversParentTypes['DashboardOs'] = ResolversParentTypes['DashboardOs']> = ResolversObject<{
hostname?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
uptime?: Resolver<Maybe<ResolversTypes['DateTime']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export type DashboardServiceResolvers<ContextType = Context, ParentType extends ResolversParentTypes['DashboardService'] = ResolversParentTypes['DashboardService']> = ResolversObject<{
name?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
online?: Resolver<Maybe<ResolversTypes['Boolean']>, ParentType, ContextType>;
uptime?: Resolver<Maybe<ResolversTypes['DashboardServiceUptime']>, ParentType, ContextType>;
version?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export type DashboardServiceUptimeResolvers<ContextType = Context, ParentType extends ResolversParentTypes['DashboardServiceUptime'] = ResolversParentTypes['DashboardServiceUptime']> = ResolversObject<{
timestamp?: Resolver<Maybe<ResolversTypes['DateTime']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export type DashboardTwoFactorResolvers<ContextType = Context, ParentType extends ResolversParentTypes['DashboardTwoFactor'] = ResolversParentTypes['DashboardTwoFactor']> = ResolversObject<{
local?: Resolver<Maybe<ResolversTypes['DashboardTwoFactorLocal']>, ParentType, ContextType>;
remote?: Resolver<Maybe<ResolversTypes['DashboardTwoFactorRemote']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export type DashboardTwoFactorLocalResolvers<ContextType = Context, ParentType extends ResolversParentTypes['DashboardTwoFactorLocal'] = ResolversParentTypes['DashboardTwoFactorLocal']> = ResolversObject<{
enabled?: Resolver<Maybe<ResolversTypes['Boolean']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export type DashboardTwoFactorRemoteResolvers<ContextType = Context, ParentType extends ResolversParentTypes['DashboardTwoFactorRemote'] = ResolversParentTypes['DashboardTwoFactorRemote']> = ResolversObject<{
enabled?: Resolver<Maybe<ResolversTypes['Boolean']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export type DashboardVarsResolvers<ContextType = Context, ParentType extends ResolversParentTypes['DashboardVars'] = ResolversParentTypes['DashboardVars']> = ResolversObject<{
flashGuid?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
regState?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
regTy?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
serverDescription?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
serverName?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export type DashboardVersionsResolvers<ContextType = Context, ParentType extends ResolversParentTypes['DashboardVersions'] = ResolversParentTypes['DashboardVersions']> = ResolversObject<{
unraid?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export type DashboardVmsResolvers<ContextType = Context, ParentType extends ResolversParentTypes['DashboardVms'] = ResolversParentTypes['DashboardVms']> = ResolversObject<{
installed?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
started?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export interface DateTimeScalarConfig extends GraphQLScalarTypeConfig<ResolversTypes['DateTime'], any> {
name: 'DateTime';
}
@@ -2154,7 +2475,6 @@ export type MutationResolvers<ContextType = Context, ParentType extends Resolver
reboot?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
removeDiskFromArray?: Resolver<Maybe<ResolversTypes['Array']>, ParentType, ContextType, Partial<MutationremoveDiskFromArrayArgs>>;
resumeParityCheck?: Resolver<Maybe<ResolversTypes['JSON']>, ParentType, ContextType>;
sendNotification?: Resolver<Maybe<ResolversTypes['Notification']>, ParentType, ContextType, RequireFields<MutationsendNotificationArgs, 'notification'>>;
setAdditionalAllowedOrigins?: Resolver<Array<ResolversTypes['String']>, ParentType, ContextType, RequireFields<MutationsetAdditionalAllowedOriginsArgs, 'input'>>;
setupRemoteAccess?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationsetupRemoteAccessArgs, 'input'>>;
shutdown?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
@@ -2163,9 +2483,11 @@ export type MutationResolvers<ContextType = Context, ParentType extends Resolver
stopArray?: Resolver<Maybe<ResolversTypes['Array']>, ParentType, ContextType>;
unmountArrayDisk?: Resolver<Maybe<ResolversTypes['Disk']>, ParentType, ContextType, RequireFields<MutationunmountArrayDiskArgs, 'id'>>;
updateApikey?: Resolver<Maybe<ResolversTypes['ApiKey']>, ParentType, ContextType, RequireFields<MutationupdateApikeyArgs, 'name'>>;
updateNetwork?: Resolver<ResolversTypes['Network'], ParentType, ContextType, RequireFields<MutationupdateNetworkArgs, 'data'>>;
}>;
export type NetworkResolvers<ContextType = Context, ParentType extends ResolversParentTypes['Network'] = ResolversParentTypes['Network']> = ResolversObject<{
accessUrls?: Resolver<Maybe<Array<ResolversTypes['AccessUrl']>>, ParentType, ContextType>;
carrierChanges?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
duplex?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
iface?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
@@ -2334,6 +2656,7 @@ export type QueryResolvers<ContextType = Context, ParentType extends ResolversPa
registration?: Resolver<Maybe<ResolversTypes['Registration']>, ParentType, ContextType>;
remoteAccess?: Resolver<ResolversTypes['RemoteAccess'], ParentType, ContextType>;
server?: Resolver<Maybe<ResolversTypes['Server']>, ParentType, ContextType>;
serverDashboard?: Resolver<ResolversTypes['Dashboard'], ParentType, ContextType>;
servers?: Resolver<Array<ResolversTypes['Server']>, ParentType, ContextType>;
shares?: Resolver<Maybe<Array<Maybe<ResolversTypes['Share']>>>, ParentType, ContextType>;
unassignedDevices?: Resolver<Maybe<Array<Maybe<ResolversTypes['UnassignedDevice']>>>, ParentType, ContextType>;
@@ -2446,6 +2769,10 @@ export type SystemResolvers<ContextType = Context, ParentType extends ResolversP
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>;
export interface URLScalarConfig extends GraphQLScalarTypeConfig<ResolversTypes['URL'], any> {
name: 'URL';
}
export interface UUIDScalarConfig extends GraphQLScalarTypeConfig<ResolversTypes['UUID'], any> {
name: 'UUID';
}
@@ -2728,10 +3055,12 @@ export type WelcomeResolvers<ContextType = Context, ParentType extends Resolvers
}>;
export type Resolvers<ContextType = Context> = ResolversObject<{
AccessUrl?: AccessUrlResolvers<ContextType>;
ApiKey?: ApiKeyResolvers<ContextType>;
ApiKeyResponse?: ApiKeyResponseResolvers<ContextType>;
Array?: ArrayResolvers<ContextType>;
ArrayCapacity?: ArrayCapacityResolvers<ContextType>;
ArrayCapacityBytes?: ArrayCapacityBytesResolvers<ContextType>;
ArrayDisk?: ArrayDiskResolvers<ContextType>;
Baseboard?: BaseboardResolvers<ContextType>;
Capacity?: CapacityResolvers<ContextType>;
@@ -2742,6 +3071,21 @@ export type Resolvers<ContextType = Context> = ResolversObject<{
ContainerHostConfig?: ContainerHostConfigResolvers<ContextType>;
ContainerMount?: ContainerMountResolvers<ContextType>;
ContainerPort?: ContainerPortResolvers<ContextType>;
Dashboard?: DashboardResolvers<ContextType>;
DashboardApps?: DashboardAppsResolvers<ContextType>;
DashboardArray?: DashboardArrayResolvers<ContextType>;
DashboardCase?: DashboardCaseResolvers<ContextType>;
DashboardConfig?: DashboardConfigResolvers<ContextType>;
DashboardDisplay?: DashboardDisplayResolvers<ContextType>;
DashboardOs?: DashboardOsResolvers<ContextType>;
DashboardService?: DashboardServiceResolvers<ContextType>;
DashboardServiceUptime?: DashboardServiceUptimeResolvers<ContextType>;
DashboardTwoFactor?: DashboardTwoFactorResolvers<ContextType>;
DashboardTwoFactorLocal?: DashboardTwoFactorLocalResolvers<ContextType>;
DashboardTwoFactorRemote?: DashboardTwoFactorRemoteResolvers<ContextType>;
DashboardVars?: DashboardVarsResolvers<ContextType>;
DashboardVersions?: DashboardVersionsResolvers<ContextType>;
DashboardVms?: DashboardVmsResolvers<ContextType>;
DateTime?: GraphQLScalarType;
Devices?: DevicesResolvers<ContextType>;
Disk?: DiskResolvers<ContextType>;
@@ -2781,6 +3125,7 @@ export type Resolvers<ContextType = Context> = ResolversObject<{
Share?: ShareResolvers<ContextType>;
Subscription?: SubscriptionResolvers<ContextType>;
System?: SystemResolvers<ContextType>;
URL?: GraphQLScalarType;
UUID?: GraphQLScalarType;
UnassignedDevice?: UnassignedDeviceResolvers<ContextType>;
Uptime?: UptimeResolvers<ContextType>;

View File

@@ -0,0 +1,62 @@
/* eslint-disable */
import * as types from './graphql.js';
import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
/**
* Map of all GraphQL operations in the project.
*
* This map has several performance disadvantages:
* 1. It is not tree-shakeable, so it will include all operations in the project.
* 2. It is not minifiable, so the string of a GraphQL query will be multiple times inside the bundle.
* 3. It does not support dead code elimination, so it will add unused operations.
*
* Therefore it is highly recommended to use the babel or swc plugin for production.
*/
const documents = {
"\nmutation sendRemoteAccessMutation($remoteAccess: RemoteAccessInput!) {\n\tremoteSession(remoteAccess: $remoteAccess)\n}\n": types.sendRemoteAccessMutationDocument,
"\n mutation sendRemoteGraphQLResponse($input: RemoteGraphQLServerInput!) {\n remoteGraphQLResponse(input: $input)\n }\n": types.sendRemoteGraphQLResponseDocument,
"\n fragment RemoteGraphQLEventFragment on RemoteGraphQLEvent {\n remoteGraphQLEventData: data {\n type\n body\n sha256\n }\n }\n": types.RemoteGraphQLEventFragmentFragmentDoc,
"\n fragment RemoteAccessEventFragment on RemoteAccessEvent {\n type\n data {\n type\n url {\n type\n name\n ipv4\n ipv6\n }\n apiKey\n }\n }\n": types.RemoteAccessEventFragmentFragmentDoc,
"\n subscription events {\n events {\n __typename\n ... on ClientConnectedEvent {\n connectedData: data {\n type\n version\n apiKey\n }\n connectedEvent: type\n }\n ... on ClientDisconnectedEvent {\n disconnectedData: data {\n type\n version\n apiKey\n }\n disconnectedEvent: type\n }\n ...RemoteAccessEventFragment\n ...RemoteGraphQLEventFragment\n }\n }\n": types.eventsDocument,
};
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*
*
* @example
* ```ts
* const query = graphql(`query GetUser($id: ID!) { user(id: $id) { name } }`);
* ```
*
* The query argument is unknown!
* Please regenerate the types.
*/
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: "\nmutation sendRemoteAccessMutation($remoteAccess: RemoteAccessInput!) {\n\tremoteSession(remoteAccess: $remoteAccess)\n}\n"): (typeof documents)["\nmutation sendRemoteAccessMutation($remoteAccess: RemoteAccessInput!) {\n\tremoteSession(remoteAccess: $remoteAccess)\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 sendRemoteGraphQLResponse($input: RemoteGraphQLServerInput!) {\n remoteGraphQLResponse(input: $input)\n }\n"): (typeof documents)["\n mutation sendRemoteGraphQLResponse($input: RemoteGraphQLServerInput!) {\n remoteGraphQLResponse(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 fragment RemoteGraphQLEventFragment on RemoteGraphQLEvent {\n remoteGraphQLEventData: data {\n type\n body\n sha256\n }\n }\n"): (typeof documents)["\n fragment RemoteGraphQLEventFragment on RemoteGraphQLEvent {\n remoteGraphQLEventData: data {\n type\n body\n sha256\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 fragment RemoteAccessEventFragment on RemoteAccessEvent {\n type\n data {\n type\n url {\n type\n name\n ipv4\n ipv6\n }\n apiKey\n }\n }\n"): (typeof documents)["\n fragment RemoteAccessEventFragment on RemoteAccessEvent {\n type\n data {\n type\n url {\n type\n name\n ipv4\n ipv6\n }\n apiKey\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 subscription events {\n events {\n __typename\n ... on ClientConnectedEvent {\n connectedData: data {\n type\n version\n apiKey\n }\n connectedEvent: type\n }\n ... on ClientDisconnectedEvent {\n disconnectedData: data {\n type\n version\n apiKey\n }\n disconnectedEvent: type\n }\n ...RemoteAccessEventFragment\n ...RemoteGraphQLEventFragment\n }\n }\n"): (typeof documents)["\n subscription events {\n events {\n __typename\n ... on ClientConnectedEvent {\n connectedData: data {\n type\n version\n apiKey\n }\n connectedEvent: type\n }\n ... on ClientDisconnectedEvent {\n disconnectedData: data {\n type\n version\n apiKey\n }\n disconnectedEvent: type\n }\n ...RemoteAccessEventFragment\n ...RemoteGraphQLEventFragment\n }\n }\n"];
export function graphql(source: string) {
return (documents as any)[source] ?? {};
}
export type DocumentType<TDocumentNode extends DocumentNode<any, any>> = TDocumentNode extends DocumentNode< infer TType, any> ? TType : never;

View File

@@ -709,30 +709,6 @@ export type Vars = {
regTy?: Maybe<Scalars['String']['output']>;
};
export type updateDashboardMutationVariables = Exact<{
data: DashboardInput;
apiKey: Scalars['String']['input'];
}>;
export type updateDashboardMutation = { __typename?: 'Mutation', updateDashboard: { __typename?: 'Dashboard', apps?: { __typename?: 'DashboardApps', installed?: number | null } | null } };
export type sendNotificationMutationVariables = Exact<{
notification: NotificationInput;
apiKey: Scalars['String']['input'];
}>;
export type sendNotificationMutation = { __typename?: 'Mutation', sendNotification?: { __typename?: 'Notification', title?: string | null, subject?: string | null, description?: string | null, importance?: Importance | null, link?: string | null, status: NotificationStatus } | null };
export type updateNetworkMutationVariables = Exact<{
data: NetworkInput;
apiKey: Scalars['String']['input'];
}>;
export type updateNetworkMutation = { __typename?: 'Mutation', updateNetwork: { __typename?: 'Network', accessUrls?: Array<{ __typename?: 'AccessUrl', name?: string | null, type: URL_TYPE, ipv4?: URL | null, ipv6?: URL | null }> | null } };
export type sendRemoteAccessMutationMutationVariables = Exact<{
remoteAccess: RemoteAccessInput;
}>;
@@ -764,9 +740,6 @@ export type eventsSubscription = { __typename?: 'Subscription', events?: Array<{
export const RemoteGraphQLEventFragmentFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteGraphQLEventFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteGraphQLEvent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"remoteGraphQLEventData"},"name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"body"}},{"kind":"Field","name":{"kind":"Name","value":"sha256"}}]}}]}}]} as unknown as DocumentNode<RemoteGraphQLEventFragmentFragment, unknown>;
export const RemoteAccessEventFragmentFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteAccessEventFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteAccessEvent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"url"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"ipv4"}},{"kind":"Field","name":{"kind":"Name","value":"ipv6"}}]}},{"kind":"Field","name":{"kind":"Name","value":"apiKey"}}]}}]}}]} as unknown as DocumentNode<RemoteAccessEventFragmentFragment, unknown>;
export const updateDashboardDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"updateDashboard"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"data"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"DashboardInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"apiKey"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateDashboard"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"data"}}}],"directives":[{"kind":"Directive","name":{"kind":"Name","value":"auth"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"apiKey"},"value":{"kind":"Variable","name":{"kind":"Name","value":"apiKey"}}}]}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"apps"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"installed"}}]}}]}}]}}]} as unknown as DocumentNode<updateDashboardMutation, updateDashboardMutationVariables>;
export const sendNotificationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"sendNotification"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"notification"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"NotificationInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"apiKey"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sendNotification"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"notification"},"value":{"kind":"Variable","name":{"kind":"Name","value":"notification"}}}],"directives":[{"kind":"Directive","name":{"kind":"Name","value":"auth"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"apiKey"},"value":{"kind":"Variable","name":{"kind":"Name","value":"apiKey"}}}]}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"subject"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"importance"}},{"kind":"Field","name":{"kind":"Name","value":"link"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}}]}}]} as unknown as DocumentNode<sendNotificationMutation, sendNotificationMutationVariables>;
export const updateNetworkDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"updateNetwork"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"data"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"NetworkInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"apiKey"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateNetwork"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"data"}}}],"directives":[{"kind":"Directive","name":{"kind":"Name","value":"auth"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"apiKey"},"value":{"kind":"Variable","name":{"kind":"Name","value":"apiKey"}}}]}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"accessUrls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"ipv4"}},{"kind":"Field","name":{"kind":"Name","value":"ipv6"}}]}}]}}]}}]} as unknown as DocumentNode<updateNetworkMutation, updateNetworkMutationVariables>;
export const sendRemoteAccessMutationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"sendRemoteAccessMutation"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"remoteAccess"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteAccessInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"remoteSession"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"remoteAccess"},"value":{"kind":"Variable","name":{"kind":"Name","value":"remoteAccess"}}}]}]}}]} as unknown as DocumentNode<sendRemoteAccessMutationMutation, sendRemoteAccessMutationMutationVariables>;
export const sendRemoteGraphQLResponseDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"sendRemoteGraphQLResponse"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteGraphQLServerInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"remoteGraphQLResponse"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode<sendRemoteGraphQLResponseMutation, sendRemoteGraphQLResponseMutationVariables>;
export const eventsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"subscription","name":{"kind":"Name","value":"events"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"events"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ClientConnectedEvent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"connectedData"},"name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"version"}},{"kind":"Field","name":{"kind":"Name","value":"apiKey"}}]}},{"kind":"Field","alias":{"kind":"Name","value":"connectedEvent"},"name":{"kind":"Name","value":"type"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ClientDisconnectedEvent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"disconnectedData"},"name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"version"}},{"kind":"Field","name":{"kind":"Name","value":"apiKey"}}]}},{"kind":"Field","alias":{"kind":"Name","value":"disconnectedEvent"},"name":{"kind":"Name","value":"type"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"RemoteAccessEventFragment"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"RemoteGraphQLEventFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteAccessEventFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteAccessEvent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"url"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"ipv4"}},{"kind":"Field","name":{"kind":"Name","value":"ipv6"}}]}},{"kind":"Field","name":{"kind":"Name","value":"apiKey"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteGraphQLEventFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteGraphQLEvent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"remoteGraphQLEventData"},"name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"body"}},{"kind":"Field","name":{"kind":"Name","value":"sha256"}}]}}]}}]} as unknown as DocumentNode<eventsSubscription, eventsSubscriptionVariables>;

View File

@@ -0,0 +1,14 @@
import { graphql } from '@app/graphql/generated/client/gql';
// This doesn't need auth directive, new mothership can handle clients with no auth directives
export const SEND_DYNAMIC_REMOTE_ACCESS_MUTATION = graphql(/* GraphQL */ `
mutation sendRemoteAccessMutation($remoteAccess: RemoteAccessInput!) {
remoteSession(remoteAccess: $remoteAccess)
}
`);
export const SEND_REMOTE_QUERY_RESPONSE = graphql(/* GraphQL */ `
mutation sendRemoteGraphQLResponse($input: RemoteGraphQLServerInput!) {
remoteGraphQLResponse(input: $input)
}
`);

View File

@@ -1,91 +0,0 @@
import { dashboardLogger } from '@app/core/log';
import { generateData } from '@app/common/dashboard/generate-data';
import { PUBSUB_CHANNEL, pubsub } from '@app/core/pubsub';
import { getters, store } from '@app/store';
import { saveDataPacket } from '@app/store/modules/dashboard';
import { isEqual } from 'lodash';
import { GraphQLClient } from '@app/mothership/graphql-client';
import { SEND_DASHBOARD_PAYLOAD_MUTATION } from '../../mothership/mutations';
import { type DashboardInput } from '../../generated/client/graphql';
import { getDiff } from 'json-difference';
import { DEBUG } from '@app/environment';
import { isApolloError } from '@apollo/client/core';
const isNumberBetween = (min: number, max: number) => (num: number) => num > min && num < max;
const logAndReturn = <T>(returnValue: T, logLevel: 'info' | 'debug' | 'trace' | 'error', logLine: string, ...logParams: unknown[]): T => {
dashboardLogger[logLevel](logLine, ...logParams);
return returnValue;
};
const ONE_MB = 1_024 * 1_024;
const ONE_HUNDRED_MB = 100 * ONE_MB;
const canSendDataPacket = (dataPacket: DashboardInput | null) => {
const { lastDataPacketTimestamp, lastDataPacket } = getters.dashboard();
// Const { lastDataPacketTimestamp, lastDataPacketString, lastDataPacket } = dashboardStore;
if (!dataPacket) return logAndReturn(false, 'error', 'Not sending update to dashboard becuase the data packet is empty');
// UPDATE - No data packet has been sent since boot
if (!lastDataPacketTimestamp) return logAndReturn(true, 'debug', 'Sending update as none have been sent since the API started');
// NO_UPDATE - This is an exact copy of the last data packet
if (isEqual(dataPacket, lastDataPacket)) return logAndReturn(false, 'trace', '[NETWORK] Skipping Update');
if (!lastDataPacket) return logAndReturn(true, 'debug', 'Sending update as no data packets have been stored in state yet');
const difference = getDiff(lastDataPacket, dataPacket);
const oldBytesFree = lastDataPacket.array?.capacity.bytes?.free;
const newBytesFree = dataPacket.array?.capacity.bytes?.free;
if (oldBytesFree && newBytesFree && difference.added.length === 0 && difference.removed.length === 0 && difference.edited.length === 2) {
// If size has changed less than 100 MB (and nothing else has changed), don't send an update
const numberBetweenCheck = isNumberBetween((Number(oldBytesFree) * ONE_MB) - ONE_HUNDRED_MB, (Number(oldBytesFree) * ONE_MB) + ONE_HUNDRED_MB);
if (numberBetweenCheck(Number(newBytesFree) * ONE_MB)) {
logAndReturn(false, 'info', 'Size has not changed enough to send a new dashboard payload');
}
}
return logAndReturn(true, 'trace', 'Sending update because the packets are not equal');
};
export const publishToDashboard = async () => {
try {
const dataPacket = await generateData();
// Only update data on change
if (!canSendDataPacket(dataPacket)) return;
dashboardLogger.debug('New Data Packet Is: %o', dataPacket);
// Save new data packet
store.dispatch(saveDataPacket({ lastDataPacket: dataPacket }));
// Publish the updated data
dashboardLogger.trace({ dataPacket } , 'Publishing update');
// Update local clients
await pubsub.publish(PUBSUB_CHANNEL.DASHBOARD, {
dashboard: dataPacket,
});
if (dataPacket) {
const client = GraphQLClient.getInstance();
if (!client) {
throw new Error('Invalid Client');
}
// Update mothership
await client.mutate({ mutation: SEND_DASHBOARD_PAYLOAD_MUTATION, variables: { apiKey: getters.config().remote.apikey, data: dataPacket } });
} else {
dashboardLogger.error('DataPacket Was Empty');
}
} catch (error: unknown) {
if (error instanceof Error && isApolloError(error)) {
dashboardLogger.error('Failed publishing with GQL Errors: %s, \nClient Errors: %s', error.graphQLErrors.map(error => error.message).join(','), error.clientErrors.join(', '));
}
if (DEBUG) dashboardLogger.error(error);
}
};

View File

@@ -1,21 +1,13 @@
import { GraphQLClient } from '@app/mothership/graphql-client';
import { type Nginx } from '@app/core/types/states/nginx';
import { type RootState, store, getters } from '@app/store';
import { type RootState, store } from '@app/store';
import {
type NetworkInput,
URL_TYPE,
type AccessUrlInput,
} from '@app/graphql/generated/client/graphql';
import { dashboardLogger, logger } from '@app/core';
import { isEqual } from 'lodash';
import { SEND_NETWORK_MUTATION } from '@app/graphql/mothership/mutations';
import { saveNetworkPacket } from '@app/store/modules/dashboard';
import { ApolloError } from '@apollo/client/core/core.cjs';
import { logger } from '@app/core';
import {
AccessUrlInputSchema,
NetworkInputSchema,
} from '@app/graphql/generated/client/validators';
import { ZodError } from 'zod';
interface UrlForFieldInput {
url: string;
@@ -271,66 +263,3 @@ export const getServerIps = (
return { urls: safeUrls, errors };
};
export const publishNetwork = async () => {
try {
const client = GraphQLClient.getInstance();
const datapacket = getServerIps();
if (datapacket.errors) {
const zodErrors = datapacket.errors.filter(
(error) => error instanceof ZodError
);
if (zodErrors.length) {
dashboardLogger.warn(
'Validation Errors Encountered with Network Payload: %s',
zodErrors.map((error) => error.message).join(',')
);
}
}
const networkPacket: NetworkInput = { accessUrls: datapacket.urls };
const validatedNetwork = NetworkInputSchema().parse(networkPacket);
const { lastNetworkPacket } = getters.dashboard();
const { apikey: apiKey } = getters.config().remote;
if (
isEqual(
JSON.stringify(lastNetworkPacket),
JSON.stringify(validatedNetwork)
)
) {
dashboardLogger.trace('[DASHBOARD] Skipping Update');
} else if (client) {
dashboardLogger.info(
{ validatedNetwork },
'Sending data packet for network'
);
const result = await client.mutate({
mutation: SEND_NETWORK_MUTATION,
variables: {
apiKey,
data: validatedNetwork,
},
});
dashboardLogger.debug(
{ result },
'Sent network mutation with %s urls',
datapacket.urls.length
);
store.dispatch(
saveNetworkPacket({ lastNetworkPacket: validatedNetwork })
);
}
} catch (error: unknown) {
dashboardLogger.trace('ERROR', error);
if (error instanceof ApolloError) {
dashboardLogger.error(
'Failed publishing with GQL Errors: %s, \nClient Errors: %s',
(error as ApolloError).graphQLErrors.map((error) => error.message).join(','),
(error as ApolloError).clientErrors.join(', ')
);
} else {
dashboardLogger.error(error);
}
}
};

View File

@@ -3,6 +3,7 @@ scalar Long
scalar UUID
scalar DateTime
scalar Port
scalar URL
type Welcome {
message: String!
@@ -16,7 +17,6 @@ type Query {
type Mutation {
login(username: String!, password: String!): String
sendNotification(notification: NotificationInput!): Notification
shutdown: String
reboot: String
}

View File

@@ -42,6 +42,8 @@ input SetupRemoteAccessInput {
type Query {
remoteAccess: RemoteAccess!
extraAllowedOrigins: [String!]!
""" Temporary Type to Enable Swapping Dashboard over in Connect without major changes """
serverDashboard: Dashboard!
}
type Mutation {

View File

@@ -0,0 +1,93 @@
type DashboardApps {
installed: Int
started: Int
}
type DashboardVersions {
unraid: String
}
type DashboardOs {
hostname: String
uptime: DateTime
}
type DashboardVms {
installed: Int
started: Int
}
type ArrayCapacityBytes {
free: Long
used: Long
total: Long
}
type ArrayCapacity {
bytes: ArrayCapacityBytes
}
type DashboardArray {
"""
Current array state
"""
state: String
"""
Current array capacity
"""
capacity: ArrayCapacity
}
type DashboardCase {
icon: String
url: String
error: String
base64: String
}
type DashboardDisplay {
case: DashboardCase
}
type DashboardConfig {
valid: Boolean
error: String
}
type DashboardVars {
regState: String
regTy: String
flashGuid: String
serverName: String
serverDescription: String
}
type DashboardTwoFactorRemote {
enabled: Boolean
}
type DashboardTwoFactorLocal {
enabled: Boolean
}
type DashboardTwoFactor {
remote: DashboardTwoFactorRemote
local: DashboardTwoFactorLocal
}
type Dashboard {
id: ID!
lastPublish: DateTime
online: Boolean
apps: DashboardApps
versions: DashboardVersions
os: DashboardOs
vms: DashboardVms
array: DashboardArray
services: [DashboardService]
display: DashboardDisplay
config: DashboardConfig
vars: DashboardVars
twoFactor: DashboardTwoFactor
network: Network
}

View File

@@ -0,0 +1,33 @@
enum URL_TYPE {
LAN
WIREGUARD
WAN
MDNS
DEFAULT
}
type AccessUrl {
type: URL_TYPE!
name: String
ipv4: URL
ipv6: URL
}
type Network {
accessUrls: [AccessUrl!]
}
input AccessUrlInput {
type: URL_TYPE!
name: String
ipv4: URL
ipv6: URL
}
input NetworkInput {
accessUrls: [AccessUrlInput!]!
}
type Mutation {
updateNetwork(data: NetworkInput!): Network!
}

View File

@@ -0,0 +1,22 @@
type DashboardServiceUptime {
timestamp: DateTime
}
type DashboardService {
name: String
online: Boolean
uptime: DashboardServiceUptime
version: String
}
input DashboardServiceUptimeInput {
timestamp: DateTime!
}
input DashboardServiceInput {
name: String!
online: Boolean!
uptime: DashboardServiceUptimeInput
version: String!
}

View File

@@ -22,10 +22,6 @@ input NotificationFilter {
limit: Int!
}
type Mutation {
sendNotification(notification: NotificationInput!): Notification
}
type Query {
notifications(filter: NotificationFilter!): [Notification!]!
}

View File

@@ -2,10 +2,6 @@
import { minigraphLogger, mothershipLogger } from '@app/core/log';
import { GraphQLClient } from './graphql-client';
import { store } from '@app/store';
import {
startDashboardProducer,
stopDashboardProducer,
} from '@app/store/modules/dashboard';
import {
EVENTS_SUBSCRIPTION,
@@ -57,15 +53,6 @@ export const subscribeToEvents = async (apiKey: string) => {
}
}
// Dashboard Connected to Mothership
if (
type === ClientType.DASHBOARD &&
apiKey === eventApiKey
) {
store.dispatch(startDashboardProducer());
}
break;
}
@@ -80,15 +67,6 @@ export const subscribeToEvents = async (apiKey: string) => {
}
}
// The dashboard was closed or went idle
if (
type === ClientType.DASHBOARD &&
apiKey === eventApiKey
) {
store.dispatch(stopDashboardProducer());
}
break;
}

View File

@@ -5,7 +5,6 @@ import { configReducer } from '@app/store/modules/config';
import { emhttp } from '@app/store/modules/emhttp';
import { registration } from '@app/store/modules/registration';
import { cache } from '@app/store/modules/cache';
import { dashboard } from '@app/store/modules/dashboard';
import { docker } from '@app/store/modules/docker';
import { upnp } from '@app/store/modules/upnp';
import { listenerMiddleware } from '@app/store/listeners/listener-middleware';
@@ -27,7 +26,6 @@ export const store = configureStore({
remoteGraphQL: remoteGraphQLReducer,
notifications: notificationReducer,
cache: cache.reducer,
dashboard: dashboard.reducer,
docker: docker.reducer,
upnp: upnp.reducer,
dynamix: dynamix.reducer,
@@ -45,7 +43,6 @@ export const getters = {
apiKey: () => store.getState().apiKey,
cache: () => store.getState().cache,
config: () => store.getState().config,
dashboard: () => store.getState().dashboard,
docker: () => store.getState().docker,
dynamix: () => store.getState().dynamix,
emhttp: () => store.getState().emhttp,

View File

@@ -2,6 +2,7 @@ import {
DateTimeResolver,
JSONResolver,
PortResolver,
URLResolver,
UUIDResolver,
} from 'graphql-scalars';
import { GraphQLLong } from '@app/graphql/resolvers/graphql-type-long';
@@ -42,6 +43,7 @@ import { print } from 'graphql';
UUID: UUIDResolver,
DateTime: DateTimeResolver,
Port: PortResolver,
URL: URLResolver
},
// schema: schema
}),

View File

@@ -130,4 +130,5 @@ export class CloudResolver {
await store.dispatch(setupRemoteAccessThunk(input)).unwrap();
return true;
}
}

View File

@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { DashboardResolver } from './dashboard.resolver';
describe('DashboardResolver', () => {
let resolver: DashboardResolver;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [DashboardResolver],
}).compile();
resolver = module.get<DashboardResolver>(DashboardResolver);
});
it('should be defined', () => {
expect(resolver).toBeDefined();
});
});

View File

@@ -0,0 +1,38 @@
import { dashboardDataServer } from '@app/common/dashboard/generate-server-data';
import { Dashboard } from '@app/graphql/generated/api/types';
import { getServerIps } from '@app/graphql/resolvers/subscription/network';
import { Query, ResolveField, Resolver } from '@nestjs/graphql';
import { UseRoles } from 'nest-access-control';
import { ZodError } from 'zod';
@Resolver('Dashboard')
@UseRoles({
resource: 'connect',
action: 'read',
possession: 'own',
})
export class DashboardResolver {
@Query('serverDashboard')
public async serverDashboard(): Promise<Dashboard> {
console.log('Dashboard is', await dashboardDataServer());
return await dashboardDataServer();
}
@ResolveField()
public network(): Dashboard['network'] {
const datapacket = getServerIps();
if (datapacket.errors) {
const zodErrors = datapacket.errors.filter(
(error) => error instanceof ZodError
);
if (zodErrors.length) {
console.warn(
'Validation Errors Encountered with Network Payload: %s',
zodErrors.map((error) => error.message).join(',')
);
}
}
const networkPacket: Dashboard['network'] = { accessUrls: datapacket.urls };
return networkPacket;
}
}

View File

@@ -6,7 +6,6 @@ import { UseRoles } from 'nest-access-control';
import { Logger } from '@nestjs/common';
import { type NotificationInput } from '@app/graphql/generated/client/graphql';
import { GraphQLClient } from '@app/mothership/graphql-client';
import { SEND_NOTIFICATION_MUTATION } from '@app/graphql/mothership/mutations';
import { PUBSUB_CHANNEL, createSubscription } from '@app/core/pubsub';
@Resolver()
@@ -44,49 +43,6 @@ export class NotificationsResolver {
.slice(offset, limit + offset);
}
@Mutation('sendNotification')
@UseRoles({
resource: 'notifications',
action: 'create',
possession: 'own',
})
public async sendNotification(
@Args('notification') notification: NotificationInput
) {
this.logger.log('Sending notification', JSON.stringify(notification));
const promise = new Promise((res, rej) => {
setTimeout(async () => {
rej(new GraphQLError('Sending Notification Timeout'));
}, 5_000);
const client = GraphQLClient.getInstance();
// If there's no mothership connection then bail
if (!client) {
this.logger.error('Mothership is not working');
throw new GraphQLError('Mothership is down');
}
client
.query({
query: SEND_NOTIFICATION_MUTATION,
variables: {
notification: notification,
apiKey: getters.config().remote.apikey,
},
})
.then((result) => {
this.logger.debug(
'Query Result from Notifications.ts',
result
);
res(notification);
})
.catch((err) => {
rej(err);
});
});
return promise;
}
@Subscription('notificationAdded')
@UseRoles({
resource: 'notifications',

View File

@@ -14,12 +14,14 @@ import { OwnerResolver } from './owner/owner.resolver';
import { RegistrationResolver } from './registration/registration.resolver';
import { ServerResolver } from './servers/server.resolver';
import { VarsResolver } from './vars/vars.resolver';
import { DashboardResolver } from './dashboard/dashboard.resolver';
@Module({
providers: [
ArrayResolver,
CloudResolver,
ConfigResolver,
DashboardResolver,
DisksResolver,
DockerContainersResolver,
DisplayResolver,