fix: app can be linted (#639)

This commit is contained in:
Eli Bosley
2023-05-02 11:39:35 -04:00
committed by GitHub
parent 085eb3d345
commit 01e0ffcbc0
14 changed files with 1651 additions and 46 deletions
+70
View File
@@ -0,0 +1,70 @@
overwrite: true
emitLegacyCommonJSImports: false
verbose: true
require:
- ts-node/register
config:
namingConvention:
typeNames: './fix-array-type.cjs'
enumValues: 'change-case#upperCase'
useTypeImports: true
scalars:
DateTime: string
Long: number
JSON: "{ [key: string]: any }"
URL: URL
Port: number
UUID: string
generates:
src/graphql/generated/client/:
documents: './src/graphql/mothership/*.ts'
schema:
'${MOTHERSHIP_GRAPHQL_LINK}':
headers:
origin: 'https://forums.unraid.net'
preset: client
presetConfig:
gqlTagName: graphql
config:
useTypeImports: true
withObjectType: true
plugins:
- add: { content: '/* eslint-disable */' }
src/graphql/generate/validators.ts:
schema:
'${MOTHERSHIP_GRAPHQL_LINK}':
headers:
origin: 'https://forums.unraid.net'
plugins:
- typescript-validation-schema
- add: { content: '/* eslint-disable */'}
config:
importFrom: '@app/graphql/generated/client/graphql'
strictScalars: false
schema: 'zod'
# Generate Types for the API Server
src/graphql/generated/api/types.ts:
schema:
- './src/graphql/types.ts'
- './src/graphql/schema/types/**/*.graphql'
plugins:
- typescript
- typescript-resolvers
- add: { content: '/* eslint-disable */' }
config:
contextType: '@app/graphql/schema/utils#Context'
useIndexSignature: true
# Generate Operations for any built in API Server Operations (ie report.ts)
src/graphql/generated/api/operations.ts:
documents: './src/graphql/client/api/*.ts'
schema:
- './src/graphql/types.ts'
- './src/graphql/schema/types/**/*.graphql'
preset: import-types
presetConfig:
typesPath: '@app/graphql/generated/api/types'
plugins:
- typescript-operations
- typed-document-node
- add: { content: '/* eslint-disable */' }
+2 -2
View File
@@ -1,5 +1,5 @@
[api]
version="3.1.0+948d5ecf"
version="3.1.0+0baf1385"
[local]
[notifier]
apikey="unnotify_30994bfaccf839c65bae75f7fa12dd5ee16e69389f754c3b98ed7d5"
@@ -19,4 +19,4 @@ allowedOrigins="/var/run/unraid-notifications.sock, /var/run/unraid-php.sock, /v
[upc]
apikey="unupc_fab6ff6ffe51040595c6d9ffb63a353ba16cc2ad7d93f813a2e80a5810"
[connectionStatus]
minigraph="PRE_INIT"
minigraph="CONNECTED"
@@ -83,14 +83,14 @@ test('Returns generated data', async () => {
},
"os": {
"hostname": "Tower",
"uptime": 2022-06-10T04:35:58.276Z,
"uptime": "2022-06-10T04:35:58.276Z",
},
"services": [
{
"name": "unraid-api",
"online": true,
"uptime": {
"timestamp": 2022-06-10T04:35:58.276Z,
"timestamp": "2022-06-10T04:35:58.276Z",
},
"version": "THIS_WILL_BE_REPLACED_WHEN_BUILT",
},
@@ -98,7 +98,7 @@ test('Returns generated data', async () => {
"name": "dynamic-remote-access",
"online": false,
"uptime": {
"timestamp": 2022-06-10T04:35:58.276Z,
"timestamp": "2022-06-10T04:35:58.276Z",
},
"version": "DISABLED",
},
+1 -1
View File
@@ -63,7 +63,7 @@ export const start = async () => {
// Spawn child
// First arg is path (inside PKG), second arg is restart, stop, etc, rest is args to main argument
const [path, _, ...rest] = process.argv.slice(1);
const [path, , ...rest] = process.argv.slice(1);
const replacedCommand = [path, 'start', ...rest];
const child = spawn(process.execPath, replacedCommand, {
// In the parent set the tracking environment variable
+93
View File
@@ -0,0 +1,93 @@
import { getters, type RootState, store } from '@app/store';
import { uniq } from 'lodash';
import { getServerIps, getUrlForField } from '@app/graphql/resolvers/subscription/network';
import { FileLoadStatus } from '@app/store/types';
import { logger } from '../core';
import { ENVIRONMENT, INTROSPECTION } from '@app/environment';
const getAllowedSocks = (): string[] => [
// Notifier bridge
'/var/run/unraid-notifications.sock',
// Unraid PHP scripts
'/var/run/unraid-php.sock',
// CLI
'/var/run/unraid-cli.sock',
];
const getLocalAccessUrlsForServer = (state: RootState = store.getState()): string[] => {
const { emhttp } = state;
if (emhttp.status !== FileLoadStatus.LOADED) {
return [];
}
const { nginx } = emhttp;
try {
return [
getUrlForField({ url: 'localhost', port: nginx.httpPort }).toString(),
getUrlForField({ url: 'localhost', portSsl: nginx.httpsPort }).toString(),
];
} catch (error: unknown) {
logger.debug('Caught error in getLocalAccessUrlsForServer: \n%o', error);
return [];
}
};
const getRemoteAccessUrlsForAllowedOrigins = (state: RootState = store.getState()): string[] => {
const { urls } = getServerIps(state);
if (urls) {
return urls.reduce<string[]>((acc, curr) => {
if (curr.ipv4 && curr.ipv6) {
acc.push(curr.ipv4.toString());
} else if (curr.ipv4) {
acc.push(curr.ipv4.toString());
} else if (curr.ipv6) {
acc.push(curr.ipv6.toString());
}
return acc;
}, []);
}
return [];
};
const getExtraOrigins = (): string[] => {
const { extraOrigins } = getters.config().api;
if (extraOrigins) {
return extraOrigins.split(', ').filter(origin => origin.startsWith('http://') || origin.startsWith('https://'));
}
return [];
};
const getConnectOrigins = () : string[] => {
const connectMain = 'https://connect.myunraid.net';
const connectStaging = 'https://staging.connect.myunraid.net';
const connectDev = 'https://dev-my.myunraid.net:4000';
return [
connectMain,
connectStaging,
connectDev
]
}
const getApolloSandbox = (): string[] => {
if (INTROSPECTION || ENVIRONMENT === 'development') {
return ['https://studio.apollographql.com'];
}
return [];
}
export const getAllowedOrigins = (state: RootState = store.getState()): string[] => uniq([
...getAllowedSocks(),
...getLocalAccessUrlsForServer(),
...getRemoteAccessUrlsForAllowedOrigins(state),
...getExtraOrigins(),
...getConnectOrigins(),
...getApolloSandbox()
]).map(url => url.endsWith('/') ? url.slice(0, -1) : url);
@@ -0,0 +1,4 @@
import { uptime } from 'os';
// Get uptime on boot and convert to date
export const bootTimestamp = new Date(new Date().getTime() - (uptime() * 1_000));
+13 -28
View File
@@ -1,7 +1,6 @@
import { ConnectListAllDomainsFlags } from '@vmngr/libvirt';
import { getHypervisor } from '@app/core/utils/vms/get-hypervisor';
import display from '@app/graphql/resolvers/query/display';
import { docker } from '@app/core/utils/clients/docker';
import { getUnraidVersion } from '@app/common/dashboard/get-unraid-version';
import { getArray } from '@app/common/dashboard/get-array';
import { bootTimestamp } from '@app/common/dashboard/boot-timestamp';
@@ -37,22 +36,7 @@ const getVmSummary = async (): Promise<DashboardInput['vms']> => {
}
};
/*
const twoFactor = (): Dashboard['twoFactor'] => {
const { isRemoteEnabled, isLocalEnabled } = checkTwoFactorEnabled();
return {
remote: {
enabled: isRemoteEnabled,
},
local: {
enabled: isLocalEnabled,
},
};
}; */
const getDynamicRemoteAccessService = (): DashboardServiceInput | null => {
const uptimeTimestamp = bootTimestamp.toISOString();
const { config, dynamicRemoteAccess } = store.getState();
const enabledStatus = config.remote.dynamicRemoteAccessType;
@@ -61,23 +45,24 @@ const getDynamicRemoteAccessService = (): DashboardServiceInput | null => {
online: enabledStatus !== DynamicRemoteAccessType.DISABLED,
version: dynamicRemoteAccess.runningType,
uptime: {
timestamp: new Date(uptimeTimestamp),
timestamp: bootTimestamp.toISOString(),
},
};
};
const services = (): DashboardInput['services'] => {
const uptimeTimestamp = bootTimestamp.toISOString();
const dynamicRemoteAccess = getDynamicRemoteAccessService();
return [{
name: 'unraid-api',
online: true,
uptime: {
timestamp: new Date(uptimeTimestamp),
},
version: API_VERSION,
},
...(dynamicRemoteAccess ? [dynamicRemoteAccess] : [])];
return [
{
name: 'unraid-api',
online: true,
uptime: {
timestamp: bootTimestamp.toISOString(),
},
version: API_VERSION,
},
...(dynamicRemoteAccess ? [dynamicRemoteAccess] : []),
];
};
const getData = async (): Promise<DashboardInput> => {
@@ -99,7 +84,7 @@ const getData = async (): Promise<DashboardInput> => {
},
os: {
hostname: emhttp.var.name,
uptime: new Date(bootTimestamp.toISOString()),
uptime: bootTimestamp.toISOString()
},
vms: await getVmSummary(),
array: getArray(),
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { getters } from '@app/store';
import type { DiskShare, Share, UserShare } from '@app/core/types/states/share';
import { type ArrayDisk } from '@app/graphql/generated/api/types';
+214
View File
@@ -0,0 +1,214 @@
/* eslint-disable */
import { z } from 'zod'
import { AccessUrlInput, ArrayCapacityBytesInput, ArrayCapacityInput, ClientType, ConfigErrorState, DashboardAppsInput, DashboardArrayInput, DashboardCaseInput, DashboardConfigInput, DashboardDisplayInput, DashboardInput, DashboardOsInput, DashboardServiceInput, DashboardServiceUptimeInput, DashboardTwoFactorInput, DashboardTwoFactorLocalInput, DashboardTwoFactorRemoteInput, DashboardVarsInput, DashboardVersionsInput, DashboardVmsInput, EventType, Importance, KeyType, NetworkInput, NotificationInput, NotificationStatus, PingEventSource, RegistrationState, RemoteAccessEventActionType, RemoteAccessInput, RemoteGraphQLClientInput, RemoteGraphQLEventType, RemoteGraphQLServerInput, ServerStatus, URL_TYPE, UpdateType } from '@app/graphql/generated/client/graphql'
type Properties<T> = Required<{
[K in keyof T]: z.ZodType<T[K], any, T[K]>;
}>;
type definedNonNullAny = {};
export const isDefinedNonNullAny = (v: any): v is definedNonNullAny => v !== undefined && v !== null;
export const definedNonNullAnySchema = z.any().refine((v) => isDefinedNonNullAny(v));
export function AccessUrlInputSchema(): z.ZodObject<Properties<AccessUrlInput>> {
return z.object<Properties<AccessUrlInput>>({
ipv4: definedNonNullAnySchema.nullish(),
ipv6: definedNonNullAnySchema.nullish(),
name: z.string().nullish(),
type: definedNonNullAnySchema
})
}
export function ArrayCapacityBytesInputSchema(): z.ZodObject<Properties<ArrayCapacityBytesInput>> {
return z.object<Properties<ArrayCapacityBytesInput>>({
free: z.number().nullish(),
total: z.number().nullish(),
used: z.number().nullish()
})
}
export function ArrayCapacityInputSchema(): z.ZodObject<Properties<ArrayCapacityInput>> {
return z.object<Properties<ArrayCapacityInput>>({
bytes: z.lazy(() => definedNonNullAnySchema.nullish())
})
}
export const ClientTypeSchema = z.nativeEnum(ClientType);
export const ConfigErrorStateSchema = z.nativeEnum(ConfigErrorState);
export function DashboardAppsInputSchema(): z.ZodObject<Properties<DashboardAppsInput>> {
return z.object<Properties<DashboardAppsInput>>({
installed: z.number(),
started: z.number()
})
}
export function DashboardArrayInputSchema(): z.ZodObject<Properties<DashboardArrayInput>> {
return z.object<Properties<DashboardArrayInput>>({
capacity: z.lazy(() => definedNonNullAnySchema),
state: z.string()
})
}
export function DashboardCaseInputSchema(): z.ZodObject<Properties<DashboardCaseInput>> {
return z.object<Properties<DashboardCaseInput>>({
base64: z.string(),
error: z.string().nullish(),
icon: z.string(),
url: z.string()
})
}
export function DashboardConfigInputSchema(): z.ZodObject<Properties<DashboardConfigInput>> {
return z.object<Properties<DashboardConfigInput>>({
error: z.string().nullish(),
valid: z.boolean()
})
}
export function DashboardDisplayInputSchema(): z.ZodObject<Properties<DashboardDisplayInput>> {
return z.object<Properties<DashboardDisplayInput>>({
case: z.lazy(() => definedNonNullAnySchema)
})
}
export function DashboardInputSchema(): z.ZodObject<Properties<DashboardInput>> {
return z.object<Properties<DashboardInput>>({
apps: z.lazy(() => definedNonNullAnySchema),
array: z.lazy(() => definedNonNullAnySchema),
config: z.lazy(() => definedNonNullAnySchema),
display: z.lazy(() => definedNonNullAnySchema),
os: z.lazy(() => definedNonNullAnySchema),
services: z.array(z.lazy(() => definedNonNullAnySchema)),
twoFactor: z.lazy(() => definedNonNullAnySchema.nullish()),
vars: z.lazy(() => definedNonNullAnySchema),
versions: z.lazy(() => definedNonNullAnySchema),
vms: z.lazy(() => definedNonNullAnySchema)
})
}
export function DashboardOsInputSchema(): z.ZodObject<Properties<DashboardOsInput>> {
return z.object<Properties<DashboardOsInput>>({
hostname: z.string(),
uptime: z.string()
})
}
export function DashboardServiceInputSchema(): z.ZodObject<Properties<DashboardServiceInput>> {
return z.object<Properties<DashboardServiceInput>>({
name: z.string(),
online: z.boolean(),
uptime: z.lazy(() => definedNonNullAnySchema.nullish()),
version: z.string()
})
}
export function DashboardServiceUptimeInputSchema(): z.ZodObject<Properties<DashboardServiceUptimeInput>> {
return z.object<Properties<DashboardServiceUptimeInput>>({
timestamp: z.string()
})
}
export function DashboardTwoFactorInputSchema(): z.ZodObject<Properties<DashboardTwoFactorInput>> {
return z.object<Properties<DashboardTwoFactorInput>>({
local: z.lazy(() => definedNonNullAnySchema),
remote: z.lazy(() => definedNonNullAnySchema)
})
}
export function DashboardTwoFactorLocalInputSchema(): z.ZodObject<Properties<DashboardTwoFactorLocalInput>> {
return z.object<Properties<DashboardTwoFactorLocalInput>>({
enabled: z.boolean()
})
}
export function DashboardTwoFactorRemoteInputSchema(): z.ZodObject<Properties<DashboardTwoFactorRemoteInput>> {
return z.object<Properties<DashboardTwoFactorRemoteInput>>({
enabled: z.boolean()
})
}
export function DashboardVarsInputSchema(): z.ZodObject<Properties<DashboardVarsInput>> {
return z.object<Properties<DashboardVarsInput>>({
flashGuid: z.string(),
regState: z.string(),
regTy: z.string()
})
}
export function DashboardVersionsInputSchema(): z.ZodObject<Properties<DashboardVersionsInput>> {
return z.object<Properties<DashboardVersionsInput>>({
unraid: z.string()
})
}
export function DashboardVmsInputSchema(): z.ZodObject<Properties<DashboardVmsInput>> {
return z.object<Properties<DashboardVmsInput>>({
installed: z.number(),
started: z.number()
})
}
export const EventTypeSchema = z.nativeEnum(EventType);
export const ImportanceSchema = z.nativeEnum(Importance);
export const KeyTypeSchema = z.nativeEnum(KeyType);
export function NetworkInputSchema(): z.ZodObject<Properties<NetworkInput>> {
return z.object<Properties<NetworkInput>>({
accessUrls: z.array(z.lazy(() => definedNonNullAnySchema))
})
}
export function NotificationInputSchema(): z.ZodObject<Properties<NotificationInput>> {
return z.object<Properties<NotificationInput>>({
description: z.string().nullish(),
importance: definedNonNullAnySchema,
link: z.string().nullish(),
subject: z.string().nullish(),
title: z.string().nullish()
})
}
export const NotificationStatusSchema = z.nativeEnum(NotificationStatus);
export const PingEventSourceSchema = z.nativeEnum(PingEventSource);
export const RegistrationStateSchema = z.nativeEnum(RegistrationState);
export const RemoteAccessEventActionTypeSchema = z.nativeEnum(RemoteAccessEventActionType);
export function RemoteAccessInputSchema(): z.ZodObject<Properties<RemoteAccessInput>> {
return z.object<Properties<RemoteAccessInput>>({
apiKey: z.string(),
type: definedNonNullAnySchema,
url: z.lazy(() => definedNonNullAnySchema.nullish())
})
}
export function RemoteGraphQLClientInputSchema(): z.ZodObject<Properties<RemoteGraphQLClientInput>> {
return z.object<Properties<RemoteGraphQLClientInput>>({
apiKey: z.string(),
body: z.string()
})
}
export const RemoteGraphQLEventTypeSchema = z.nativeEnum(RemoteGraphQLEventType);
export function RemoteGraphQLServerInputSchema(): z.ZodObject<Properties<RemoteGraphQLServerInput>> {
return z.object<Properties<RemoteGraphQLServerInput>>({
body: z.string(),
sha256: z.string(),
type: definedNonNullAnySchema
})
}
export const ServerStatusSchema = z.nativeEnum(ServerStatus);
export const URL_TYPESchema = z.nativeEnum(URL_TYPE);
export const UpdateTypeSchema = z.nativeEnum(UpdateType);
+1 -1
View File
@@ -14,7 +14,7 @@ export type Scalars = {
Boolean: boolean;
Int: number;
Float: number;
DateTime: Date;
DateTime: string;
JSON: { [key: string]: any };
Long: number;
UUID: string;
+770
View File
@@ -0,0 +1,770 @@
/* eslint-disable */
import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
export type Maybe<T> = T | null;
export type InputMaybe<T> = Maybe<T>;
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
ID: string;
String: string;
Boolean: boolean;
Int: number;
Float: number;
/** A date-time string at UTC, such as 2007-12-03T10:15:30Z, compliant with the `date-time` format outlined in section 5.6 of the RFC 3339 profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar. */
DateTime: string;
/** A field whose value is a IPv4 address: https://en.wikipedia.org/wiki/IPv4. */
IPv4: any;
/** A field whose value is a IPv6 address: https://en.wikipedia.org/wiki/IPv6. */
IPv6: any;
/** The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). */
JSON: { [key: string]: any };
/** The `Long` scalar type represents 52-bit integers */
Long: number;
/** A field whose value is a valid TCP port within the range of 0 to 65535: https://en.wikipedia.org/wiki/Transmission_Control_Protocol#TCP_ports */
Port: number;
/** A field whose value conforms to the standard URL format as specified in RFC3986: https://www.ietf.org/rfc/rfc3986.txt. */
URL: URL;
};
export type AccessUrl = {
__typename?: 'AccessUrl';
ipv4?: Maybe<Scalars['URL']>;
ipv6?: Maybe<Scalars['URL']>;
name?: Maybe<Scalars['String']>;
type: URL_TYPE;
};
export type AccessUrlInput = {
ipv4?: InputMaybe<Scalars['URL']>;
ipv6?: InputMaybe<Scalars['URL']>;
name?: InputMaybe<Scalars['String']>;
type: URL_TYPE;
};
export type ArrayCapacity = {
__typename?: 'ArrayCapacity';
bytes?: Maybe<ArrayCapacityBytes>;
};
export type ArrayCapacityBytes = {
__typename?: 'ArrayCapacityBytes';
free?: Maybe<Scalars['Long']>;
total?: Maybe<Scalars['Long']>;
used?: Maybe<Scalars['Long']>;
};
export type ArrayCapacityBytesInput = {
free?: InputMaybe<Scalars['Long']>;
total?: InputMaybe<Scalars['Long']>;
used?: InputMaybe<Scalars['Long']>;
};
export type ArrayCapacityInput = {
bytes?: InputMaybe<ArrayCapacityBytesInput>;
};
export type ClientConnectedEvent = {
__typename?: 'ClientConnectedEvent';
data: ClientConnectionEventData;
type: EventType;
};
export type ClientConnectionEventData = {
__typename?: 'ClientConnectionEventData';
apiKey: Scalars['String'];
type: ClientType;
version: Scalars['String'];
};
export type ClientDisconnectedEvent = {
__typename?: 'ClientDisconnectedEvent';
data: ClientConnectionEventData;
type: EventType;
};
export type ClientPingEvent = {
__typename?: 'ClientPingEvent';
data: PingEventData;
type: EventType;
};
export enum ClientType {
API = 'API',
DASHBOARD = 'DASHBOARD'
}
export type Config = {
__typename?: 'Config';
error?: Maybe<ConfigErrorState>;
valid?: Maybe<Scalars['Boolean']>;
};
export enum ConfigErrorState {
INVALID = 'INVALID',
NO_KEY_SERVER = 'NO_KEY_SERVER',
UNKNOWN_ERROR = 'UNKNOWN_ERROR',
WITHDRAWN = 'WITHDRAWN'
}
export type Dashboard = {
__typename?: 'Dashboard';
apps?: Maybe<DashboardApps>;
array?: Maybe<DashboardArray>;
config?: Maybe<DashboardConfig>;
display?: Maybe<DashboardDisplay>;
id: Scalars['ID'];
lastPublish?: Maybe<Scalars['DateTime']>;
network?: Maybe<Network>;
online?: Maybe<Scalars['Boolean']>;
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']>;
started?: Maybe<Scalars['Int']>;
};
export type DashboardAppsInput = {
installed: Scalars['Int'];
started: Scalars['Int'];
};
export type DashboardArray = {
__typename?: 'DashboardArray';
/** Current array capacity */
capacity?: Maybe<ArrayCapacity>;
/** Current array state */
state?: Maybe<Scalars['String']>;
};
export type DashboardArrayInput = {
/** Current array capacity */
capacity: ArrayCapacityInput;
/** Current array state */
state: Scalars['String'];
};
export type DashboardCase = {
__typename?: 'DashboardCase';
base64?: Maybe<Scalars['String']>;
error?: Maybe<Scalars['String']>;
icon?: Maybe<Scalars['String']>;
url?: Maybe<Scalars['String']>;
};
export type DashboardCaseInput = {
base64: Scalars['String'];
error?: InputMaybe<Scalars['String']>;
icon: Scalars['String'];
url: Scalars['String'];
};
export type DashboardConfig = {
__typename?: 'DashboardConfig';
error?: Maybe<Scalars['String']>;
valid?: Maybe<Scalars['Boolean']>;
};
export type DashboardConfigInput = {
error?: InputMaybe<Scalars['String']>;
valid: Scalars['Boolean'];
};
export type DashboardDisplay = {
__typename?: 'DashboardDisplay';
case?: Maybe<DashboardCase>;
};
export type DashboardDisplayInput = {
case: DashboardCaseInput;
};
export type DashboardInput = {
apps: DashboardAppsInput;
array: DashboardArrayInput;
config: DashboardConfigInput;
display: DashboardDisplayInput;
os: DashboardOsInput;
services: Array<DashboardServiceInput>;
twoFactor?: InputMaybe<DashboardTwoFactorInput>;
vars: DashboardVarsInput;
versions: DashboardVersionsInput;
vms: DashboardVmsInput;
};
export type DashboardOs = {
__typename?: 'DashboardOs';
hostname?: Maybe<Scalars['String']>;
uptime?: Maybe<Scalars['DateTime']>;
};
export type DashboardOsInput = {
hostname: Scalars['String'];
uptime: Scalars['DateTime'];
};
export type DashboardService = {
__typename?: 'DashboardService';
name?: Maybe<Scalars['String']>;
online?: Maybe<Scalars['Boolean']>;
uptime?: Maybe<DashboardServiceUptime>;
version?: Maybe<Scalars['String']>;
};
export type DashboardServiceInput = {
name: Scalars['String'];
online: Scalars['Boolean'];
uptime?: InputMaybe<DashboardServiceUptimeInput>;
version: Scalars['String'];
};
export type DashboardServiceUptime = {
__typename?: 'DashboardServiceUptime';
timestamp?: Maybe<Scalars['DateTime']>;
};
export type DashboardServiceUptimeInput = {
timestamp: Scalars['DateTime'];
};
export type DashboardTwoFactor = {
__typename?: 'DashboardTwoFactor';
local?: Maybe<DashboardTwoFactorLocal>;
remote?: Maybe<DashboardTwoFactorRemote>;
};
export type DashboardTwoFactorInput = {
local: DashboardTwoFactorLocalInput;
remote: DashboardTwoFactorRemoteInput;
};
export type DashboardTwoFactorLocal = {
__typename?: 'DashboardTwoFactorLocal';
enabled?: Maybe<Scalars['Boolean']>;
};
export type DashboardTwoFactorLocalInput = {
enabled: Scalars['Boolean'];
};
export type DashboardTwoFactorRemote = {
__typename?: 'DashboardTwoFactorRemote';
enabled?: Maybe<Scalars['Boolean']>;
};
export type DashboardTwoFactorRemoteInput = {
enabled: Scalars['Boolean'];
};
export type DashboardVars = {
__typename?: 'DashboardVars';
flashGuid?: Maybe<Scalars['String']>;
regState?: Maybe<Scalars['String']>;
regTy?: Maybe<Scalars['String']>;
};
export type DashboardVarsInput = {
flashGuid: Scalars['String'];
regState: Scalars['String'];
regTy: Scalars['String'];
};
export type DashboardVersions = {
__typename?: 'DashboardVersions';
unraid?: Maybe<Scalars['String']>;
};
export type DashboardVersionsInput = {
unraid: Scalars['String'];
};
export type DashboardVms = {
__typename?: 'DashboardVms';
installed?: Maybe<Scalars['Int']>;
started?: Maybe<Scalars['Int']>;
};
export type DashboardVmsInput = {
installed: Scalars['Int'];
started: Scalars['Int'];
};
export type Event = ClientConnectedEvent | ClientDisconnectedEvent | ClientPingEvent | RemoteAccessEvent | RemoteGraphQLEvent | UpdateEvent;
export enum EventType {
CLIENT_CONNECTED_EVENT = 'CLIENT_CONNECTED_EVENT',
CLIENT_DISCONNECTED_EVENT = 'CLIENT_DISCONNECTED_EVENT',
CLIENT_PING_EVENT = 'CLIENT_PING_EVENT',
REMOTE_ACCESS_EVENT = 'REMOTE_ACCESS_EVENT',
REMOTE_GRAPHQL_EVENT = 'REMOTE_GRAPHQL_EVENT',
UPDATE_EVENT = 'UPDATE_EVENT'
}
export type FullServerDetails = {
__typename?: 'FullServerDetails';
apiConnectedCount?: Maybe<Scalars['Int']>;
apiVersion?: Maybe<Scalars['String']>;
connectionTimestamp?: Maybe<Scalars['String']>;
dashboard?: Maybe<Dashboard>;
lastPublish?: Maybe<Scalars['String']>;
network?: Maybe<Network>;
online?: Maybe<Scalars['Boolean']>;
};
export enum Importance {
ALERT = 'ALERT',
INFO = 'INFO',
WARNING = 'WARNING'
}
export enum KeyType {
BASIC = 'BASIC',
PLUS = 'PLUS',
PRO = 'PRO',
TRIAL = 'TRIAL'
}
export type KsServerDetails = {
__typename?: 'KsServerDetails';
accessLabel: Scalars['String'];
accessUrl: Scalars['String'];
apiKey?: Maybe<Scalars['String']>;
description: Scalars['String'];
dnsHash: Scalars['String'];
flashBackupDate?: Maybe<Scalars['Int']>;
flashBackupUrl: Scalars['String'];
flashProduct: Scalars['String'];
flashVendor: Scalars['String'];
guid: Scalars['String'];
ipsId: Scalars['String'];
keyType: KeyType;
licenseKey: Scalars['String'];
name: Scalars['String'];
plgVersion?: Maybe<Scalars['String']>;
signedIn: Scalars['Boolean'];
};
export type LegacyService = {
__typename?: 'LegacyService';
name?: Maybe<Scalars['String']>;
online?: Maybe<Scalars['Boolean']>;
uptime?: Maybe<Scalars['Int']>;
version?: Maybe<Scalars['String']>;
};
export type Mutation = {
__typename?: 'Mutation';
remoteGraphQLResponse: Scalars['Boolean'];
remoteMutation: Scalars['String'];
remoteSession?: Maybe<Scalars['Boolean']>;
sendNotification?: Maybe<Notification>;
sendPing?: Maybe<Scalars['Boolean']>;
updateDashboard: Dashboard;
updateNetwork: Network;
};
export type MutationremoteGraphQLResponseArgs = {
input: RemoteGraphQLServerInput;
};
export type MutationremoteMutationArgs = {
input: RemoteGraphQLClientInput;
};
export type MutationremoteSessionArgs = {
remoteAccess: RemoteAccessInput;
};
export type MutationsendNotificationArgs = {
notification: NotificationInput;
};
export type MutationupdateDashboardArgs = {
data: DashboardInput;
};
export type MutationupdateNetworkArgs = {
data: NetworkInput;
};
export type Network = {
__typename?: 'Network';
accessUrls?: Maybe<Array<AccessUrl>>;
};
export type NetworkInput = {
accessUrls: Array<AccessUrlInput>;
};
export type Notification = {
__typename?: 'Notification';
description?: Maybe<Scalars['String']>;
importance?: Maybe<Importance>;
link?: Maybe<Scalars['String']>;
status: NotificationStatus;
subject?: Maybe<Scalars['String']>;
title?: Maybe<Scalars['String']>;
};
export type NotificationInput = {
description?: InputMaybe<Scalars['String']>;
importance: Importance;
link?: InputMaybe<Scalars['String']>;
subject?: InputMaybe<Scalars['String']>;
title?: InputMaybe<Scalars['String']>;
};
export enum NotificationStatus {
FAILED_TO_SEND = 'FAILED_TO_SEND',
NOT_FOUND = 'NOT_FOUND',
PENDING = 'PENDING',
SENT = 'SENT'
}
export type PingEvent = {
__typename?: 'PingEvent';
data?: Maybe<Scalars['String']>;
type: EventType;
};
export type PingEventData = {
__typename?: 'PingEventData';
source: PingEventSource;
};
export enum PingEventSource {
API = 'API',
MOTHERSHIP = 'MOTHERSHIP'
}
export type ProfileModel = {
__typename?: 'ProfileModel';
avatar?: Maybe<Scalars['String']>;
url?: Maybe<Scalars['String']>;
userId?: Maybe<Scalars['ID']>;
username?: Maybe<Scalars['String']>;
};
export type Query = {
__typename?: 'Query';
apiVersion?: Maybe<Scalars['String']>;
dashboard?: Maybe<Dashboard>;
ksServers: Array<KsServerDetails>;
online?: Maybe<Scalars['Boolean']>;
remoteQuery: Scalars['String'];
servers: Array<Maybe<Server>>;
status?: Maybe<ServerStatus>;
};
export type QuerydashboardArgs = {
id: Scalars['String'];
};
export type QueryremoteQueryArgs = {
input: RemoteGraphQLClientInput;
};
export enum RegistrationState {
/** Basic */
BASIC = 'BASIC',
/** BLACKLISTED */
EBLACKLISTED = 'EBLACKLISTED',
/** BLACKLISTED */
EBLACKLISTED1 = 'EBLACKLISTED1',
/** BLACKLISTED */
EBLACKLISTED2 = 'EBLACKLISTED2',
/** Trial Expired */
EEXPIRED = 'EEXPIRED',
/** GUID Error */
EGUID = 'EGUID',
/** Multiple License Keys Present */
EGUID1 = 'EGUID1',
/** Trial Requires Internet Connection */
ENOCONN = 'ENOCONN',
/** No Flash */
ENOFLASH = 'ENOFLASH',
ENOFLASH1 = 'ENOFLASH1',
ENOFLASH2 = 'ENOFLASH2',
ENOFLASH3 = 'ENOFLASH3',
ENOFLASH4 = 'ENOFLASH4',
ENOFLASH5 = 'ENOFLASH5',
ENOFLASH6 = 'ENOFLASH6',
ENOFLASH7 = 'ENOFLASH7',
/** No Keyfile */
ENOKEYFILE = 'ENOKEYFILE',
/** No Keyfile */
ENOKEYFILE1 = 'ENOKEYFILE1',
/** Missing key file */
ENOKEYFILE2 = 'ENOKEYFILE2',
/** Invalid installation */
ETRIAL = 'ETRIAL',
/** Plus */
PLUS = 'PLUS',
/** Pro */
PRO = 'PRO',
/** Trial */
TRIAL = 'TRIAL'
}
export type RemoteAccessEvent = {
__typename?: 'RemoteAccessEvent';
data: RemoteAccessEventData;
type: EventType;
};
/** Defines whether remote access event is the initiation (from connect) or the response (from the server) */
export enum RemoteAccessEventActionType {
ACK = 'ACK',
END = 'END',
INIT = 'INIT',
PING = 'PING'
}
export type RemoteAccessEventData = {
__typename?: 'RemoteAccessEventData';
apiKey: Scalars['String'];
type: RemoteAccessEventActionType;
url?: Maybe<AccessUrl>;
};
export type RemoteAccessInput = {
apiKey: Scalars['String'];
type: RemoteAccessEventActionType;
url?: InputMaybe<AccessUrlInput>;
};
export type RemoteGraphQLClientInput = {
apiKey: Scalars['String'];
body: Scalars['String'];
};
export type RemoteGraphQLEvent = {
__typename?: 'RemoteGraphQLEvent';
data: RemoteGraphQLEventData;
type: EventType;
};
export type RemoteGraphQLEventData = {
__typename?: 'RemoteGraphQLEventData';
/** Contains mutation / subscription / query data in the form of body: JSON, variables: JSON */
body: Scalars['String'];
/** sha256 hash of the body */
sha256: Scalars['String'];
type: RemoteGraphQLEventType;
};
export enum RemoteGraphQLEventType {
REMOTE_MUTATION_EVENT = 'REMOTE_MUTATION_EVENT',
REMOTE_QUERY_EVENT = 'REMOTE_QUERY_EVENT',
REMOTE_SUBSCRIPTION_EVENT = 'REMOTE_SUBSCRIPTION_EVENT',
REMOTE_SUBSCRIPTION_EVENT_PING = 'REMOTE_SUBSCRIPTION_EVENT_PING'
}
export type RemoteGraphQLServerInput = {
/** Body - contains an object containing data: (GQL response data) or errors: (GQL Errors) */
body: Scalars['String'];
/** sha256 hash of the body */
sha256: Scalars['String'];
type: RemoteGraphQLEventType;
};
export type Server = {
__typename?: 'Server';
apikey?: Maybe<Scalars['String']>;
guid?: Maybe<Scalars['String']>;
lanip?: Maybe<Scalars['String']>;
localurl?: Maybe<Scalars['String']>;
name?: Maybe<Scalars['String']>;
owner?: Maybe<ProfileModel>;
remoteurl?: Maybe<Scalars['String']>;
status?: Maybe<ServerStatus>;
wanip?: Maybe<Scalars['String']>;
};
/** Defines server fields that have a TTL on them, for example last ping */
export type ServerFieldsWithTtl = {
__typename?: 'ServerFieldsWithTtl';
lastPing?: Maybe<Scalars['String']>;
};
export type ServerModel = {
apikey: Scalars['String'];
guid: Scalars['String'];
lanip: Scalars['String'];
localurl: Scalars['String'];
name: Scalars['String'];
remoteurl: Scalars['String'];
wanip: Scalars['String'];
};
export enum ServerStatus {
NEVER_CONNECTED = 'never_connected',
OFFLINE = 'offline',
ONLINE = 'online'
}
export type Service = {
__typename?: 'Service';
name?: Maybe<Scalars['String']>;
online?: Maybe<Scalars['Boolean']>;
uptime?: Maybe<Uptime>;
version?: Maybe<Scalars['String']>;
};
export type Subscription = {
__typename?: 'Subscription';
events?: Maybe<Array<Event>>;
remoteSubscription: Scalars['String'];
servers: Array<Server>;
};
export type SubscriptionremoteSubscriptionArgs = {
input: RemoteGraphQLClientInput;
};
export type TwoFactorLocal = {
__typename?: 'TwoFactorLocal';
enabled?: Maybe<Scalars['Boolean']>;
};
export type TwoFactorRemote = {
__typename?: 'TwoFactorRemote';
enabled?: Maybe<Scalars['Boolean']>;
};
export type TwoFactorWithToken = {
__typename?: 'TwoFactorWithToken';
local?: Maybe<TwoFactorLocal>;
remote?: Maybe<TwoFactorRemote>;
token?: Maybe<Scalars['String']>;
};
export type TwoFactorWithoutToken = {
__typename?: 'TwoFactorWithoutToken';
local?: Maybe<TwoFactorLocal>;
remote?: Maybe<TwoFactorRemote>;
};
export enum URL_TYPE {
DEFAULT = 'DEFAULT',
LAN = 'LAN',
MDNS = 'MDNS',
WAN = 'WAN',
WIREGUARD = 'WIREGUARD'
}
export type UpdateEvent = {
__typename?: 'UpdateEvent';
data: UpdateEventData;
type: EventType;
};
export type UpdateEventData = {
__typename?: 'UpdateEventData';
apiKey: Scalars['String'];
type: UpdateType;
};
export enum UpdateType {
DASHBOARD = 'DASHBOARD',
NETWORK = 'NETWORK'
}
export type Uptime = {
__typename?: 'Uptime';
timestamp?: Maybe<Scalars['String']>;
};
export type UserProfileModelWithServers = {
__typename?: 'UserProfileModelWithServers';
profile: ProfileModel;
servers: Array<Server>;
};
export type Vars = {
__typename?: 'Vars';
expireTime?: Maybe<Scalars['DateTime']>;
flashGuid?: Maybe<Scalars['String']>;
regState?: Maybe<RegistrationState>;
regTm2?: Maybe<Scalars['String']>;
regTy?: Maybe<Scalars['String']>;
};
export type updateDashboardMutationVariables = Exact<{
data: DashboardInput;
apiKey: Scalars['String'];
}>;
export type updateDashboardMutation = { __typename?: 'Mutation', updateDashboard: { __typename?: 'Dashboard', apps?: { __typename?: 'DashboardApps', installed?: number | null } | null } };
export type sendNotificationMutationVariables = Exact<{
notification: NotificationInput;
apiKey: Scalars['String'];
}>;
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'];
}>;
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;
}>;
export type sendRemoteAccessMutationMutation = { __typename?: 'Mutation', remoteSession?: boolean | null };
export type sendRemoteGraphQLResponseMutationVariables = Exact<{
input: RemoteGraphQLServerInput;
}>;
export type sendRemoteGraphQLResponseMutation = { __typename?: 'Mutation', remoteGraphQLResponse: boolean };
export type RemoteGraphQLEventFragmentFragment = { __typename?: 'RemoteGraphQLEvent', remoteGraphQLEventData: { __typename?: 'RemoteGraphQLEventData', type: RemoteGraphQLEventType, body: string, sha256: string } } & { ' $fragmentName'?: 'RemoteGraphQLEventFragmentFragment' };
export type RemoteAccessEventFragmentFragment = { __typename?: 'RemoteAccessEvent', type: EventType, data: { __typename?: 'RemoteAccessEventData', type: RemoteAccessEventActionType, apiKey: string, url?: { __typename?: 'AccessUrl', type: URL_TYPE, name?: string | null, ipv4?: URL | null, ipv6?: URL | null } | null } } & { ' $fragmentName'?: 'RemoteAccessEventFragmentFragment' };
export type eventsSubscriptionVariables = Exact<{ [key: string]: never; }>;
export type eventsSubscription = { __typename?: 'Subscription', events?: Array<{ __typename: 'ClientConnectedEvent', connectedEvent: EventType, connectedData: { __typename?: 'ClientConnectionEventData', type: ClientType, version: string, apiKey: string } } | { __typename: 'ClientDisconnectedEvent', disconnectedEvent: EventType, disconnectedData: { __typename?: 'ClientConnectionEventData', type: ClientType, version: string, apiKey: string } } | { __typename: 'ClientPingEvent' } | (
{ __typename: 'RemoteAccessEvent' }
& { ' $fragmentRefs'?: { 'RemoteAccessEventFragmentFragment': RemoteAccessEventFragmentFragment } }
) | (
{ __typename: 'RemoteGraphQLEvent' }
& { ' $fragmentRefs'?: { 'RemoteGraphQLEventFragmentFragment': RemoteGraphQLEventFragmentFragment } }
) | { __typename: 'UpdateEvent' }> | null };
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>;
+464
View File
@@ -0,0 +1,464 @@
import {
baseboard,
cpu,
cpuFlags,
mem,
memLayout,
osInfo,
system,
versions,
} from 'systeminformation';
import { docker } from '@app/core/utils/clients/docker';
import {
type InfoApps,
type Os as InfoOs,
type InfoCpu,
type Display,
type Theme,
type Temperature,
type Baseboard,
type Versions,
type InfoMemory,
type MemoryLayout,
type System,
type Devices,
type InfoResolvers,
type Gpu,
} from '@app/graphql/generated/api/types';
import { getters } from '@app/store';
import { loadState } from '@app/core/utils/misc/load-state';
import { type DynamixConfig } from '@app/core/types/ini';
import { toBoolean } from '@app/core/utils/casting';
import toBytes from 'bytes';
import { getUnraidVersion } from '@app/common/dashboard/get-unraid-version';
import { AppError } from '@app/core/errors/app-error';
import { cleanStdout } from '@app/core/utils/misc/clean-stdout';
import { getMachineId } from '@app/core/utils/misc/get-machine-id';
import { execaCommandSync, execa } from 'execa';
import { pathExists } from 'path-exists';
import { filter as asyncFilter } from 'p-iteration';
import { isSymlink } from 'path-type';
import type { PciDevice } from '@app/core/types';
import { vmRegExps } from '@app/core/utils/vms/domain/vm-regexps';
import { getPciDevices } from '@app/core/utils/vms/get-pci-devices';
import { filterDevices } from '@app/core/utils/vms/filter-devices';
import { sanitizeVendor } from '@app/core/utils/vms/domain/sanitize-vendor';
import { sanitizeProduct } from '@app/core/utils/vms/domain/sanitize-product';
import { bootTimestamp } from '@app/common/dashboard/boot-timestamp';
export const generateApps = async (): Promise<InfoApps> => {
const installed = await docker
.listContainers({ all: true })
.catch(() => [])
.then((containers) => containers.length);
const started = await docker
.listContainers()
.catch(() => [])
.then((containers) => containers.length);
return { installed, started };
};
const generateOs = async (): Promise<InfoOs> => {
const os = await osInfo();
return {
...os,
uptime: bootTimestamp.toISOString(),
};
};
const generateCpu = async (): Promise<InfoCpu> => {
const { cores, physicalCores, speedMin, speedMax, stepping, ...rest } =
await cpu();
const flags = await cpuFlags().then((flags) => flags.split(' '));
return {
...rest,
cores: physicalCores,
threads: cores,
flags,
stepping: Number(stepping),
// @TODO Find out what these should be if they're not defined
speedmin: speedMin || -1,
speedmax: speedMax || -1,
};
};
const generateDisplay = async (): Promise<Display> => {
const filePath = getters.paths()['dynamix-config'];
const state = loadState<DynamixConfig>(filePath);
if (!state) {
return {};
}
const { theme, unit, ...display } = state.display;
return {
...display,
theme: theme as Theme,
unit: unit as Temperature,
scale: toBoolean(display.scale),
tabs: toBoolean(display.tabs),
resize: toBoolean(display.resize),
wwn: toBoolean(display.wwn),
total: toBoolean(display.total),
usage: toBoolean(display.usage),
text: toBoolean(display.text),
warning: Number.parseInt(display.warning, 10),
critical: Number.parseInt(display.critical, 10),
hot: Number.parseInt(display.hot, 10),
max: Number.parseInt(display.max, 10),
locale: display.locale || 'en_US',
};
};
const generateBaseboard = async (): Promise<Baseboard> => baseboard();
const generateVersions = async (): Promise<Versions> => {
const unraid = await getUnraidVersion();
const softwareVersions = await versions();
return {
unraid,
...softwareVersions,
};
};
const generateMemory = async (): Promise<InfoMemory> => {
const layout = await memLayout().then((dims) =>
dims.map((dim) => dim as MemoryLayout)
);
const info = await mem();
let max = info.total;
// Max memory
try {
const memoryInfo = await execa('dmidecode', ['-t', 'memory'])
.then(cleanStdout)
.catch((error: NodeJS.ErrnoException) => {
if (error.code === 'ENOENT') {
throw new AppError('The dmidecode cli utility is missing.');
}
throw error;
});
const lines = memoryInfo.split('\n');
const header = lines.find((line) =>
line.startsWith('Physical Memory Array')
);
if (header) {
const start = lines.indexOf(header);
const nextHeaders = lines
.slice(start, -1)
.find((line) => line.startsWith('Handle '));
if (nextHeaders) {
const end = lines.indexOf(nextHeaders);
const fields = lines.slice(start, end);
max = toBytes(
fields
?.find((line) =>
line.trim().startsWith('Maximum Capacity')
)
?.trim()
?.split(': ')[1] ?? '0'
);
}
}
} catch {
// Ignore errors here
}
return {
layout,
max,
...info,
};
};
const generateDevices = async (): Promise<Devices> => {
/**
* Set device class to device.
* @param device The device to modify.
* @returns The same device passed in but with the class modified.
*/
const addDeviceClass = (device: Readonly<PciDevice>): PciDevice => {
const modifiedDevice: PciDevice = {
...device,
class: 'other',
};
// GPU
if (vmRegExps.allowedGpuClassId.test(device.typeid)) {
modifiedDevice.class = 'vga';
// Specialized product name cleanup for GPU
// GF116 [GeForce GTX 550 Ti] --> GeForce GTX 550 Ti
const regex = new RegExp(/.+\[(?<gpuName>.+)]/);
const productName = regex.exec(device.productname)?.groups?.gpuName;
if (productName) {
modifiedDevice.productname = productName;
}
return modifiedDevice;
// Audio
}
if (vmRegExps.allowedAudioClassId.test(device.typeid)) {
modifiedDevice.class = 'audio';
return modifiedDevice;
}
return modifiedDevice;
};
/**
* System PCI devices.
*/
const systemPciDevices = async (): Promise<PciDevice[]> => {
const devices = await getPciDevices();
const basePath = '/sys/bus/pci/devices/0000:';
// Remove devices with no IOMMU support
const filteredDevices = await asyncFilter(
devices,
async (device: Readonly<PciDevice>) =>
pathExists(`${basePath}${device.id}/iommu_group/`)
);
/**
* Run device cleanup
*
* Tasks:
* - Mark disallowed devices
* - Add class
* - Add whether kernel-bound driver exists
* - Cleanup device vendor/product names
*/
const processedDevices = await filterDevices(filteredDevices).then(
async (devices) =>
Promise.all(
devices
// @ts-expect-error - Device is not PciDevice
.map((device) => addDeviceClass(device))
.map(async (device) => {
// Attempt to get the current kernel-bound driver for this pci device
await isSymlink(
`${basePath}${device.id}/driver`
).then((symlink) => {
if (symlink) {
// $strLink = @readlink('/sys/bus/pci/devices/0000:'.$arrMatch['id']. '/driver');
// if (!empty($strLink)) {
// $strDriver = basename($strLink);
// }
}
});
// Clean up the vendor and product name
device.vendorname = sanitizeVendor(
device.vendorname
);
device.productname = sanitizeProduct(
device.productname
);
return device;
})
)
);
return processedDevices;
};
/**
* System GPU Devices
*
* @name systemGPUDevices
* @ignore
* @private
*/
const systemGPUDevices: Promise<Gpu[]> = systemPciDevices().then(
(devices) => {
return devices.filter(
(device) => device.class === 'vga' && !device.allowed
).map(entry => {
const gpu: Gpu = {
blacklisted: entry.allowed,
class: entry.class,
id: entry.id,
productid: entry.product,
typeid: entry.typeid,
type: entry.manufacturer,
vendorname: entry.vendorname
}
return gpu;
});
}
).catch(() => []);
/**
* System usb devices.
* @returns Array of USB devices.
*/
const getSystemUSBDevices = async () => {
try {
// Get a list of all usb hubs so we can filter the allowed/disallowed
const usbHubs = await execa(
'cat /sys/bus/usb/drivers/hub/*/modalias',
{ shell: true }
)
.then(({ stdout }) =>
stdout.split('\n').map((line) => {
// eslint-disable-next-line @typescript-eslint/prefer-regexp-exec
const [, id] = line.match(/usb:v(\w{9})/) ?? [];
return id.replace('p', ':');
})
)
.catch(() => [] as string[]);
const emhttp = getters.emhttp();
// Remove boot drive
const filterBootDrive = (device: Readonly<PciDevice>): boolean =>
emhttp.var.flashGuid !== device.guid;
// Remove usb hubs
const filterUsbHubs = (device: Readonly<PciDevice>): boolean =>
!usbHubs.includes(device.id);
// Clean up the name
const sanitizeVendorName = (device: Readonly<PciDevice>) => {
const vendorname = sanitizeVendor(device.vendorname || '');
return {
...device,
vendorname,
};
};
const parseDeviceLine = (
line: Readonly<string>
): { value: string; string: string } => {
const emptyLine = { value: '', string: '' };
// If the line is blank return nothing
if (!line) {
return emptyLine;
}
// Parse the line
const [, _] = line.split(/[ \t]{2,}/).filter(Boolean);
// eslint-disable-next-line @typescript-eslint/prefer-regexp-exec
const match = _.match(/^(\S+)\s(.*)/)?.slice(1);
// If there's no match return nothing
if (!match) {
return emptyLine;
}
return {
value: match[0],
string: match[1],
};
};
// Add extra fields to device
const parseDevice = (device: Readonly<PciDevice>) => {
const modifiedDevice: PciDevice = {
...device,
};
const info = execaCommandSync(
`lsusb -d ${device.id} -v`
).stdout.split('\n');
const deviceName = device.name.trim();
const iSerial = parseDeviceLine(
info.filter((line) => line.includes('iSerial'))[0]
);
const iProduct = parseDeviceLine(
info.filter((line) => line.includes('iProduct'))[0]
);
const iManufacturer = parseDeviceLine(
info.filter((line) => line.includes('iManufacturer'))[0]
);
const idProduct = parseDeviceLine(
info.filter((line) => line.includes('idProduct'))[0]
);
const idVendor = parseDeviceLine(
info.filter((line) => line.includes('idVendor'))[0]
);
const serial = `${iSerial.string
.slice(8)
.slice(0, 4)}-${iSerial.string.slice(8).slice(4)}`;
const guid = `${idVendor.value.slice(
2
)}-${idProduct.value.slice(2)}-${serial}`;
modifiedDevice.serial = iSerial.string;
modifiedDevice.product = iProduct.string;
modifiedDevice.manufacturer = iManufacturer.string;
modifiedDevice.guid = guid;
// Set name if missing
if (deviceName === '') {
modifiedDevice.name =
`${iProduct.string} ${iManufacturer.string}`.trim();
}
// Name still blank? Replace using fallback default
if (deviceName === '') {
modifiedDevice.name = '[unnamed device]';
}
// Ensure name is trimmed
modifiedDevice.name = device.name.trim();
return modifiedDevice;
};
const parseUsbDevices = (stdout: string) =>
stdout.split('\n').map((line) => {
const regex = new RegExp(/^.+: ID (?<id>\S+)(?<name>.*)$/);
const result = regex.exec(line);
return result?.groups as unknown as PciDevice;
}) ?? [];
// Get all usb devices
const usbDevices = await execa('lsusb').then(async ({ stdout }) =>
parseUsbDevices(stdout)
.map(parseDevice)
.filter(filterBootDrive)
.filter(filterUsbHubs)
.map(sanitizeVendorName)
);
return usbDevices;
} catch (error: unknown) {
return [];
}
};
return {
// Scsi: await scsiDevices,
gpu: await systemGPUDevices,
// Move this to interfaces
// network: await si.networkInterfaces(),
pci: await systemPciDevices(),
usb: await getSystemUSBDevices(),
};
};
const generateMachineId = async (): Promise<string> => getMachineId();
const generateSystem = async (): Promise<System> => system();
export const infoSubResolvers: InfoResolvers = {
apps: async () => generateApps(),
baseboard: async () => generateBaseboard(),
cpu: async () => generateCpu(),
devices: async () => generateDevices(),
display: async () => generateDisplay(),
machineId: async () => generateMachineId(),
memory: async () => generateMemory(),
os: async () => generateOs(),
system: async () => generateSystem(),
versions: async () => generateVersions(),
};
-1
View File
@@ -1,5 +1,4 @@
import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
import merge from 'lodash/merge';
import { DaemonConnectionStatus } from '@app/store/types';
import { type DockerContainer } from '@app/graphql/generated/api/types';
+15 -10
View File
@@ -2,18 +2,23 @@ import { store } from '@app/store';
import { dockerLogger } from '@app/core/log';
import { updateDockerState } from '@app/store/modules/docker';
import { getDockerContainers } from '@app/core/modules/index';
import { ContainerState } from '@app/graphql/generated/api/types';
import { docker } from '@app/core/utils/index';
import DockerEE from 'docker-event-emitter';
import { debounce } from 'lodash';
const updateContainerCache = async () => {
try {
await getDockerContainers({ useCache: false });
} catch (err) {
dockerLogger.warn('Caught error getting containers %o', err)
store.dispatch(updateDockerState({ installed: null, running: null, containers: [] }))
}
try {
await getDockerContainers({ useCache: false });
} catch (err) {
dockerLogger.warn('Caught error getting containers %o', err);
store.dispatch(
updateDockerState({
installed: null,
running: null,
containers: [],
})
);
}
};
const debouncedContainerCacheUpdate = debounce(updateContainerCache, 500);
@@ -50,12 +55,12 @@ export const setupDockerWatch = async (): Promise<DockerEE> => {
dockerLogger.addContext('data', data);
dockerLogger.debug(`[${data.from}] ${data.Type}->${data.Action}`);
dockerLogger.removeContext('data');
await debouncedContainerCacheUpdate()
await debouncedContainerCacheUpdate();
}
);
// Get docker container count on first start
// Get docker container count on first start
await debouncedContainerCacheUpdate();
await dee.start();
dockerLogger.debug('Binding to docker events');
return dee;
return dee;
};