mirror of
https://github.com/unraid/api.git
synced 2026-03-04 14:48:59 -06:00
chore: add a prefix scalar instead of prefix plugin (#1361)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced a new `PrefixedID` scalar type for all GraphQL IDs, ensuring unique identifiers across multiple servers by prefixing IDs with a server identifier. - Added detailed documentation for the `PrefixedID` scalar in the API schema. - Grouped VM and parity check mutations under dedicated fields for better organization. - Added new scalar `Port` and input type `AccessUrlInput` for improved type safety. - **Refactor** - Replaced all usages of standard `ID` or `String` with `PrefixedID` for IDs in queries, mutations, and models. - Consolidated ID handling by extending a common `Node` base class across models, removing redundant `id` declarations. - Updated GraphQL argument types and resolver signatures to explicitly use `PrefixedID`. - Updated GraphQL code generation to map `PrefixedID` to TypeScript `string`. - **Bug Fixes** - Enhanced test assertions to verify API key creation timestamps are strings. - Fixed internal property naming for registration ID. - **Chores** - Removed legacy ID prefix Apollo Server plugin in favor of the new scalar approach. - Cleaned up imports and unused fields related to ID handling. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
This commit is contained in:
@@ -74,8 +74,7 @@ type ArrayCapacity {
|
||||
}
|
||||
|
||||
type ArrayDisk implements Node {
|
||||
"""Disk identifier, only set for present disks on the system"""
|
||||
id: ID!
|
||||
id: PrefixedID!
|
||||
|
||||
"""
|
||||
Array slot number. Parity1 is always 0 and Parity2 is always 29. Array slots will be 1 - 28. Cache slots are 30 - 53. Flash is 54.
|
||||
@@ -143,7 +142,7 @@ type ArrayDisk implements Node {
|
||||
}
|
||||
|
||||
interface Node {
|
||||
id: ID!
|
||||
id: PrefixedID!
|
||||
}
|
||||
|
||||
"""The `Long` scalar type represents 52-bit integers"""
|
||||
@@ -181,7 +180,7 @@ enum ArrayDiskFsColor {
|
||||
}
|
||||
|
||||
type UnraidArray implements Node {
|
||||
id: ID!
|
||||
id: PrefixedID!
|
||||
|
||||
"""Current array state"""
|
||||
state: ArrayState!
|
||||
@@ -217,7 +216,7 @@ enum ArrayState {
|
||||
}
|
||||
|
||||
type Share implements Node {
|
||||
id: ID!
|
||||
id: PrefixedID!
|
||||
|
||||
"""Display name"""
|
||||
name: String
|
||||
@@ -348,8 +347,7 @@ type ConnectSettingsValues {
|
||||
}
|
||||
|
||||
type ConnectSettings implements Node {
|
||||
"""The unique identifier for the Connect settings"""
|
||||
id: ID!
|
||||
id: PrefixedID!
|
||||
|
||||
"""The data schema for the Connect settings"""
|
||||
dataSchema: JSON!
|
||||
@@ -367,8 +365,7 @@ The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://
|
||||
scalar JSON @specifiedBy(url: "http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf")
|
||||
|
||||
type Connect implements Node {
|
||||
"""The unique identifier for the Connect instance"""
|
||||
id: ID!
|
||||
id: PrefixedID!
|
||||
|
||||
"""The status of dynamic remote access"""
|
||||
dynamicRemoteAccess: DynamicRemoteAccessStatus!
|
||||
@@ -378,18 +375,19 @@ type Connect implements Node {
|
||||
}
|
||||
|
||||
type Network implements Node {
|
||||
id: ID!
|
||||
id: PrefixedID!
|
||||
accessUrls: [AccessUrl!]
|
||||
}
|
||||
|
||||
type ProfileModel {
|
||||
userId: ID
|
||||
type ProfileModel implements Node {
|
||||
id: PrefixedID!
|
||||
username: String!
|
||||
url: String!
|
||||
avatar: String!
|
||||
}
|
||||
|
||||
type Server {
|
||||
type Server implements Node {
|
||||
id: PrefixedID!
|
||||
owner: ProfileModel!
|
||||
guid: String!
|
||||
apikey: String!
|
||||
@@ -428,9 +426,8 @@ enum DiskFsType {
|
||||
NTFS
|
||||
}
|
||||
|
||||
type Disk {
|
||||
"""The unique identifier of the disk"""
|
||||
id: String!
|
||||
type Disk implements Node {
|
||||
id: PrefixedID!
|
||||
|
||||
"""The device path of the disk (e.g. /dev/sdb)"""
|
||||
device: String!
|
||||
@@ -509,8 +506,8 @@ type KeyFile {
|
||||
contents: String
|
||||
}
|
||||
|
||||
type Registration {
|
||||
guid: ID
|
||||
type Registration implements Node {
|
||||
id: PrefixedID!
|
||||
type: registrationType
|
||||
keyFile: KeyFile
|
||||
state: RegistrationState
|
||||
@@ -559,7 +556,7 @@ enum RegistrationState {
|
||||
}
|
||||
|
||||
type Vars implements Node {
|
||||
id: ID!
|
||||
id: PrefixedID!
|
||||
|
||||
"""Unraid version"""
|
||||
version: String
|
||||
@@ -786,8 +783,8 @@ enum Resource {
|
||||
WELCOME
|
||||
}
|
||||
|
||||
type ApiKey {
|
||||
id: ID!
|
||||
type ApiKey implements Node {
|
||||
id: PrefixedID!
|
||||
name: String!
|
||||
description: String
|
||||
roles: [Role!]!
|
||||
@@ -802,8 +799,8 @@ enum Role {
|
||||
GUEST
|
||||
}
|
||||
|
||||
type ApiKeyWithSecret {
|
||||
id: ID!
|
||||
type ApiKeyWithSecret implements Node {
|
||||
id: PrefixedID!
|
||||
name: String!
|
||||
description: String
|
||||
roles: [Role!]!
|
||||
@@ -825,13 +822,13 @@ type ArrayMutations {
|
||||
removeDiskFromArray(input: ArrayDiskInput!): UnraidArray!
|
||||
|
||||
"""Mount a disk in the array"""
|
||||
mountArrayDisk(id: String!): ArrayDisk!
|
||||
mountArrayDisk(id: PrefixedID!): ArrayDisk!
|
||||
|
||||
"""Unmount a disk from the array"""
|
||||
unmountArrayDisk(id: String!): ArrayDisk!
|
||||
unmountArrayDisk(id: PrefixedID!): ArrayDisk!
|
||||
|
||||
"""Clear statistics for a disk in the array"""
|
||||
clearArrayDiskStatistics(id: String!): Boolean!
|
||||
clearArrayDiskStatistics(id: PrefixedID!): Boolean!
|
||||
}
|
||||
|
||||
input ArrayStateInput {
|
||||
@@ -846,7 +843,7 @@ enum ArrayStateInputState {
|
||||
|
||||
input ArrayDiskInput {
|
||||
"""Disk ID"""
|
||||
id: ID!
|
||||
id: PrefixedID!
|
||||
|
||||
"""The slot for the disk"""
|
||||
slot: Int
|
||||
@@ -854,33 +851,33 @@ input ArrayDiskInput {
|
||||
|
||||
type DockerMutations {
|
||||
"""Start a container"""
|
||||
start(id: String!): DockerContainer!
|
||||
start(id: PrefixedID!): DockerContainer!
|
||||
|
||||
"""Stop a container"""
|
||||
stop(id: String!): DockerContainer!
|
||||
stop(id: PrefixedID!): DockerContainer!
|
||||
}
|
||||
|
||||
type VmMutations {
|
||||
"""Start a virtual machine"""
|
||||
start(id: String!): Boolean!
|
||||
start(id: PrefixedID!): Boolean!
|
||||
|
||||
"""Stop a virtual machine"""
|
||||
stop(id: String!): Boolean!
|
||||
stop(id: PrefixedID!): Boolean!
|
||||
|
||||
"""Pause a virtual machine"""
|
||||
pause(id: String!): Boolean!
|
||||
pause(id: PrefixedID!): Boolean!
|
||||
|
||||
"""Resume a virtual machine"""
|
||||
resume(id: String!): Boolean!
|
||||
resume(id: PrefixedID!): Boolean!
|
||||
|
||||
"""Force stop a virtual machine"""
|
||||
forceStop(id: String!): Boolean!
|
||||
forceStop(id: PrefixedID!): Boolean!
|
||||
|
||||
"""Reboot a virtual machine"""
|
||||
reboot(id: String!): Boolean!
|
||||
reboot(id: PrefixedID!): Boolean!
|
||||
|
||||
"""Reset a virtual machine"""
|
||||
reset(id: String!): Boolean!
|
||||
reset(id: PrefixedID!): Boolean!
|
||||
}
|
||||
|
||||
"""
|
||||
@@ -935,13 +932,13 @@ A date-time string at UTC, such as 2019-12-03T09:54:33Z, compliant with the date
|
||||
scalar DateTime
|
||||
|
||||
type Config implements Node {
|
||||
id: ID!
|
||||
id: PrefixedID!
|
||||
valid: Boolean
|
||||
error: String
|
||||
}
|
||||
|
||||
type InfoApps implements Node {
|
||||
id: ID!
|
||||
id: PrefixedID!
|
||||
|
||||
"""How many docker containers are installed"""
|
||||
installed: Int!
|
||||
@@ -951,7 +948,7 @@ type InfoApps implements Node {
|
||||
}
|
||||
|
||||
type Baseboard implements Node {
|
||||
id: ID!
|
||||
id: PrefixedID!
|
||||
manufacturer: String!
|
||||
model: String
|
||||
version: String
|
||||
@@ -960,7 +957,7 @@ type Baseboard implements Node {
|
||||
}
|
||||
|
||||
type InfoCpu implements Node {
|
||||
id: ID!
|
||||
id: PrefixedID!
|
||||
manufacturer: String!
|
||||
brand: String!
|
||||
vendor: String!
|
||||
@@ -981,7 +978,7 @@ type InfoCpu implements Node {
|
||||
}
|
||||
|
||||
type Gpu implements Node {
|
||||
id: ID!
|
||||
id: PrefixedID!
|
||||
type: String!
|
||||
typeid: String!
|
||||
vendorname: String!
|
||||
@@ -991,7 +988,7 @@ type Gpu implements Node {
|
||||
}
|
||||
|
||||
type Pci implements Node {
|
||||
id: ID!
|
||||
id: PrefixedID!
|
||||
type: String
|
||||
typeid: String
|
||||
vendorname: String
|
||||
@@ -1002,20 +999,20 @@ type Pci implements Node {
|
||||
class: String
|
||||
}
|
||||
|
||||
type Usb {
|
||||
id: ID!
|
||||
type Usb implements Node {
|
||||
id: PrefixedID!
|
||||
name: String
|
||||
}
|
||||
|
||||
type Devices implements Node {
|
||||
id: ID!
|
||||
id: PrefixedID!
|
||||
gpu: [Gpu!]!
|
||||
pci: [Pci!]!
|
||||
usb: [Usb!]!
|
||||
}
|
||||
|
||||
type Case implements Node {
|
||||
id: ID!
|
||||
id: PrefixedID!
|
||||
icon: String
|
||||
url: String
|
||||
error: String
|
||||
@@ -1023,7 +1020,7 @@ type Case implements Node {
|
||||
}
|
||||
|
||||
type Display implements Node {
|
||||
id: ID!
|
||||
id: PrefixedID!
|
||||
case: Case
|
||||
date: String
|
||||
number: String
|
||||
@@ -1057,7 +1054,8 @@ enum Temperature {
|
||||
F
|
||||
}
|
||||
|
||||
type MemoryLayout {
|
||||
type MemoryLayout implements Node {
|
||||
id: PrefixedID!
|
||||
size: Int!
|
||||
bank: String
|
||||
type: String
|
||||
@@ -1072,7 +1070,7 @@ type MemoryLayout {
|
||||
}
|
||||
|
||||
type InfoMemory implements Node {
|
||||
id: ID!
|
||||
id: PrefixedID!
|
||||
max: Int!
|
||||
total: Int!
|
||||
free: Int!
|
||||
@@ -1087,7 +1085,7 @@ type InfoMemory implements Node {
|
||||
}
|
||||
|
||||
type Os implements Node {
|
||||
id: ID!
|
||||
id: PrefixedID!
|
||||
platform: String
|
||||
distro: String
|
||||
release: String
|
||||
@@ -1103,7 +1101,7 @@ type Os implements Node {
|
||||
}
|
||||
|
||||
type System implements Node {
|
||||
id: ID!
|
||||
id: PrefixedID!
|
||||
manufacturer: String
|
||||
model: String
|
||||
version: String
|
||||
@@ -1113,7 +1111,7 @@ type System implements Node {
|
||||
}
|
||||
|
||||
type Versions implements Node {
|
||||
id: ID!
|
||||
id: PrefixedID!
|
||||
kernel: String
|
||||
openssl: String
|
||||
systemOpenssl: String
|
||||
@@ -1143,7 +1141,7 @@ type Versions implements Node {
|
||||
}
|
||||
|
||||
type Info implements Node {
|
||||
id: ID!
|
||||
id: PrefixedID!
|
||||
|
||||
"""Count of docker containers"""
|
||||
apps: InfoApps!
|
||||
@@ -1153,7 +1151,7 @@ type Info implements Node {
|
||||
display: Display!
|
||||
|
||||
"""Machine ID"""
|
||||
machineId: ID
|
||||
machineId: PrefixedID
|
||||
memory: InfoMemory!
|
||||
os: Os!
|
||||
system: System!
|
||||
@@ -1182,8 +1180,8 @@ type ContainerHostConfig {
|
||||
networkMode: String!
|
||||
}
|
||||
|
||||
type DockerContainer {
|
||||
id: ID!
|
||||
type DockerContainer implements Node {
|
||||
id: PrefixedID!
|
||||
names: [String!]!
|
||||
image: String!
|
||||
imageId: String!
|
||||
@@ -1212,9 +1210,9 @@ enum ContainerState {
|
||||
EXITED
|
||||
}
|
||||
|
||||
type DockerNetwork {
|
||||
type DockerNetwork implements Node {
|
||||
id: PrefixedID!
|
||||
name: String!
|
||||
id: ID!
|
||||
created: String!
|
||||
scope: String!
|
||||
driver: String!
|
||||
@@ -1231,13 +1229,13 @@ type DockerNetwork {
|
||||
}
|
||||
|
||||
type Docker implements Node {
|
||||
id: ID!
|
||||
id: PrefixedID!
|
||||
containers(skipCache: Boolean! = false): [DockerContainer!]!
|
||||
networks(skipCache: Boolean! = false): [DockerNetwork!]!
|
||||
}
|
||||
|
||||
type Flash implements Node {
|
||||
id: ID!
|
||||
id: PrefixedID!
|
||||
guid: String!
|
||||
vendor: String!
|
||||
product: String!
|
||||
@@ -1283,8 +1281,8 @@ type NotificationOverview {
|
||||
archive: NotificationCounts!
|
||||
}
|
||||
|
||||
type Notification {
|
||||
id: ID!
|
||||
type Notification implements Node {
|
||||
id: PrefixedID!
|
||||
|
||||
"""Also known as 'event'"""
|
||||
title: String!
|
||||
@@ -1310,8 +1308,8 @@ enum NotificationType {
|
||||
ARCHIVE
|
||||
}
|
||||
|
||||
type Notifications {
|
||||
id: ID!
|
||||
type Notifications implements Node {
|
||||
id: PrefixedID!
|
||||
|
||||
"""A cached overview of the notifications in the system & their severity."""
|
||||
overview: NotificationOverview!
|
||||
@@ -1331,14 +1329,18 @@ type Owner {
|
||||
avatar: String!
|
||||
}
|
||||
|
||||
type VmDomain {
|
||||
uuid: ID!
|
||||
type VmDomain implements Node {
|
||||
"""The unique identifier for the vm (uuid)"""
|
||||
id: PrefixedID!
|
||||
|
||||
"""A friendly name for the vm"""
|
||||
name: String
|
||||
|
||||
"""Current domain vm state"""
|
||||
state: VmState!
|
||||
|
||||
"""The UUID of the vm"""
|
||||
uuid: String @deprecated(reason: "Use id instead")
|
||||
}
|
||||
|
||||
"""The state of a virtual machine"""
|
||||
@@ -1353,8 +1355,8 @@ enum VmState {
|
||||
PMSUSPENDED
|
||||
}
|
||||
|
||||
type Vms {
|
||||
id: ID!
|
||||
type Vms implements Node {
|
||||
id: PrefixedID!
|
||||
domains: [VmDomain!]
|
||||
domain: [VmDomain!]
|
||||
}
|
||||
@@ -1364,16 +1366,15 @@ type Uptime {
|
||||
}
|
||||
|
||||
type Service implements Node {
|
||||
id: ID!
|
||||
id: PrefixedID!
|
||||
name: String
|
||||
online: Boolean
|
||||
uptime: Uptime
|
||||
version: String
|
||||
}
|
||||
|
||||
type UserAccount {
|
||||
"""A unique identifier for the user"""
|
||||
id: ID!
|
||||
type UserAccount implements Node {
|
||||
id: PrefixedID!
|
||||
|
||||
"""The name of the user"""
|
||||
name: String!
|
||||
@@ -1388,9 +1389,12 @@ type UserAccount {
|
||||
permissions: [Permission!]
|
||||
}
|
||||
|
||||
"\n### Description:\n\nID scalar type that prefixes the underlying ID with the server identifier on output and strips it on input.\n\nWe use this scalar type to ensure that the ID is unique across all servers, allowing the same underlying resource ID to be used across different server instances.\n\n#### Input Behavior:\n\nWhen providing an ID as input (e.g., in arguments or input objects), the server identifier prefix ('<serverId>:') is optional.\n\n- If the prefix is present (e.g., '123:456'), it will be automatically stripped, and only the underlying ID ('456') will be used internally.\n- If the prefix is absent (e.g., '456'), the ID will be used as-is.\n\nThis makes it flexible for clients, as they don't strictly need to know or provide the server ID.\n\n#### Output Behavior:\n\nWhen an ID is returned in the response (output), it will *always* be prefixed with the current server's unique identifier (e.g., '123:456').\n\n#### Example:\n\nNote: The server identifier is '123' in this example.\n\n##### Input (Prefix Optional):\n```graphql\n# Both of these are valid inputs resolving to internal ID '456'\n{\n someQuery(id: \"123:456\") { ... }\n anotherQuery(id: \"456\") { ... }\n}\n```\n\n##### Output (Prefix Always Added):\n```graphql\n# Assuming internal ID is '456'\n{\n \"data\": {\n \"someResource\": {\n \"id\": \"123:456\" \n }\n }\n}\n```\n "
|
||||
scalar PrefixedID
|
||||
|
||||
type Query {
|
||||
apiKeys: [ApiKey!]!
|
||||
apiKey(id: String!): ApiKey
|
||||
apiKey(id: PrefixedID!): ApiKey
|
||||
cloud: Cloud!
|
||||
config: Config!
|
||||
display: Display!
|
||||
@@ -1421,7 +1425,7 @@ type Query {
|
||||
extraAllowedOrigins: [String!]!
|
||||
docker: Docker!
|
||||
disks: [Disk!]!
|
||||
disk(id: String!): Disk!
|
||||
disk(id: PrefixedID!): Disk!
|
||||
health: String!
|
||||
getDemo: String!
|
||||
}
|
||||
@@ -1433,19 +1437,19 @@ type Mutation {
|
||||
|
||||
"""Creates a new notification record"""
|
||||
createNotification(input: NotificationData!): Notification!
|
||||
deleteNotification(id: String!, type: NotificationType!): NotificationOverview!
|
||||
deleteNotification(id: PrefixedID!, type: NotificationType!): NotificationOverview!
|
||||
|
||||
"""Deletes all archived notifications on server."""
|
||||
deleteArchivedNotifications: NotificationOverview!
|
||||
|
||||
"""Marks a notification as archived."""
|
||||
archiveNotification(id: String!): Notification!
|
||||
archiveNotifications(ids: [String!]!): NotificationOverview!
|
||||
archiveNotification(id: PrefixedID!): Notification!
|
||||
archiveNotifications(ids: [PrefixedID!]!): NotificationOverview!
|
||||
archiveAll(importance: NotificationImportance): NotificationOverview!
|
||||
|
||||
"""Marks a notification as unread."""
|
||||
unreadNotification(id: String!): Notification!
|
||||
unarchiveNotifications(ids: [String!]!): NotificationOverview!
|
||||
unreadNotification(id: PrefixedID!): Notification!
|
||||
unarchiveNotifications(ids: [PrefixedID!]!): NotificationOverview!
|
||||
unarchiveAll(importance: NotificationImportance): NotificationOverview!
|
||||
|
||||
"""Reads each notification to recompute & update the overview."""
|
||||
@@ -1481,12 +1485,12 @@ input AddPermissionInput {
|
||||
}
|
||||
|
||||
input AddRoleForApiKeyInput {
|
||||
apiKeyId: ID!
|
||||
apiKeyId: PrefixedID!
|
||||
role: Role!
|
||||
}
|
||||
|
||||
input RemoveRoleFromApiKeyInput {
|
||||
apiKeyId: ID!
|
||||
apiKeyId: PrefixedID!
|
||||
role: Role!
|
||||
}
|
||||
|
||||
|
||||
@@ -83,7 +83,9 @@ export const getLocalServer = (getState = store.getState): Array<Server> => {
|
||||
|
||||
return [
|
||||
{
|
||||
id: 'local',
|
||||
owner: {
|
||||
id: 'local',
|
||||
username: config.remote.username ?? 'root',
|
||||
url: '',
|
||||
avatar: '',
|
||||
|
||||
@@ -520,11 +520,13 @@ describe('ApiKeyService', () => {
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0]).toEqual({
|
||||
...mockApiKey,
|
||||
createdAt: expect.any(String),
|
||||
id: 'test-api-id',
|
||||
key: 'test-api-key',
|
||||
});
|
||||
expect(result[1]).toEqual({
|
||||
...mockApiKey,
|
||||
createdAt: expect.any(String),
|
||||
id: 'unique-id',
|
||||
key: 'unique-key',
|
||||
});
|
||||
|
||||
@@ -7,15 +7,15 @@ import { NoUnusedVariablesRule } from 'graphql';
|
||||
import { JSONResolver, URLResolver } from 'graphql-scalars';
|
||||
|
||||
import { ENVIRONMENT } from '@app/environment.js';
|
||||
import { GraphQLLong } from '@app/graphql/resolvers/graphql-type-long.js';
|
||||
import { getters } from '@app/store/index.js';
|
||||
import {
|
||||
UsePermissionsDirective,
|
||||
usePermissionsSchemaTransformer,
|
||||
} from '@app/unraid-api/graph/directives/use-permissions.directive.js';
|
||||
import { idPrefixPlugin } from '@app/unraid-api/graph/id-prefix-plugin.js';
|
||||
import { ResolversModule } from '@app/unraid-api/graph/resolvers/resolvers.module.js';
|
||||
import { sandboxPlugin } from '@app/unraid-api/graph/sandbox-plugin.js';
|
||||
import { GraphQLLong } from '@app/unraid-api/graph/scalars/graphql-type-long.js';
|
||||
import { PrefixedID as PrefixedIDScalar } from '@app/unraid-api/graph/scalars/graphql-type-prefixed-id.js';
|
||||
import { PluginModule } from '@app/unraid-api/plugin/plugin.module.js';
|
||||
|
||||
@Module({
|
||||
@@ -42,7 +42,7 @@ import { PluginModule } from '@app/unraid-api/plugin/plugin.module.js';
|
||||
extra,
|
||||
};
|
||||
},
|
||||
plugins: [sandboxPlugin, idPrefixPlugin] as any[],
|
||||
plugins: [sandboxPlugin] as any[],
|
||||
subscriptions: {
|
||||
'graphql-ws': {
|
||||
path: '/graphql',
|
||||
@@ -63,7 +63,7 @@ import { PluginModule } from '@app/unraid-api/plugin/plugin.module.js';
|
||||
},
|
||||
}),
|
||||
],
|
||||
providers: [],
|
||||
providers: [PrefixedIDScalar],
|
||||
exports: [GraphQLModule],
|
||||
})
|
||||
export class GraphModule {}
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
import { type ApolloServerPlugin } from '@apollo/server';
|
||||
|
||||
import { getServerIdentifier } from '@app/core/utils/server-identifier.js';
|
||||
import { updateObject } from '@app/utils.js';
|
||||
|
||||
type ObjectModifier = (obj: object) => void;
|
||||
|
||||
/**
|
||||
* Returns a function that takes an object and updates any 'id' properties to
|
||||
* include the given serverId as a prefix.
|
||||
*
|
||||
* e.g. If the object is { id: '1234' }, the returned function will update it to
|
||||
* { id: '<serverId>:1234' }.
|
||||
*
|
||||
* @param serverId - The server identifier to use as the prefix.
|
||||
* @returns A function that takes an object and updates any 'id' properties with the given serverId.
|
||||
*/
|
||||
function prefixWithServerId(serverId: string): ObjectModifier {
|
||||
return (currentObj) => {
|
||||
if ('id' in currentObj && typeof currentObj.id === 'string') {
|
||||
currentObj.id = `${serverId}:${currentObj.id}`;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an object and removes any server prefix from the 'id' property.
|
||||
*
|
||||
* e.g. If the object is { id: '<serverId>:1234' }, the returned function will update it to
|
||||
* { id: '1234' }.
|
||||
*
|
||||
* @param current - The object to update. If it has an 'id' property that is a string and
|
||||
* has a server prefix, the prefix is removed.
|
||||
*/
|
||||
const stripServerPrefixFromIds: ObjectModifier = (current) => {
|
||||
if ('id' in current && typeof current.id === 'string') {
|
||||
const parts = current.id.split(':');
|
||||
// if there are more or less than 2 parts to the split,
|
||||
// assume there is no server prefix and don't touch it.
|
||||
if (parts.length === 2) {
|
||||
current.id = parts[1];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const idPrefixPlugin: ApolloServerPlugin = {
|
||||
async requestDidStart(requestContext) {
|
||||
if (requestContext.request.operationName === 'IntrospectionQuery') {
|
||||
// Don't modify the introspection query
|
||||
return;
|
||||
}
|
||||
// If ID is requested, return an ID field with an extra prefix
|
||||
return {
|
||||
async didResolveOperation({ request }) {
|
||||
if (request.variables) {
|
||||
updateObject(request.variables, stripServerPrefixFromIds);
|
||||
}
|
||||
},
|
||||
async willSendResponse({ response }) {
|
||||
if (response.body.kind === 'single' && response.body.singleResult.data) {
|
||||
const serverId = getServerIdentifier();
|
||||
updateObject(response.body.singleResult.data, prefixWithServerId(serverId));
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -1,20 +1,19 @@
|
||||
import { Field, ID, InputType, ObjectType } from '@nestjs/graphql';
|
||||
import { Field, InputType, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import { Transform, Type } from 'class-transformer';
|
||||
import {
|
||||
ArrayMinSize,
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
IsDate,
|
||||
IsEnum,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsString,
|
||||
ValidateIf,
|
||||
ValidateNested,
|
||||
} from 'class-validator';
|
||||
|
||||
import { Resource, Role } from '@app/unraid-api/graph/resolvers/base.model.js';
|
||||
import { Node, Resource, Role } from '@app/unraid-api/graph/resolvers/base.model.js';
|
||||
import { PrefixedID } from '@app/unraid-api/graph/scalars/graphql-type-prefixed-id.js';
|
||||
|
||||
@ObjectType()
|
||||
export class Permission {
|
||||
@@ -29,13 +28,8 @@ export class Permission {
|
||||
actions!: string[];
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class ApiKey {
|
||||
@Field(() => ID)
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
id!: string;
|
||||
|
||||
@ObjectType({ implements: () => Node })
|
||||
export class ApiKey extends Node {
|
||||
@Field()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@@ -119,7 +113,7 @@ export class CreateApiKeyInput {
|
||||
|
||||
@InputType()
|
||||
export class AddRoleForApiKeyInput {
|
||||
@Field(() => ID)
|
||||
@Field(() => PrefixedID)
|
||||
@IsString()
|
||||
apiKeyId!: string;
|
||||
|
||||
@@ -130,7 +124,7 @@ export class AddRoleForApiKeyInput {
|
||||
|
||||
@InputType()
|
||||
export class RemoveRoleFromApiKeyInput {
|
||||
@Field(() => ID)
|
||||
@Field(() => PrefixedID)
|
||||
@IsString()
|
||||
apiKeyId!: string;
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
} from '@app/unraid-api/graph/resolvers/api-key/api-key.model.js';
|
||||
import { Resource, Role } from '@app/unraid-api/graph/resolvers/base.model.js';
|
||||
import { validateObject } from '@app/unraid-api/graph/resolvers/validation.utils.js';
|
||||
import { PrefixedID } from '@app/unraid-api/graph/scalars/graphql-type-prefixed-id.js';
|
||||
|
||||
@Resolver(() => ApiKey)
|
||||
export class ApiKeyResolver {
|
||||
@@ -40,7 +41,10 @@ export class ApiKeyResolver {
|
||||
resource: Resource.API_KEY,
|
||||
possession: AuthPossession.ANY,
|
||||
})
|
||||
async apiKey(@Args('id') id: string): Promise<ApiKey | null> {
|
||||
async apiKey(
|
||||
@Args('id', { type: () => PrefixedID })
|
||||
id: string
|
||||
): Promise<ApiKey | null> {
|
||||
return this.apiKeyService.findById(id);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Field, ID, InputType, Int, ObjectType, registerEnumType } from '@nestjs/graphql';
|
||||
import { Field, InputType, Int, ObjectType, registerEnumType } from '@nestjs/graphql';
|
||||
|
||||
import { IsEnum } from 'class-validator';
|
||||
|
||||
import { GraphQLLong } from '@app/graphql/resolvers/graphql-type-long.js';
|
||||
import { Node } from '@app/unraid-api/graph/resolvers/base.model.js';
|
||||
import { GraphQLLong } from '@app/unraid-api/graph/scalars/graphql-type-long.js';
|
||||
import { PrefixedID } from '@app/unraid-api/graph/scalars/graphql-type-prefixed-id.js';
|
||||
|
||||
@ObjectType()
|
||||
export class Capacity {
|
||||
@@ -29,10 +30,7 @@ export class ArrayCapacity {
|
||||
@ObjectType({
|
||||
implements: () => Node,
|
||||
})
|
||||
export class ArrayDisk implements Node {
|
||||
@Field(() => ID, { description: 'Disk identifier, only set for present disks on the system' })
|
||||
id!: string;
|
||||
|
||||
export class ArrayDisk extends Node {
|
||||
@Field(() => Int, {
|
||||
description:
|
||||
'Array slot number. Parity1 is always 0 and Parity2 is always 29. Array slots will be 1 - 28. Cache slots are 30 - 53. Flash is 54.',
|
||||
@@ -132,10 +130,7 @@ export class ArrayDisk implements Node {
|
||||
@ObjectType({
|
||||
implements: () => Node,
|
||||
})
|
||||
export class UnraidArray implements Node {
|
||||
@Field(() => ID)
|
||||
id!: string;
|
||||
|
||||
export class UnraidArray extends Node {
|
||||
@Field(() => ArrayState, { description: 'Current array state' })
|
||||
state!: ArrayState;
|
||||
|
||||
@@ -157,7 +152,7 @@ export class UnraidArray implements Node {
|
||||
|
||||
@InputType()
|
||||
export class ArrayDiskInput {
|
||||
@Field(() => ID, { description: 'Disk ID' })
|
||||
@Field(() => PrefixedID, { description: 'Disk ID' })
|
||||
id!: string;
|
||||
|
||||
@Field(() => Int, { nullable: true, description: 'The slot for the disk' })
|
||||
@@ -244,10 +239,7 @@ registerEnumType(ArrayDiskFsColor, {
|
||||
@ObjectType({
|
||||
implements: () => Node,
|
||||
})
|
||||
export class Share implements Node {
|
||||
@Field(() => ID)
|
||||
id!: string;
|
||||
|
||||
export class Share extends Node {
|
||||
@Field(() => String, { description: 'Display name', nullable: true })
|
||||
name?: string | null;
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
import { ArrayService } from '@app/unraid-api/graph/resolvers/array/array.service.js';
|
||||
import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js';
|
||||
import { ArrayMutations } from '@app/unraid-api/graph/resolvers/mutation/mutation.model.js';
|
||||
import { PrefixedID } from '@app/unraid-api/graph/scalars/graphql-type-prefixed-id.js';
|
||||
|
||||
/**
|
||||
* Nested Resolvers for Mutations MUST use @ResolveField() instead of @Mutation()
|
||||
@@ -62,7 +63,7 @@ export class ArrayMutationsResolver {
|
||||
resource: Resource.ARRAY,
|
||||
possession: AuthPossession.ANY,
|
||||
})
|
||||
public async mountArrayDisk(@Args('id') id: string): Promise<ArrayDisk> {
|
||||
public async mountArrayDisk(@Args('id', { type: () => PrefixedID }) id: string): Promise<ArrayDisk> {
|
||||
const array = await this.arrayService.mountArrayDisk(id);
|
||||
const disk =
|
||||
array.disks.find((disk) => disk.id === id) ||
|
||||
@@ -82,7 +83,9 @@ export class ArrayMutationsResolver {
|
||||
resource: Resource.ARRAY,
|
||||
possession: AuthPossession.ANY,
|
||||
})
|
||||
public async unmountArrayDisk(@Args('id') id: string): Promise<ArrayDisk> {
|
||||
public async unmountArrayDisk(
|
||||
@Args('id', { type: () => PrefixedID }) id: string
|
||||
): Promise<ArrayDisk> {
|
||||
const array = await this.arrayService.unmountArrayDisk(id);
|
||||
const disk =
|
||||
array.disks.find((disk) => disk.id === id) ||
|
||||
@@ -102,7 +105,9 @@ export class ArrayMutationsResolver {
|
||||
resource: Resource.ARRAY,
|
||||
possession: AuthPossession.ANY,
|
||||
})
|
||||
public async clearArrayDiskStatistics(@Args('id') id: string): Promise<boolean> {
|
||||
public async clearArrayDiskStatistics(
|
||||
@Args('id', { type: () => PrefixedID }) id: string
|
||||
): Promise<boolean> {
|
||||
await this.arrayService.clearArrayDiskStatistics(id);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { Field, ID, InterfaceType, registerEnumType } from '@nestjs/graphql';
|
||||
import { Field, InterfaceType, registerEnumType } from '@nestjs/graphql';
|
||||
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
import { PrefixedID } from '@app/unraid-api/graph/scalars/graphql-type-prefixed-id.js';
|
||||
|
||||
// Register enums
|
||||
export enum Resource {
|
||||
@@ -40,7 +44,9 @@ export enum Role {
|
||||
|
||||
@InterfaceType()
|
||||
export class Node {
|
||||
@Field(() => ID)
|
||||
@Field(() => PrefixedID)
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
id!: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import { Field, ID, ObjectType } from '@nestjs/graphql';
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import { Node } from '@app/unraid-api/graph/resolvers/base.model.js';
|
||||
|
||||
@ObjectType({
|
||||
implements: () => Node,
|
||||
})
|
||||
export class Config implements Node {
|
||||
@Field(() => ID)
|
||||
id!: string;
|
||||
|
||||
export class Config extends Node {
|
||||
@Field(() => Boolean, { nullable: true })
|
||||
valid?: boolean | null;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { Args, ID, Mutation, Query, ResolveField, Resolver } from '@nestjs/graphql';
|
||||
import { Args, Mutation, Query, ResolveField, Resolver } from '@nestjs/graphql';
|
||||
|
||||
import { Layout } from '@jsonforms/core';
|
||||
import { GraphQLJSON, GraphQLJSONObject } from 'graphql-scalars';
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
RemoteAccess,
|
||||
SetupRemoteAccessInput,
|
||||
} from '@app/unraid-api/graph/resolvers/connect/connect.model.js';
|
||||
import { PrefixedID } from '@app/unraid-api/graph/scalars/graphql-type-prefixed-id.js';
|
||||
import { DataSlice } from '@app/unraid-api/types/json-forms.js';
|
||||
|
||||
@Resolver(() => ConnectSettings)
|
||||
@@ -31,7 +32,7 @@ export class ConnectSettingsResolver {
|
||||
private readonly logger = new Logger(ConnectSettingsResolver.name);
|
||||
constructor(private readonly connectSettingsService: ConnectSettingsService) {}
|
||||
|
||||
@ResolveField(() => ID)
|
||||
@ResolveField(() => PrefixedID)
|
||||
public async id(): Promise<string> {
|
||||
return 'connectSettingsForm';
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
import { GraphQLJSON, GraphQLURL } from 'graphql-scalars';
|
||||
|
||||
import { Node } from '@app/unraid-api/graph/resolvers/base.model.js';
|
||||
import { PrefixedID } from '@app/unraid-api/graph/scalars/graphql-type-prefixed-id.js';
|
||||
|
||||
export enum WAN_ACCESS_TYPE {
|
||||
DYNAMIC = 'DYNAMIC',
|
||||
@@ -326,12 +327,7 @@ export class ApiSettingsInput {
|
||||
@ObjectType({
|
||||
implements: () => Node,
|
||||
})
|
||||
export class ConnectSettings implements Node {
|
||||
@Field(() => ID, { description: 'The unique identifier for the Connect settings' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
id!: string;
|
||||
|
||||
export class ConnectSettings extends Node {
|
||||
@Field(() => GraphQLJSON, { description: 'The data schema for the Connect settings' })
|
||||
@IsObject()
|
||||
dataSchema!: Record<string, any>;
|
||||
@@ -348,12 +344,8 @@ export class ConnectSettings implements Node {
|
||||
@ObjectType({
|
||||
implements: () => Node,
|
||||
})
|
||||
export class Connect {
|
||||
@Field(() => ID, { description: 'The unique identifier for the Connect instance' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
id!: string;
|
||||
|
||||
export class Connect extends Node {
|
||||
@Field(() => DynamicRemoteAccessStatus, { description: 'The status of dynamic remote access' })
|
||||
@Field(() => DynamicRemoteAccessStatus, { description: 'The status of dynamic remote access' })
|
||||
@ValidateNested()
|
||||
dynamicRemoteAccess?: DynamicRemoteAccessStatus;
|
||||
@@ -366,10 +358,7 @@ export class Connect {
|
||||
@ObjectType({
|
||||
implements: () => Node,
|
||||
})
|
||||
export class Network implements Node {
|
||||
@Field(() => ID)
|
||||
id!: string;
|
||||
|
||||
export class Network extends Node {
|
||||
@Field(() => [AccessUrl], { nullable: true })
|
||||
accessUrls?: AccessUrl[];
|
||||
}
|
||||
|
||||
@@ -3,6 +3,9 @@ import { Field, ObjectType, registerEnumType } from '@nestjs/graphql';
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsArray, IsEnum, IsNumber, IsOptional, IsString, ValidateNested } from 'class-validator';
|
||||
|
||||
import { Node } from '@app/unraid-api/graph/resolvers/base.model.js';
|
||||
import { PrefixedID } from '@app/unraid-api/graph/scalars/graphql-type-prefixed-id.js';
|
||||
|
||||
export enum DiskFsType {
|
||||
XFS = 'XFS',
|
||||
BTRFS = 'BTRFS',
|
||||
@@ -55,12 +58,8 @@ export class DiskPartition {
|
||||
size!: number;
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class Disk {
|
||||
@Field(() => String, { description: 'The unique identifier of the disk' })
|
||||
@IsString()
|
||||
id!: string;
|
||||
|
||||
@ObjectType({ implements: () => Node })
|
||||
export class Disk extends Node {
|
||||
@Field(() => String, { description: 'The device path of the disk (e.g. /dev/sdb)' })
|
||||
@IsString()
|
||||
device!: string;
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js';
|
||||
import { Disk } from '@app/unraid-api/graph/resolvers/disks/disks.model.js';
|
||||
import { DisksService } from '@app/unraid-api/graph/resolvers/disks/disks.service.js';
|
||||
import { PrefixedID } from '@app/unraid-api/graph/scalars/graphql-type-prefixed-id.js';
|
||||
|
||||
@Resolver(() => Disk)
|
||||
export class DisksResolver {
|
||||
@@ -29,7 +30,7 @@ export class DisksResolver {
|
||||
resource: Resource.DISK,
|
||||
possession: AuthPossession.ANY,
|
||||
})
|
||||
public async disk(@Args('id') id: string) {
|
||||
public async disk(@Args('id', { type: () => PrefixedID }) id: string) {
|
||||
return this.disksService.getDisk(id);
|
||||
}
|
||||
|
||||
|
||||
@@ -71,11 +71,8 @@ export class ContainerMount {
|
||||
propagation!: string;
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class DockerContainer {
|
||||
@Field(() => ID)
|
||||
id!: string;
|
||||
|
||||
@ObjectType({ implements: () => Node })
|
||||
export class DockerContainer extends Node {
|
||||
@Field(() => [String])
|
||||
names!: string[];
|
||||
|
||||
@@ -119,14 +116,11 @@ export class DockerContainer {
|
||||
autoStart!: boolean;
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class DockerNetwork {
|
||||
@ObjectType({ implements: () => Node })
|
||||
export class DockerNetwork extends Node {
|
||||
@Field(() => String)
|
||||
name!: string;
|
||||
|
||||
@Field(() => ID)
|
||||
id!: string;
|
||||
|
||||
@Field(() => String)
|
||||
created!: string;
|
||||
|
||||
@@ -170,10 +164,7 @@ export class DockerNetwork {
|
||||
@ObjectType({
|
||||
implements: () => Node,
|
||||
})
|
||||
export class Docker implements Node {
|
||||
@Field(() => ID)
|
||||
id!: string;
|
||||
|
||||
export class Docker extends Node {
|
||||
@Field(() => [DockerContainer])
|
||||
containers!: DockerContainer[];
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js';
|
||||
import { DockerContainer } from '@app/unraid-api/graph/resolvers/docker/docker.model.js';
|
||||
import { DockerService } from '@app/unraid-api/graph/resolvers/docker/docker.service.js';
|
||||
import { DockerMutations } from '@app/unraid-api/graph/resolvers/mutation/mutation.model.js';
|
||||
import { PrefixedID } from '@app/unraid-api/graph/scalars/graphql-type-prefixed-id.js';
|
||||
|
||||
/**
|
||||
* Nested Resolvers for Mutations MUST use @ResolveField() instead of @Mutation()
|
||||
@@ -23,7 +24,7 @@ export class DockerMutationsResolver {
|
||||
resource: Resource.DOCKER,
|
||||
possession: AuthPossession.ANY,
|
||||
})
|
||||
public async start(@Args('id') id: string) {
|
||||
public async start(@Args('id', { type: () => PrefixedID }) id: string) {
|
||||
return this.dockerService.start(id);
|
||||
}
|
||||
|
||||
@@ -33,7 +34,7 @@ export class DockerMutationsResolver {
|
||||
resource: Resource.DOCKER,
|
||||
possession: AuthPossession.ANY,
|
||||
})
|
||||
public async stop(@Args('id') id: string) {
|
||||
public async stop(@Args('id', { type: () => PrefixedID }) id: string) {
|
||||
return this.dockerService.stop(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import { Field, ID, ObjectType } from '@nestjs/graphql';
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import { Node } from '@app/unraid-api/graph/resolvers/base.model.js';
|
||||
|
||||
@ObjectType({
|
||||
implements: () => Node,
|
||||
})
|
||||
export class Flash implements Node {
|
||||
@Field(() => ID)
|
||||
id!: string;
|
||||
|
||||
export class Flash extends Node {
|
||||
@Field(() => String)
|
||||
guid!: string;
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ export class FlashResolver {
|
||||
|
||||
return {
|
||||
id: 'flash',
|
||||
guid: emhttp.var.flashGuid,
|
||||
vendor: emhttp.var.flashVendor,
|
||||
product: emhttp.var.flashProduct,
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
import { GraphQLJSON } from 'graphql-scalars';
|
||||
|
||||
import { Node } from '@app/unraid-api/graph/resolvers/base.model.js';
|
||||
import { PrefixedID } from '@app/unraid-api/graph/scalars/graphql-type-prefixed-id.js';
|
||||
|
||||
export enum Temperature {
|
||||
C = 'C',
|
||||
@@ -31,13 +32,8 @@ registerEnumType(Theme, {
|
||||
description: 'Display theme',
|
||||
});
|
||||
|
||||
@ObjectType({
|
||||
implements: () => Node,
|
||||
})
|
||||
export class InfoApps implements Node {
|
||||
@Field(() => ID)
|
||||
id!: string;
|
||||
|
||||
@ObjectType({ implements: () => Node })
|
||||
export class InfoApps extends Node {
|
||||
@Field(() => Int, { description: 'How many docker containers are installed' })
|
||||
installed!: number;
|
||||
|
||||
@@ -45,13 +41,8 @@ export class InfoApps implements Node {
|
||||
started!: number;
|
||||
}
|
||||
|
||||
@ObjectType({
|
||||
implements: () => Node,
|
||||
})
|
||||
export class Baseboard implements Node {
|
||||
@Field(() => ID)
|
||||
id!: string;
|
||||
|
||||
@ObjectType({ implements: () => Node })
|
||||
export class Baseboard extends Node {
|
||||
@Field(() => String)
|
||||
manufacturer!: string;
|
||||
|
||||
@@ -68,13 +59,8 @@ export class Baseboard implements Node {
|
||||
assetTag?: string;
|
||||
}
|
||||
|
||||
@ObjectType({
|
||||
implements: () => Node,
|
||||
})
|
||||
export class InfoCpu implements Node {
|
||||
@Field(() => ID)
|
||||
id!: string;
|
||||
|
||||
@ObjectType({ implements: () => Node })
|
||||
export class InfoCpu extends Node {
|
||||
@Field(() => String)
|
||||
manufacturer!: string;
|
||||
|
||||
@@ -127,13 +113,8 @@ export class InfoCpu implements Node {
|
||||
flags!: string[];
|
||||
}
|
||||
|
||||
@ObjectType({
|
||||
implements: () => Node,
|
||||
})
|
||||
export class Gpu implements Node {
|
||||
@Field(() => ID)
|
||||
id!: string;
|
||||
|
||||
@ObjectType({ implements: () => Node })
|
||||
export class Gpu extends Node {
|
||||
@Field(() => String)
|
||||
type!: string;
|
||||
|
||||
@@ -153,13 +134,8 @@ export class Gpu implements Node {
|
||||
class!: string;
|
||||
}
|
||||
|
||||
@ObjectType({
|
||||
implements: () => Node,
|
||||
})
|
||||
export class Network implements Node {
|
||||
@Field(() => ID)
|
||||
id!: string;
|
||||
|
||||
@ObjectType({ implements: () => Node })
|
||||
export class Network extends Node {
|
||||
@Field(() => String, { nullable: true })
|
||||
iface?: string;
|
||||
|
||||
@@ -197,13 +173,8 @@ export class Network implements Node {
|
||||
carrierChanges?: string;
|
||||
}
|
||||
|
||||
@ObjectType({
|
||||
implements: () => Node,
|
||||
})
|
||||
export class Pci implements Node {
|
||||
@Field(() => ID)
|
||||
id!: string;
|
||||
|
||||
@ObjectType({ implements: () => Node })
|
||||
export class Pci extends Node {
|
||||
@Field(() => String, { nullable: true })
|
||||
type?: string;
|
||||
|
||||
@@ -229,22 +200,14 @@ export class Pci implements Node {
|
||||
class?: string;
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class Usb {
|
||||
@Field(() => ID)
|
||||
id!: string;
|
||||
|
||||
@ObjectType({ implements: () => Node })
|
||||
export class Usb extends Node {
|
||||
@Field(() => String, { nullable: true })
|
||||
name?: string;
|
||||
}
|
||||
|
||||
@ObjectType({
|
||||
implements: () => Node,
|
||||
})
|
||||
export class Devices implements Node {
|
||||
@Field(() => ID)
|
||||
id!: string;
|
||||
|
||||
@ObjectType({ implements: () => Node })
|
||||
export class Devices extends Node {
|
||||
@Field(() => [Gpu])
|
||||
gpu!: Gpu[];
|
||||
|
||||
@@ -255,9 +218,7 @@ export class Devices implements Node {
|
||||
usb!: Usb[];
|
||||
}
|
||||
|
||||
@ObjectType({
|
||||
implements: () => Node,
|
||||
})
|
||||
@ObjectType({ implements: () => Node })
|
||||
export class Case {
|
||||
@Field(() => String, { nullable: true })
|
||||
icon?: string;
|
||||
@@ -272,13 +233,8 @@ export class Case {
|
||||
base64?: string;
|
||||
}
|
||||
|
||||
@ObjectType({
|
||||
implements: () => Node,
|
||||
})
|
||||
export class Display implements Node {
|
||||
@Field(() => ID, { nullable: false })
|
||||
id!: string;
|
||||
|
||||
@ObjectType({ implements: () => Node })
|
||||
export class Display extends Node {
|
||||
@Field(() => Case, { nullable: true })
|
||||
case?: Case;
|
||||
|
||||
@@ -340,8 +296,8 @@ export class Display implements Node {
|
||||
locale?: string;
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class MemoryLayout {
|
||||
@ObjectType({ implements: () => Node })
|
||||
export class MemoryLayout extends Node {
|
||||
@Field(() => Int)
|
||||
size!: number;
|
||||
|
||||
@@ -376,13 +332,8 @@ export class MemoryLayout {
|
||||
voltageMax?: number;
|
||||
}
|
||||
|
||||
@ObjectType({
|
||||
implements: () => Node,
|
||||
})
|
||||
export class InfoMemory implements Node {
|
||||
@Field(() => ID)
|
||||
id!: string;
|
||||
|
||||
@ObjectType({ implements: () => Node })
|
||||
export class InfoMemory extends Node {
|
||||
@Field(() => Int)
|
||||
max!: number;
|
||||
|
||||
@@ -417,13 +368,8 @@ export class InfoMemory implements Node {
|
||||
layout!: MemoryLayout[];
|
||||
}
|
||||
|
||||
@ObjectType({
|
||||
implements: () => Node,
|
||||
})
|
||||
export class Os implements Node {
|
||||
@Field(() => ID)
|
||||
id!: string;
|
||||
|
||||
@ObjectType({ implements: () => Node })
|
||||
export class Os extends Node {
|
||||
@Field(() => String, { nullable: true })
|
||||
platform?: string;
|
||||
|
||||
@@ -461,13 +407,8 @@ export class Os implements Node {
|
||||
uptime?: string;
|
||||
}
|
||||
|
||||
@ObjectType({
|
||||
implements: () => Node,
|
||||
})
|
||||
export class System implements Node {
|
||||
@Field(() => ID)
|
||||
id!: string;
|
||||
|
||||
@ObjectType({ implements: () => Node })
|
||||
export class System extends Node {
|
||||
@Field(() => String, { nullable: true })
|
||||
manufacturer?: string;
|
||||
|
||||
@@ -487,13 +428,8 @@ export class System implements Node {
|
||||
sku?: string;
|
||||
}
|
||||
|
||||
@ObjectType({
|
||||
implements: () => Node,
|
||||
})
|
||||
export class Versions implements Node {
|
||||
@Field(() => ID)
|
||||
id!: string;
|
||||
|
||||
@ObjectType({ implements: () => Node })
|
||||
export class Versions extends Node {
|
||||
@Field(() => String, { nullable: true })
|
||||
kernel?: string;
|
||||
|
||||
@@ -573,13 +509,8 @@ export class Versions implements Node {
|
||||
unraid?: string;
|
||||
}
|
||||
|
||||
@ObjectType({
|
||||
implements: () => Node,
|
||||
})
|
||||
export class Info implements Node {
|
||||
@Field(() => ID)
|
||||
id!: string;
|
||||
|
||||
@ObjectType({ implements: () => Node })
|
||||
export class Info extends Node {
|
||||
@Field(() => InfoApps, { description: 'Count of docker containers' })
|
||||
apps!: InfoApps;
|
||||
|
||||
@@ -595,7 +526,7 @@ export class Info implements Node {
|
||||
@Field(() => Display)
|
||||
display!: Display;
|
||||
|
||||
@Field(() => ID, { description: 'Machine ID', nullable: true })
|
||||
@Field(() => PrefixedID, { description: 'Machine ID', nullable: true })
|
||||
machineId?: string;
|
||||
|
||||
@Field(() => InfoMemory)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Field, ID, InputType, Int, ObjectType, registerEnumType } from '@nestjs/graphql';
|
||||
import { Field, InputType, Int, ObjectType, registerEnumType } from '@nestjs/graphql';
|
||||
|
||||
import { IsEnum, IsInt, IsNotEmpty, IsOptional, IsString, Min } from 'class-validator';
|
||||
|
||||
import { Node } from '@app/unraid-api/graph/resolvers/base.model.js';
|
||||
|
||||
export enum NotificationType {
|
||||
UNREAD = 'UNREAD',
|
||||
ARCHIVE = 'ARCHIVE',
|
||||
@@ -109,13 +111,9 @@ export class NotificationOverview {
|
||||
archive!: NotificationCounts;
|
||||
}
|
||||
|
||||
@ObjectType('Notification')
|
||||
export class Notification {
|
||||
@Field(() => ID)
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
id!: string;
|
||||
|
||||
@ObjectType({ implements: () => Node })
|
||||
export class Notification extends Node {
|
||||
@Field({ description: "Also known as 'event'" })
|
||||
@Field({ description: "Also known as 'event'" })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@@ -157,13 +155,8 @@ export class Notification {
|
||||
formattedTimestamp?: string;
|
||||
}
|
||||
|
||||
@ObjectType('Notifications')
|
||||
export class Notifications {
|
||||
@Field(() => ID)
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
id!: string;
|
||||
|
||||
@ObjectType({ implements: () => Node })
|
||||
export class Notifications extends Node {
|
||||
@Field(() => NotificationOverview, {
|
||||
description: 'A cached overview of the notifications in the system & their severity.',
|
||||
})
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
NotificationType,
|
||||
} from '@app/unraid-api/graph/resolvers/notifications/notifications.model.js';
|
||||
import { NotificationsService } from '@app/unraid-api/graph/resolvers/notifications/notifications.service.js';
|
||||
import { PrefixedID } from '@app/unraid-api/graph/scalars/graphql-type-prefixed-id.js';
|
||||
|
||||
@Resolver(() => Notifications)
|
||||
export class NotificationsResolver {
|
||||
@@ -66,7 +67,7 @@ export class NotificationsResolver {
|
||||
|
||||
@Mutation(() => NotificationOverview)
|
||||
public async deleteNotification(
|
||||
@Args('id', { type: () => String })
|
||||
@Args('id', { type: () => PrefixedID })
|
||||
id: string,
|
||||
@Args('type', { type: () => NotificationType })
|
||||
type: NotificationType
|
||||
@@ -84,7 +85,7 @@ export class NotificationsResolver {
|
||||
|
||||
@Mutation(() => Notification, { description: 'Marks a notification as archived.' })
|
||||
public archiveNotification(
|
||||
@Args('id', { type: () => String })
|
||||
@Args('id', { type: () => PrefixedID })
|
||||
id: string
|
||||
): Promise<Notification> {
|
||||
return this.notificationsService.archiveNotification({ id });
|
||||
@@ -92,7 +93,7 @@ export class NotificationsResolver {
|
||||
|
||||
@Mutation(() => NotificationOverview)
|
||||
public async archiveNotifications(
|
||||
@Args('ids', { type: () => [String] })
|
||||
@Args('ids', { type: () => [PrefixedID] })
|
||||
ids: string[]
|
||||
): Promise<NotificationOverview> {
|
||||
await this.notificationsService.archiveIds(ids);
|
||||
@@ -110,7 +111,7 @@ export class NotificationsResolver {
|
||||
|
||||
@Mutation(() => Notification, { description: 'Marks a notification as unread.' })
|
||||
public unreadNotification(
|
||||
@Args('id', { type: () => String })
|
||||
@Args('id', { type: () => PrefixedID })
|
||||
id: string
|
||||
): Promise<Notification> {
|
||||
return this.notificationsService.markAsUnread({ id });
|
||||
@@ -118,7 +119,7 @@ export class NotificationsResolver {
|
||||
|
||||
@Mutation(() => NotificationOverview)
|
||||
public async unarchiveNotifications(
|
||||
@Args('ids', { type: () => [String] })
|
||||
@Args('ids', { type: () => [PrefixedID] })
|
||||
ids: string[]
|
||||
): Promise<NotificationOverview> {
|
||||
await this.notificationsService.unarchiveIds(ids);
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { Field, ID, ObjectType, registerEnumType } from '@nestjs/graphql';
|
||||
import { Field, ObjectType, registerEnumType } from '@nestjs/graphql';
|
||||
|
||||
import { Node } from '@app/unraid-api/graph/resolvers/base.model.js';
|
||||
|
||||
export enum RegistrationType {
|
||||
BASIC = 'BASIC',
|
||||
@@ -83,11 +85,8 @@ export class KeyFile {
|
||||
contents?: string;
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class Registration {
|
||||
@Field(() => ID, { nullable: true })
|
||||
guid?: string;
|
||||
|
||||
@ObjectType({ implements: () => Node })
|
||||
export class Registration extends Node {
|
||||
@Field(() => RegistrationType, { nullable: true })
|
||||
type?: RegistrationType;
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ export class RegistrationResolver {
|
||||
const isExpired = emhttp.var.regTy.includes('expired');
|
||||
|
||||
const registration: Registration = {
|
||||
guid: emhttp.var.regGuid,
|
||||
id: emhttp.var.regGuid,
|
||||
type: emhttp.var.regTy,
|
||||
state: emhttp.var.regState,
|
||||
// Based on https://github.com/unraid/dynamix.unraid.net/blob/c565217fa8b2acf23943dc5c22a12d526cdf70a1/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/include/state.php#L64
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { Field, ID, ObjectType, registerEnumType } from '@nestjs/graphql';
|
||||
|
||||
@ObjectType()
|
||||
export class ProfileModel {
|
||||
@Field(() => ID, { nullable: true })
|
||||
userId?: string;
|
||||
import { Node } from '@app/unraid-api/graph/resolvers/base.model.js';
|
||||
|
||||
@ObjectType({ implements: () => Node })
|
||||
export class ProfileModel extends Node {
|
||||
@Field()
|
||||
username!: string;
|
||||
|
||||
@@ -25,8 +24,8 @@ registerEnumType(ServerStatus, {
|
||||
name: 'ServerStatus',
|
||||
});
|
||||
|
||||
@ObjectType()
|
||||
export class Server {
|
||||
@ObjectType({ implements: () => Node })
|
||||
export class Server extends Node {
|
||||
@Field(() => ProfileModel)
|
||||
owner!: ProfileModel;
|
||||
|
||||
|
||||
@@ -32,10 +32,7 @@ registerEnumType(MdState, {
|
||||
@ObjectType({
|
||||
implements: () => Node,
|
||||
})
|
||||
export class Vars implements Node {
|
||||
@Field(() => ID)
|
||||
id!: string;
|
||||
|
||||
export class Vars extends Node {
|
||||
@Field({ nullable: true, description: 'Unraid version' })
|
||||
version?: string;
|
||||
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { Field, ID, InputType, ObjectType, registerEnumType } from '@nestjs/graphql';
|
||||
import { Field, InputType, ObjectType, registerEnumType } from '@nestjs/graphql';
|
||||
|
||||
import { IsEnum, IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
import { Node } from '@app/unraid-api/graph/resolvers/base.model.js';
|
||||
import { PrefixedID } from '@app/unraid-api/graph/scalars/graphql-type-prefixed-id.js';
|
||||
|
||||
// Register the VmState enum
|
||||
export enum VmState {
|
||||
@@ -17,23 +22,31 @@ registerEnumType(VmState, {
|
||||
description: 'The state of a virtual machine',
|
||||
});
|
||||
|
||||
@ObjectType()
|
||||
export class VmDomain {
|
||||
@Field(() => ID)
|
||||
uuid!: string;
|
||||
@ObjectType({ implements: () => Node })
|
||||
export class VmDomain implements Node {
|
||||
@Field(() => PrefixedID, { description: 'The unique identifier for the vm (uuid)' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
id!: string;
|
||||
|
||||
@Field({ nullable: true, description: 'A friendly name for the vm' })
|
||||
@IsString()
|
||||
name?: string;
|
||||
|
||||
@Field(() => VmState, { description: 'Current domain vm state' })
|
||||
@IsEnum(VmState)
|
||||
state!: VmState;
|
||||
|
||||
@Field(() => String, {
|
||||
nullable: true,
|
||||
description: 'The UUID of the vm',
|
||||
deprecationReason: 'Use id instead',
|
||||
})
|
||||
uuid?: string;
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class Vms {
|
||||
@Field(() => ID)
|
||||
id!: string;
|
||||
|
||||
@ObjectType({ implements: () => Node })
|
||||
export class Vms extends Node {
|
||||
@Field(() => [VmDomain], { nullable: true })
|
||||
domains?: VmDomain[];
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js';
|
||||
import { VmMutations } from '@app/unraid-api/graph/resolvers/mutation/mutation.model.js';
|
||||
import { VmsService } from '@app/unraid-api/graph/resolvers/vms/vms.service.js';
|
||||
import { PrefixedID } from '@app/unraid-api/graph/scalars/graphql-type-prefixed-id.js';
|
||||
|
||||
/**
|
||||
* Nested Resolvers for Mutations MUST use @ResolveField() instead of @Mutation()
|
||||
@@ -22,7 +23,7 @@ export class VmMutationsResolver {
|
||||
possession: AuthPossession.ANY,
|
||||
})
|
||||
@ResolveField(() => Boolean, { description: 'Start a virtual machine' })
|
||||
async start(@Args('id') id: string): Promise<boolean> {
|
||||
async start(@Args('id', { type: () => PrefixedID }) id: string): Promise<boolean> {
|
||||
return this.vmsService.startVm(id);
|
||||
}
|
||||
|
||||
@@ -32,7 +33,7 @@ export class VmMutationsResolver {
|
||||
possession: AuthPossession.ANY,
|
||||
})
|
||||
@ResolveField(() => Boolean, { description: 'Stop a virtual machine' })
|
||||
async stop(@Args('id') id: string): Promise<boolean> {
|
||||
async stop(@Args('id', { type: () => PrefixedID }) id: string): Promise<boolean> {
|
||||
return this.vmsService.stopVm(id);
|
||||
}
|
||||
|
||||
@@ -42,7 +43,7 @@ export class VmMutationsResolver {
|
||||
possession: AuthPossession.ANY,
|
||||
})
|
||||
@ResolveField(() => Boolean, { description: 'Pause a virtual machine' })
|
||||
async pause(@Args('id') id: string): Promise<boolean> {
|
||||
async pause(@Args('id', { type: () => PrefixedID }) id: string): Promise<boolean> {
|
||||
return this.vmsService.pauseVm(id);
|
||||
}
|
||||
|
||||
@@ -52,7 +53,7 @@ export class VmMutationsResolver {
|
||||
possession: AuthPossession.ANY,
|
||||
})
|
||||
@ResolveField(() => Boolean, { description: 'Resume a virtual machine' })
|
||||
async resume(@Args('id') id: string): Promise<boolean> {
|
||||
async resume(@Args('id', { type: () => PrefixedID }) id: string): Promise<boolean> {
|
||||
return this.vmsService.resumeVm(id);
|
||||
}
|
||||
|
||||
@@ -62,7 +63,7 @@ export class VmMutationsResolver {
|
||||
possession: AuthPossession.ANY,
|
||||
})
|
||||
@ResolveField(() => Boolean, { description: 'Force stop a virtual machine' })
|
||||
async forceStop(@Args('id') id: string): Promise<boolean> {
|
||||
async forceStop(@Args('id', { type: () => PrefixedID }) id: string): Promise<boolean> {
|
||||
return this.vmsService.forceStopVm(id);
|
||||
}
|
||||
|
||||
@@ -72,7 +73,7 @@ export class VmMutationsResolver {
|
||||
possession: AuthPossession.ANY,
|
||||
})
|
||||
@ResolveField(() => Boolean, { description: 'Reboot a virtual machine' })
|
||||
async reboot(@Args('id') id: string): Promise<boolean> {
|
||||
async reboot(@Args('id', { type: () => PrefixedID }) id: string): Promise<boolean> {
|
||||
return this.vmsService.rebootVm(id);
|
||||
}
|
||||
|
||||
@@ -82,7 +83,7 @@ export class VmMutationsResolver {
|
||||
possession: AuthPossession.ANY,
|
||||
})
|
||||
@ResolveField(() => Boolean, { description: 'Reset a virtual machine' })
|
||||
async reset(@Args('id') id: string): Promise<boolean> {
|
||||
async reset(@Args('id', { type: () => PrefixedID }) id: string): Promise<boolean> {
|
||||
return this.vmsService.resetVm(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,10 +261,10 @@ describe('VmsService', () => {
|
||||
|
||||
it('should start and stop the test VM', async () => {
|
||||
expect(testVm).toBeDefined();
|
||||
expect(testVm?.uuid).toBeDefined();
|
||||
expect(testVm?.id).toBeDefined();
|
||||
|
||||
// Start the VM
|
||||
const startResult = await service.startVm(testVm!.uuid);
|
||||
const startResult = await service.startVm(testVm!.id);
|
||||
expect(startResult).toBe(true);
|
||||
|
||||
// Wait for VM to start with a more targeted approach
|
||||
@@ -282,7 +282,7 @@ describe('VmsService', () => {
|
||||
expect(isRunning).toBe(true);
|
||||
|
||||
// Stop the VM
|
||||
const stopResult = await service.stopVm(testVm!.uuid);
|
||||
const stopResult = await service.stopVm(testVm!.id);
|
||||
expect(stopResult).toBe(true);
|
||||
|
||||
// Wait for VM to stop with a more targeted approach
|
||||
|
||||
@@ -360,8 +360,9 @@ export class VmsService implements OnModuleInit, OnModuleDestroy {
|
||||
const state = this.mapDomainStateToVmState(info.state);
|
||||
|
||||
return {
|
||||
name,
|
||||
id: uuid,
|
||||
uuid,
|
||||
name,
|
||||
state,
|
||||
};
|
||||
})
|
||||
|
||||
107
api/src/unraid-api/graph/scalars/graphql-type-prefixed-id.ts
Normal file
107
api/src/unraid-api/graph/scalars/graphql-type-prefixed-id.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import { CustomScalar, Scalar } from '@nestjs/graphql';
|
||||
|
||||
import { Kind, ValueNode } from 'graphql';
|
||||
|
||||
import { getServerIdentifier } from '@app/core/utils/server-identifier.js';
|
||||
|
||||
@Scalar('PrefixedID', () => PrefixedID)
|
||||
export class PrefixedID implements CustomScalar<string, string> {
|
||||
description: string = `
|
||||
### Description:
|
||||
|
||||
ID scalar type that prefixes the underlying ID with the server identifier on output and strips it on input.
|
||||
|
||||
We use this scalar type to ensure that the ID is unique across all servers, allowing the same underlying resource ID to be used across different server instances.
|
||||
|
||||
#### Input Behavior:
|
||||
|
||||
When providing an ID as input (e.g., in arguments or input objects), the server identifier prefix ('<serverId>:') is optional.
|
||||
|
||||
- If the prefix is present (e.g., '123:456'), it will be automatically stripped, and only the underlying ID ('456') will be used internally.
|
||||
- If the prefix is absent (e.g., '456'), the ID will be used as-is.
|
||||
|
||||
This makes it flexible for clients, as they don't strictly need to know or provide the server ID.
|
||||
|
||||
#### Output Behavior:
|
||||
|
||||
When an ID is returned in the response (output), it will *always* be prefixed with the current server's unique identifier (e.g., '123:456').
|
||||
|
||||
#### Example:
|
||||
|
||||
Note: The server identifier is '123' in this example.
|
||||
|
||||
##### Input (Prefix Optional):
|
||||
\`\`\`graphql
|
||||
# Both of these are valid inputs resolving to internal ID '456'
|
||||
{
|
||||
someQuery(id: "123:456") { ... }
|
||||
anotherQuery(id: "456") { ... }
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
##### Output (Prefix Always Added):
|
||||
\`\`\`graphql
|
||||
# Assuming internal ID is '456'
|
||||
{
|
||||
"data": {
|
||||
"someResource": {
|
||||
"id": "123:456"
|
||||
}
|
||||
}
|
||||
}
|
||||
\`\`\`
|
||||
`;
|
||||
|
||||
// For output: Add the prefix
|
||||
serialize(value: unknown): string {
|
||||
if (typeof value !== 'string') {
|
||||
// Consider logging this error or handling it based on your specific needs
|
||||
console.error(`PrefixedID cannot represent non-string value: ${value}`);
|
||||
throw new Error(`PrefixedID cannot represent non-string value: ${value}`);
|
||||
}
|
||||
// Simple check to avoid double-prefixing if somehow already prefixed
|
||||
// This might happen if data is fetched internally already prefixed
|
||||
if (value.includes(':')) {
|
||||
// Optionally log or verify if the prefix matches the current serverId
|
||||
// const serverId = this.serverIdentifierService.getId();
|
||||
// if (!value.startsWith(`${serverId}:`)) {
|
||||
// console.warn(`PrefixedID serialize: Value '${value}' already has a different prefix.`);
|
||||
// }
|
||||
return value;
|
||||
}
|
||||
const serverId = getServerIdentifier();
|
||||
return `${serverId}:${value}`;
|
||||
}
|
||||
|
||||
// For input variables: Remove the prefix
|
||||
parseValue(value: unknown): string {
|
||||
if (typeof value !== 'string') {
|
||||
throw new Error(`PrefixedID cannot represent non-string value: ${value}`);
|
||||
}
|
||||
// Expecting '<serverId>:<originalId>'
|
||||
const parts = value.split(':');
|
||||
if (parts.length === 2) {
|
||||
return parts[1];
|
||||
}
|
||||
// If it doesn't have the prefix format, assume it's an internal ID already.
|
||||
// console.debug(`PrefixedID parseValue: Value '${value}' does not contain expected prefix.`);
|
||||
return value;
|
||||
}
|
||||
|
||||
// For inline query arguments: Remove the prefix
|
||||
parseLiteral(ast: ValueNode): string {
|
||||
if (ast.kind !== Kind.STRING) {
|
||||
// Handle or throw error for non-string literals if necessary
|
||||
throw new Error(
|
||||
`PrefixedID cannot represent non-string literal value: ${'value' in ast ? ast.value : null}`
|
||||
);
|
||||
}
|
||||
// Same logic as parseValue
|
||||
const parts = ast.value.split(':');
|
||||
if (parts.length === 2) {
|
||||
return parts[1];
|
||||
}
|
||||
// console.debug(`PrefixedID parseLiteral: Value '${ast.value}' does not contain expected prefix.`);
|
||||
return ast.value;
|
||||
}
|
||||
}
|
||||
@@ -11,10 +11,7 @@ export class Uptime {
|
||||
@ObjectType({
|
||||
implements: () => Node,
|
||||
})
|
||||
export class Service implements Node {
|
||||
@Field(() => ID)
|
||||
id!: string;
|
||||
|
||||
export class Service extends Node {
|
||||
@Field(() => String, { nullable: true })
|
||||
name?: string;
|
||||
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import { Field, ID, ObjectType } from '@nestjs/graphql';
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import { Permission } from '@app/unraid-api/graph/resolvers/api-key/api-key.model.js';
|
||||
import { Role } from '@app/unraid-api/graph/resolvers/base.model.js';
|
||||
|
||||
@ObjectType()
|
||||
export class UserAccount {
|
||||
@Field(() => ID, { description: 'A unique identifier for the user' })
|
||||
id!: string;
|
||||
import { Node, Role } from '@app/unraid-api/graph/resolvers/base.model.js';
|
||||
|
||||
@ObjectType({ implements: () => Node })
|
||||
export class UserAccount extends Node {
|
||||
@Field({ description: 'The name of the user' })
|
||||
name!: string;
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ const config: CodegenConfig = {
|
||||
URL: 'URL',
|
||||
Port: 'number',
|
||||
UUID: 'string',
|
||||
PrefixedID: 'string',
|
||||
},
|
||||
},
|
||||
generates: {
|
||||
|
||||
@@ -35,7 +35,7 @@ export const getNotifications = graphql(/* GraphQL */ `
|
||||
`);
|
||||
|
||||
export const archiveNotification = graphql(/* GraphQL */ `
|
||||
mutation ArchiveNotification($id: String!) {
|
||||
mutation ArchiveNotification($id: PrefixedID!) {
|
||||
archiveNotification(id: $id) {
|
||||
...NotificationFragment
|
||||
}
|
||||
@@ -59,7 +59,7 @@ export const archiveAllNotifications = graphql(/* GraphQL */ `
|
||||
`);
|
||||
|
||||
export const deleteNotification = graphql(/* GraphQL */ `
|
||||
mutation DeleteNotification($id: String!, $type: NotificationType!) {
|
||||
mutation DeleteNotification($id: PrefixedID!, $type: NotificationType!) {
|
||||
deleteNotification(id: $id, type: $type) {
|
||||
archive {
|
||||
total
|
||||
|
||||
@@ -22,9 +22,9 @@ type Documents = {
|
||||
"\n fragment NotificationFragment on Notification {\n id\n title\n subject\n description\n importance\n link\n type\n timestamp\n formattedTimestamp\n }\n": typeof types.NotificationFragmentFragmentDoc,
|
||||
"\n fragment NotificationCountFragment on NotificationCounts {\n total\n info\n warning\n alert\n }\n": typeof types.NotificationCountFragmentFragmentDoc,
|
||||
"\n query Notifications($filter: NotificationFilter!) {\n notifications {\n id\n list(filter: $filter) {\n ...NotificationFragment\n }\n }\n }\n": typeof types.NotificationsDocument,
|
||||
"\n mutation ArchiveNotification($id: String!) {\n archiveNotification(id: $id) {\n ...NotificationFragment\n }\n }\n": typeof types.ArchiveNotificationDocument,
|
||||
"\n mutation ArchiveNotification($id: PrefixedID!) {\n archiveNotification(id: $id) {\n ...NotificationFragment\n }\n }\n": typeof types.ArchiveNotificationDocument,
|
||||
"\n mutation ArchiveAllNotifications {\n archiveAll {\n unread {\n total\n }\n archive {\n info\n warning\n alert\n total\n }\n }\n }\n": typeof types.ArchiveAllNotificationsDocument,
|
||||
"\n mutation DeleteNotification($id: String!, $type: NotificationType!) {\n deleteNotification(id: $id, type: $type) {\n archive {\n total\n }\n }\n }\n": typeof types.DeleteNotificationDocument,
|
||||
"\n mutation DeleteNotification($id: PrefixedID!, $type: NotificationType!) {\n deleteNotification(id: $id, type: $type) {\n archive {\n total\n }\n }\n }\n": typeof types.DeleteNotificationDocument,
|
||||
"\n mutation DeleteAllNotifications {\n deleteArchivedNotifications {\n archive {\n total\n }\n unread {\n total\n }\n }\n }\n": typeof types.DeleteAllNotificationsDocument,
|
||||
"\n query Overview {\n notifications {\n id\n overview {\n unread {\n info\n warning\n alert\n total\n }\n archive {\n total\n }\n }\n }\n }\n": typeof types.OverviewDocument,
|
||||
"\n mutation RecomputeOverview {\n recalculateOverview {\n archive {\n ...NotificationCountFragment\n }\n unread {\n ...NotificationCountFragment\n }\n }\n }\n": typeof types.RecomputeOverviewDocument,
|
||||
@@ -48,9 +48,9 @@ const documents: Documents = {
|
||||
"\n fragment NotificationFragment on Notification {\n id\n title\n subject\n description\n importance\n link\n type\n timestamp\n formattedTimestamp\n }\n": types.NotificationFragmentFragmentDoc,
|
||||
"\n fragment NotificationCountFragment on NotificationCounts {\n total\n info\n warning\n alert\n }\n": types.NotificationCountFragmentFragmentDoc,
|
||||
"\n query Notifications($filter: NotificationFilter!) {\n notifications {\n id\n list(filter: $filter) {\n ...NotificationFragment\n }\n }\n }\n": types.NotificationsDocument,
|
||||
"\n mutation ArchiveNotification($id: String!) {\n archiveNotification(id: $id) {\n ...NotificationFragment\n }\n }\n": types.ArchiveNotificationDocument,
|
||||
"\n mutation ArchiveNotification($id: PrefixedID!) {\n archiveNotification(id: $id) {\n ...NotificationFragment\n }\n }\n": types.ArchiveNotificationDocument,
|
||||
"\n mutation ArchiveAllNotifications {\n archiveAll {\n unread {\n total\n }\n archive {\n info\n warning\n alert\n total\n }\n }\n }\n": types.ArchiveAllNotificationsDocument,
|
||||
"\n mutation DeleteNotification($id: String!, $type: NotificationType!) {\n deleteNotification(id: $id, type: $type) {\n archive {\n total\n }\n }\n }\n": types.DeleteNotificationDocument,
|
||||
"\n mutation DeleteNotification($id: PrefixedID!, $type: NotificationType!) {\n deleteNotification(id: $id, type: $type) {\n archive {\n total\n }\n }\n }\n": types.DeleteNotificationDocument,
|
||||
"\n mutation DeleteAllNotifications {\n deleteArchivedNotifications {\n archive {\n total\n }\n unread {\n total\n }\n }\n }\n": types.DeleteAllNotificationsDocument,
|
||||
"\n query Overview {\n notifications {\n id\n overview {\n unread {\n info\n warning\n alert\n total\n }\n archive {\n total\n }\n }\n }\n }\n": types.OverviewDocument,
|
||||
"\n mutation RecomputeOverview {\n recalculateOverview {\n archive {\n ...NotificationCountFragment\n }\n unread {\n ...NotificationCountFragment\n }\n }\n }\n": types.RecomputeOverviewDocument,
|
||||
@@ -115,7 +115,7 @@ export function graphql(source: "\n query Notifications($filter: NotificationFi
|
||||
/**
|
||||
* 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 ArchiveNotification($id: String!) {\n archiveNotification(id: $id) {\n ...NotificationFragment\n }\n }\n"): (typeof documents)["\n mutation ArchiveNotification($id: String!) {\n archiveNotification(id: $id) {\n ...NotificationFragment\n }\n }\n"];
|
||||
export function graphql(source: "\n mutation ArchiveNotification($id: PrefixedID!) {\n archiveNotification(id: $id) {\n ...NotificationFragment\n }\n }\n"): (typeof documents)["\n mutation ArchiveNotification($id: PrefixedID!) {\n archiveNotification(id: $id) {\n ...NotificationFragment\n }\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
@@ -123,7 +123,7 @@ export function graphql(source: "\n mutation ArchiveAllNotifications {\n arc
|
||||
/**
|
||||
* 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 DeleteNotification($id: String!, $type: NotificationType!) {\n deleteNotification(id: $id, type: $type) {\n archive {\n total\n }\n }\n }\n"): (typeof documents)["\n mutation DeleteNotification($id: String!, $type: NotificationType!) {\n deleteNotification(id: $id, type: $type) {\n archive {\n total\n }\n }\n }\n"];
|
||||
export function graphql(source: "\n mutation DeleteNotification($id: PrefixedID!, $type: NotificationType!) {\n deleteNotification(id: $id, type: $type) {\n archive {\n total\n }\n }\n }\n"): (typeof documents)["\n mutation DeleteNotification($id: PrefixedID!, $type: NotificationType!) {\n deleteNotification(id: $id, type: $type) {\n archive {\n total\n }\n }\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
|
||||
@@ -22,6 +22,56 @@ export type Scalars = {
|
||||
JSONObject: { input: any; output: any; }
|
||||
/** The `Long` scalar type represents 52-bit integers */
|
||||
Long: { input: number; output: number; }
|
||||
/** A field whose value is a valid TCP port within the range of 0 to 65535: https://en.wikipedia.org/wiki/Transmission_Control_Protocol#TCP_ports */
|
||||
Port: { input: number; output: number; }
|
||||
/**
|
||||
*
|
||||
* ### Description:
|
||||
*
|
||||
* ID scalar type that prefixes the underlying ID with the server identifier on output and strips it on input.
|
||||
*
|
||||
* We use this scalar type to ensure that the ID is unique across all servers, allowing the same underlying resource ID to be used across different server instances.
|
||||
*
|
||||
* #### Input Behavior:
|
||||
*
|
||||
* When providing an ID as input (e.g., in arguments or input objects), the server identifier prefix ('<serverId>:') is optional.
|
||||
*
|
||||
* - If the prefix is present (e.g., '123:456'), it will be automatically stripped, and only the underlying ID ('456') will be used internally.
|
||||
* - If the prefix is absent (e.g., '456'), the ID will be used as-is.
|
||||
*
|
||||
* This makes it flexible for clients, as they don't strictly need to know or provide the server ID.
|
||||
*
|
||||
* #### Output Behavior:
|
||||
*
|
||||
* When an ID is returned in the response (output), it will *always* be prefixed with the current server's unique identifier (e.g., '123:456').
|
||||
*
|
||||
* #### Example:
|
||||
*
|
||||
* Note: The server identifier is '123' in this example.
|
||||
*
|
||||
* ##### Input (Prefix Optional):
|
||||
* ```graphql
|
||||
* # Both of these are valid inputs resolving to internal ID '456'
|
||||
* {
|
||||
* someQuery(id: "123:456") { ... }
|
||||
* anotherQuery(id: "456") { ... }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* ##### Output (Prefix Always Added):
|
||||
* ```graphql
|
||||
* # Assuming internal ID is '456'
|
||||
* {
|
||||
* "data": {
|
||||
* "someResource": {
|
||||
* "id": "123:456"
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
PrefixedID: { input: string; output: string; }
|
||||
/** A field whose value conforms to the standard URL format as specified in RFC3986: https://www.ietf.org/rfc/rfc3986.txt. */
|
||||
URL: { input: URL; output: URL; }
|
||||
};
|
||||
@@ -34,13 +84,20 @@ export type AccessUrl = {
|
||||
type: UrlType;
|
||||
};
|
||||
|
||||
export type AccessUrlInput = {
|
||||
ipv4?: InputMaybe<Scalars['URL']['input']>;
|
||||
ipv6?: InputMaybe<Scalars['URL']['input']>;
|
||||
name?: InputMaybe<Scalars['String']['input']>;
|
||||
type: UrlType;
|
||||
};
|
||||
|
||||
export type AddPermissionInput = {
|
||||
actions: Array<Scalars['String']['input']>;
|
||||
resource: Resource;
|
||||
};
|
||||
|
||||
export type AddRoleForApiKeyInput = {
|
||||
apiKeyId: Scalars['ID']['input'];
|
||||
apiKeyId: Scalars['PrefixedID']['input'];
|
||||
role: Role;
|
||||
};
|
||||
|
||||
@@ -49,11 +106,11 @@ export type AllowedOriginInput = {
|
||||
origins: Array<Scalars['String']['input']>;
|
||||
};
|
||||
|
||||
export type ApiKey = {
|
||||
export type ApiKey = Node & {
|
||||
__typename?: 'ApiKey';
|
||||
createdAt: Scalars['String']['output'];
|
||||
description?: Maybe<Scalars['String']['output']>;
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
name: Scalars['String']['output'];
|
||||
permissions: Array<Permission>;
|
||||
roles: Array<Role>;
|
||||
@@ -65,11 +122,11 @@ export type ApiKeyResponse = {
|
||||
valid: Scalars['Boolean']['output'];
|
||||
};
|
||||
|
||||
export type ApiKeyWithSecret = {
|
||||
export type ApiKeyWithSecret = Node & {
|
||||
__typename?: 'ApiKeyWithSecret';
|
||||
createdAt: Scalars['String']['output'];
|
||||
description?: Maybe<Scalars['String']['output']>;
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
key: Scalars['String']['output'];
|
||||
name: Scalars['String']['output'];
|
||||
permissions: Array<Permission>;
|
||||
@@ -118,8 +175,7 @@ export type ArrayDisk = Node & {
|
||||
fsType?: Maybe<Scalars['String']['output']>;
|
||||
/** (KB) Used Size on the FS (Not present on Parity type drive) */
|
||||
fsUsed?: Maybe<Scalars['Long']['output']>;
|
||||
/** Disk identifier, only set for present disks on the system */
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
/** Array slot number. Parity1 is always 0 and Parity2 is always 29. Array slots will be 1 - 28. Cache slots are 30 - 53. Flash is 54. */
|
||||
idx: Scalars['Int']['output'];
|
||||
name?: Maybe<Scalars['String']['output']>;
|
||||
@@ -158,7 +214,7 @@ export enum ArrayDiskFsColor {
|
||||
|
||||
export type ArrayDiskInput = {
|
||||
/** Disk ID */
|
||||
id: Scalars['ID']['input'];
|
||||
id: Scalars['PrefixedID']['input'];
|
||||
/** The slot for the disk */
|
||||
slot?: InputMaybe<Scalars['Int']['input']>;
|
||||
};
|
||||
@@ -205,12 +261,12 @@ export type ArrayMutationsAddDiskToArrayArgs = {
|
||||
|
||||
|
||||
export type ArrayMutationsClearArrayDiskStatisticsArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
id: Scalars['PrefixedID']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type ArrayMutationsMountArrayDiskArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
id: Scalars['PrefixedID']['input'];
|
||||
};
|
||||
|
||||
|
||||
@@ -225,16 +281,9 @@ export type ArrayMutationsSetStateArgs = {
|
||||
|
||||
|
||||
export type ArrayMutationsUnmountArrayDiskArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
id: Scalars['PrefixedID']['input'];
|
||||
};
|
||||
|
||||
export enum ArrayPendingState {
|
||||
NO_DATA_DISKS = 'NO_DATA_DISKS',
|
||||
STARTING = 'STARTING',
|
||||
STOPPING = 'STOPPING',
|
||||
TOO_MANY_MISSING_DISKS = 'TOO_MANY_MISSING_DISKS'
|
||||
}
|
||||
|
||||
export enum ArrayState {
|
||||
DISABLE_DISK = 'DISABLE_DISK',
|
||||
INVALID_EXPANSION = 'INVALID_EXPANSION',
|
||||
@@ -259,10 +308,25 @@ export enum ArrayStateInputState {
|
||||
STOP = 'STOP'
|
||||
}
|
||||
|
||||
/** Available authentication action verbs */
|
||||
export enum AuthActionVerb {
|
||||
CREATE = 'CREATE',
|
||||
DELETE = 'DELETE',
|
||||
READ = 'READ',
|
||||
UPDATE = 'UPDATE'
|
||||
}
|
||||
|
||||
/** Available authentication possession types */
|
||||
export enum AuthPossession {
|
||||
ANY = 'ANY',
|
||||
OWN = 'OWN',
|
||||
OWN_ANY = 'OWN_ANY'
|
||||
}
|
||||
|
||||
export type Baseboard = Node & {
|
||||
__typename?: 'Baseboard';
|
||||
assetTag?: Maybe<Scalars['String']['output']>;
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
manufacturer: Scalars['String']['output'];
|
||||
model?: Maybe<Scalars['String']['output']>;
|
||||
serial?: Maybe<Scalars['String']['output']>;
|
||||
@@ -284,7 +348,7 @@ export type Case = Node & {
|
||||
base64?: Maybe<Scalars['String']['output']>;
|
||||
error?: Maybe<Scalars['String']['output']>;
|
||||
icon?: Maybe<Scalars['String']['output']>;
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
url?: Maybe<Scalars['String']['output']>;
|
||||
};
|
||||
|
||||
@@ -308,7 +372,7 @@ export type CloudResponse = {
|
||||
export type Config = Node & {
|
||||
__typename?: 'Config';
|
||||
error?: Maybe<Scalars['String']['output']>;
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
valid?: Maybe<Scalars['Boolean']['output']>;
|
||||
};
|
||||
|
||||
@@ -325,8 +389,7 @@ export type Connect = Node & {
|
||||
__typename?: 'Connect';
|
||||
/** The status of dynamic remote access */
|
||||
dynamicRemoteAccess: DynamicRemoteAccessStatus;
|
||||
/** The unique identifier for the Connect instance */
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
/** The settings for the Connect instance */
|
||||
settings: ConnectSettings;
|
||||
};
|
||||
@@ -335,8 +398,7 @@ export type ConnectSettings = Node & {
|
||||
__typename?: 'ConnectSettings';
|
||||
/** The data schema for the Connect settings */
|
||||
dataSchema: Scalars['JSON']['output'];
|
||||
/** The unique identifier for the Connect settings */
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
/** The UI schema for the Connect settings */
|
||||
uiSchema: Scalars['JSON']['output'];
|
||||
/** The values for the Connect settings */
|
||||
@@ -389,8 +451,8 @@ export type ContainerHostConfig = {
|
||||
export type ContainerPort = {
|
||||
__typename?: 'ContainerPort';
|
||||
ip?: Maybe<Scalars['String']['output']>;
|
||||
privatePort: Scalars['Int']['output'];
|
||||
publicPort: Scalars['Int']['output'];
|
||||
privatePort?: Maybe<Scalars['Port']['output']>;
|
||||
publicPort?: Maybe<Scalars['Port']['output']>;
|
||||
type: ContainerPortType;
|
||||
};
|
||||
|
||||
@@ -416,12 +478,12 @@ export type CreateApiKeyInput = {
|
||||
export type Devices = Node & {
|
||||
__typename?: 'Devices';
|
||||
gpu: Array<Gpu>;
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
pci: Array<Pci>;
|
||||
usb: Array<Usb>;
|
||||
};
|
||||
|
||||
export type Disk = {
|
||||
export type Disk = Node & {
|
||||
__typename?: 'Disk';
|
||||
/** The number of bytes per sector */
|
||||
bytesPerSector: Scalars['Float']['output'];
|
||||
@@ -429,8 +491,7 @@ export type Disk = {
|
||||
device: Scalars['String']['output'];
|
||||
/** The firmware revision of the disk */
|
||||
firmwareRevision: Scalars['String']['output'];
|
||||
/** The unique identifier of the disk */
|
||||
id: Scalars['String']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
/** The interface type of the disk */
|
||||
interfaceType: DiskInterfaceType;
|
||||
/** The model name of the disk */
|
||||
@@ -506,7 +567,7 @@ export type Display = Node & {
|
||||
dashapps?: Maybe<Scalars['String']['output']>;
|
||||
date?: Maybe<Scalars['String']['output']>;
|
||||
hot?: Maybe<Scalars['Int']['output']>;
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
locale?: Maybe<Scalars['String']['output']>;
|
||||
max?: Maybe<Scalars['Int']['output']>;
|
||||
number?: Maybe<Scalars['String']['output']>;
|
||||
@@ -526,17 +587,27 @@ export type Display = Node & {
|
||||
export type Docker = Node & {
|
||||
__typename?: 'Docker';
|
||||
containers: Array<DockerContainer>;
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
networks: Array<DockerNetwork>;
|
||||
};
|
||||
|
||||
export type DockerContainer = {
|
||||
|
||||
export type DockerContainersArgs = {
|
||||
skipCache?: Scalars['Boolean']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type DockerNetworksArgs = {
|
||||
skipCache?: Scalars['Boolean']['input'];
|
||||
};
|
||||
|
||||
export type DockerContainer = Node & {
|
||||
__typename?: 'DockerContainer';
|
||||
autoStart: Scalars['Boolean']['output'];
|
||||
command: Scalars['String']['output'];
|
||||
created: Scalars['Int']['output'];
|
||||
hostConfig?: Maybe<ContainerHostConfig>;
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
image: Scalars['String']['output'];
|
||||
imageId: Scalars['String']['output'];
|
||||
labels?: Maybe<Scalars['JSONObject']['output']>;
|
||||
@@ -560,15 +631,15 @@ export type DockerMutations = {
|
||||
|
||||
|
||||
export type DockerMutationsStartArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
id: Scalars['PrefixedID']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type DockerMutationsStopArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
id: Scalars['PrefixedID']['input'];
|
||||
};
|
||||
|
||||
export type DockerNetwork = {
|
||||
export type DockerNetwork = Node & {
|
||||
__typename?: 'DockerNetwork';
|
||||
attachable: Scalars['Boolean']['output'];
|
||||
configFrom: Scalars['JSONObject']['output'];
|
||||
@@ -577,7 +648,7 @@ export type DockerNetwork = {
|
||||
created: Scalars['String']['output'];
|
||||
driver: Scalars['String']['output'];
|
||||
enableIPv6: Scalars['Boolean']['output'];
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
ingress: Scalars['Boolean']['output'];
|
||||
internal: Scalars['Boolean']['output'];
|
||||
ipam: Scalars['JSONObject']['output'];
|
||||
@@ -606,14 +677,14 @@ export enum DynamicRemoteAccessType {
|
||||
export type EnableDynamicRemoteAccessInput = {
|
||||
/** Whether to enable or disable dynamic remote access */
|
||||
enabled: Scalars['Boolean']['input'];
|
||||
/** The URL for dynamic remote access */
|
||||
url: Scalars['URL']['input'];
|
||||
/** The AccessURL Input for dynamic remote access */
|
||||
url: AccessUrlInput;
|
||||
};
|
||||
|
||||
export type Flash = Node & {
|
||||
__typename?: 'Flash';
|
||||
guid: Scalars['String']['output'];
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
product: Scalars['String']['output'];
|
||||
vendor: Scalars['String']['output'];
|
||||
};
|
||||
@@ -622,7 +693,7 @@ export type Gpu = Node & {
|
||||
__typename?: 'Gpu';
|
||||
blacklisted: Scalars['Boolean']['output'];
|
||||
class: Scalars['String']['output'];
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
productid: Scalars['String']['output'];
|
||||
type: Scalars['String']['output'];
|
||||
typeid: Scalars['String']['output'];
|
||||
@@ -637,9 +708,9 @@ export type Info = Node & {
|
||||
cpu: InfoCpu;
|
||||
devices: Devices;
|
||||
display: Display;
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
/** Machine ID */
|
||||
machineId?: Maybe<Scalars['ID']['output']>;
|
||||
machineId?: Maybe<Scalars['PrefixedID']['output']>;
|
||||
memory: InfoMemory;
|
||||
os: Os;
|
||||
system: System;
|
||||
@@ -649,7 +720,7 @@ export type Info = Node & {
|
||||
|
||||
export type InfoApps = Node & {
|
||||
__typename?: 'InfoApps';
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
/** How many docker containers are installed */
|
||||
installed: Scalars['Int']['output'];
|
||||
/** How many docker containers are running */
|
||||
@@ -663,7 +734,7 @@ export type InfoCpu = Node & {
|
||||
cores: Scalars['Int']['output'];
|
||||
family: Scalars['String']['output'];
|
||||
flags: Array<Scalars['String']['output']>;
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
manufacturer: Scalars['String']['output'];
|
||||
model: Scalars['String']['output'];
|
||||
processors: Scalars['Int']['output'];
|
||||
@@ -684,7 +755,7 @@ export type InfoMemory = Node & {
|
||||
available: Scalars['Int']['output'];
|
||||
buffcache: Scalars['Int']['output'];
|
||||
free: Scalars['Int']['output'];
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
layout: Array<MemoryLayout>;
|
||||
max: Scalars['Int']['output'];
|
||||
swapfree: Scalars['Int']['output'];
|
||||
@@ -724,11 +795,12 @@ export type LogFileContent = {
|
||||
totalLines: Scalars['Int']['output'];
|
||||
};
|
||||
|
||||
export type MemoryLayout = {
|
||||
export type MemoryLayout = Node & {
|
||||
__typename?: 'MemoryLayout';
|
||||
bank?: Maybe<Scalars['String']['output']>;
|
||||
clockSpeed?: Maybe<Scalars['Int']['output']>;
|
||||
formFactor?: Maybe<Scalars['String']['output']>;
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
manufacturer?: Maybe<Scalars['String']['output']>;
|
||||
partNum?: Maybe<Scalars['String']['output']>;
|
||||
serialNum?: Maybe<Scalars['String']['output']>;
|
||||
@@ -762,7 +834,6 @@ export type Mutation = {
|
||||
archiveNotification: Notification;
|
||||
archiveNotifications: NotificationOverview;
|
||||
array: ArrayMutations;
|
||||
cancelParityCheck: Scalars['JSON']['output'];
|
||||
connectSignIn: Scalars['Boolean']['output'];
|
||||
connectSignOut: Scalars['Boolean']['output'];
|
||||
createApiKey: ApiKeyWithSecret;
|
||||
@@ -773,33 +844,19 @@ export type Mutation = {
|
||||
deleteNotification: NotificationOverview;
|
||||
docker: DockerMutations;
|
||||
enableDynamicRemoteAccess: Scalars['Boolean']['output'];
|
||||
/** Force stop a virtual machine */
|
||||
forceStopVm: Scalars['Boolean']['output'];
|
||||
pauseParityCheck: Scalars['JSON']['output'];
|
||||
/** Pause a virtual machine */
|
||||
pauseVm: Scalars['Boolean']['output'];
|
||||
/** Reboot a virtual machine */
|
||||
rebootVm: Scalars['Boolean']['output'];
|
||||
parityCheck: ParityCheckMutations;
|
||||
/** Reads each notification to recompute & update the overview. */
|
||||
recalculateOverview: NotificationOverview;
|
||||
removeRoleFromApiKey: Scalars['Boolean']['output'];
|
||||
/** Reset a virtual machine */
|
||||
resetVm: Scalars['Boolean']['output'];
|
||||
resumeParityCheck: Scalars['JSON']['output'];
|
||||
/** Resume a virtual machine */
|
||||
resumeVm: Scalars['Boolean']['output'];
|
||||
setAdditionalAllowedOrigins: Array<Scalars['String']['output']>;
|
||||
setDemo: Scalars['String']['output'];
|
||||
setupRemoteAccess: Scalars['Boolean']['output'];
|
||||
startParityCheck: Scalars['JSON']['output'];
|
||||
/** Start a virtual machine */
|
||||
startVm: Scalars['Boolean']['output'];
|
||||
/** Stop a virtual machine */
|
||||
stopVm: Scalars['Boolean']['output'];
|
||||
unarchiveAll: NotificationOverview;
|
||||
unarchiveNotifications: NotificationOverview;
|
||||
/** Marks a notification as unread. */
|
||||
unreadNotification: Notification;
|
||||
updateApiSettings: ConnectSettingsValues;
|
||||
vm: VmMutations;
|
||||
};
|
||||
|
||||
|
||||
@@ -814,12 +871,12 @@ export type MutationArchiveAllArgs = {
|
||||
|
||||
|
||||
export type MutationArchiveNotificationArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
id: Scalars['PrefixedID']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type MutationArchiveNotificationsArgs = {
|
||||
ids: Array<Scalars['String']['input']>;
|
||||
ids: Array<Scalars['PrefixedID']['input']>;
|
||||
};
|
||||
|
||||
|
||||
@@ -839,7 +896,7 @@ export type MutationCreateNotificationArgs = {
|
||||
|
||||
|
||||
export type MutationDeleteNotificationArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
id: Scalars['PrefixedID']['input'];
|
||||
type: NotificationType;
|
||||
};
|
||||
|
||||
@@ -849,36 +906,11 @@ export type MutationEnableDynamicRemoteAccessArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationForceStopVmArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type MutationPauseVmArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type MutationRebootVmArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type MutationRemoveRoleFromApiKeyArgs = {
|
||||
input: RemoveRoleFromApiKeyInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationResetVmArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type MutationResumeVmArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type MutationSetAdditionalAllowedOriginsArgs = {
|
||||
input: AllowedOriginInput;
|
||||
};
|
||||
@@ -889,33 +921,18 @@ export type MutationSetupRemoteAccessArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationStartParityCheckArgs = {
|
||||
correct: Scalars['Boolean']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type MutationStartVmArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type MutationStopVmArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type MutationUnarchiveAllArgs = {
|
||||
importance?: InputMaybe<NotificationImportance>;
|
||||
};
|
||||
|
||||
|
||||
export type MutationUnarchiveNotificationsArgs = {
|
||||
ids: Array<Scalars['String']['input']>;
|
||||
ids: Array<Scalars['PrefixedID']['input']>;
|
||||
};
|
||||
|
||||
|
||||
export type MutationUnreadNotificationArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
id: Scalars['PrefixedID']['input'];
|
||||
};
|
||||
|
||||
|
||||
@@ -926,18 +943,18 @@ export type MutationUpdateApiSettingsArgs = {
|
||||
export type Network = Node & {
|
||||
__typename?: 'Network';
|
||||
accessUrls?: Maybe<Array<AccessUrl>>;
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
};
|
||||
|
||||
export type Node = {
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
};
|
||||
|
||||
export type Notification = {
|
||||
export type Notification = Node & {
|
||||
__typename?: 'Notification';
|
||||
description: Scalars['String']['output'];
|
||||
formattedTimestamp?: Maybe<Scalars['String']['output']>;
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
importance: NotificationImportance;
|
||||
link?: Maybe<Scalars['String']['output']>;
|
||||
subject: Scalars['String']['output'];
|
||||
@@ -988,9 +1005,9 @@ export enum NotificationType {
|
||||
UNREAD = 'UNREAD'
|
||||
}
|
||||
|
||||
export type Notifications = {
|
||||
export type Notifications = Node & {
|
||||
__typename?: 'Notifications';
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
list: Array<Notification>;
|
||||
/** A cached overview of the notifications in the system & their severity. */
|
||||
overview: NotificationOverview;
|
||||
@@ -1009,7 +1026,7 @@ export type Os = Node & {
|
||||
codepage?: Maybe<Scalars['String']['output']>;
|
||||
distro?: Maybe<Scalars['String']['output']>;
|
||||
hostname?: Maybe<Scalars['String']['output']>;
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
kernel?: Maybe<Scalars['String']['output']>;
|
||||
logofile?: Maybe<Scalars['String']['output']>;
|
||||
platform?: Maybe<Scalars['String']['output']>;
|
||||
@@ -1047,11 +1064,30 @@ export type ParityCheck = {
|
||||
status?: Maybe<Scalars['String']['output']>;
|
||||
};
|
||||
|
||||
/** Parity check related mutations, WIP, response types and functionaliy will change */
|
||||
export type ParityCheckMutations = {
|
||||
__typename?: 'ParityCheckMutations';
|
||||
/** Cancel a parity check */
|
||||
cancel: Scalars['JSON']['output'];
|
||||
/** Pause a parity check */
|
||||
pause: Scalars['JSON']['output'];
|
||||
/** Resume a parity check */
|
||||
resume: Scalars['JSON']['output'];
|
||||
/** Start a parity check */
|
||||
start: Scalars['JSON']['output'];
|
||||
};
|
||||
|
||||
|
||||
/** Parity check related mutations, WIP, response types and functionaliy will change */
|
||||
export type ParityCheckMutationsStartArgs = {
|
||||
correct: Scalars['Boolean']['input'];
|
||||
};
|
||||
|
||||
export type Pci = Node & {
|
||||
__typename?: 'Pci';
|
||||
blacklisted?: Maybe<Scalars['String']['output']>;
|
||||
class?: Maybe<Scalars['String']['output']>;
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
productid?: Maybe<Scalars['String']['output']>;
|
||||
productname?: Maybe<Scalars['String']['output']>;
|
||||
type?: Maybe<Scalars['String']['output']>;
|
||||
@@ -1066,11 +1102,11 @@ export type Permission = {
|
||||
resource: Resource;
|
||||
};
|
||||
|
||||
export type ProfileModel = {
|
||||
export type ProfileModel = Node & {
|
||||
__typename?: 'ProfileModel';
|
||||
avatar: Scalars['String']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
url: Scalars['String']['output'];
|
||||
userId?: Maybe<Scalars['ID']['output']>;
|
||||
username: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
@@ -1088,6 +1124,8 @@ export type Query = {
|
||||
docker: Docker;
|
||||
extraAllowedOrigins: Array<Scalars['String']['output']>;
|
||||
flash: Flash;
|
||||
getDemo: Scalars['String']['output'];
|
||||
health: Scalars['String']['output'];
|
||||
info: Info;
|
||||
logFile: LogFileContent;
|
||||
logFiles: Array<LogFile>;
|
||||
@@ -1105,17 +1143,18 @@ export type Query = {
|
||||
services: Array<Service>;
|
||||
shares: Array<Share>;
|
||||
vars: Vars;
|
||||
/** Get information about all VMs on the system */
|
||||
vms: Vms;
|
||||
};
|
||||
|
||||
|
||||
export type QueryApiKeyArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
id: Scalars['PrefixedID']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type QueryDiskArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
id: Scalars['PrefixedID']['input'];
|
||||
};
|
||||
|
||||
|
||||
@@ -1125,10 +1164,10 @@ export type QueryLogFileArgs = {
|
||||
startLine?: InputMaybe<Scalars['Int']['input']>;
|
||||
};
|
||||
|
||||
export type Registration = {
|
||||
export type Registration = Node & {
|
||||
__typename?: 'Registration';
|
||||
expiration?: Maybe<Scalars['String']['output']>;
|
||||
guid?: Maybe<Scalars['ID']['output']>;
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
keyFile?: Maybe<KeyFile>;
|
||||
state?: Maybe<RegistrationState>;
|
||||
type?: Maybe<RegistrationType>;
|
||||
@@ -1182,7 +1221,7 @@ export type RemoteAccess = {
|
||||
};
|
||||
|
||||
export type RemoveRoleFromApiKeyInput = {
|
||||
apiKeyId: Scalars['ID']['input'];
|
||||
apiKeyId: Scalars['PrefixedID']['input'];
|
||||
role: Role;
|
||||
};
|
||||
|
||||
@@ -1225,10 +1264,11 @@ export enum Role {
|
||||
GUEST = 'GUEST'
|
||||
}
|
||||
|
||||
export type Server = {
|
||||
export type Server = Node & {
|
||||
__typename?: 'Server';
|
||||
apikey: Scalars['String']['output'];
|
||||
guid: Scalars['String']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
lanip: Scalars['String']['output'];
|
||||
localurl: Scalars['String']['output'];
|
||||
name: Scalars['String']['output'];
|
||||
@@ -1246,7 +1286,7 @@ export enum ServerStatus {
|
||||
|
||||
export type Service = Node & {
|
||||
__typename?: 'Service';
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
name?: Maybe<Scalars['String']['output']>;
|
||||
online?: Maybe<Scalars['Boolean']['output']>;
|
||||
uptime?: Maybe<Uptime>;
|
||||
@@ -1280,7 +1320,7 @@ export type Share = Node & {
|
||||
floor?: Maybe<Scalars['String']['output']>;
|
||||
/** (KB) Free space */
|
||||
free?: Maybe<Scalars['Long']['output']>;
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
/** Disks that are included in this share */
|
||||
include?: Maybe<Array<Scalars['String']['output']>>;
|
||||
/** LUKS status */
|
||||
@@ -1318,7 +1358,7 @@ export type SubscriptionLogFileArgs = {
|
||||
|
||||
export type System = Node & {
|
||||
__typename?: 'System';
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
manufacturer?: Maybe<Scalars['String']['output']>;
|
||||
model?: Maybe<Scalars['String']['output']>;
|
||||
serial?: Maybe<Scalars['String']['output']>;
|
||||
@@ -1357,13 +1397,9 @@ export type UnraidArray = Node & {
|
||||
capacity: ArrayCapacity;
|
||||
/** Data disks in the current array */
|
||||
disks: Array<ArrayDisk>;
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
/** Parity disks in the current array */
|
||||
parities: Array<ArrayDisk>;
|
||||
/** Array state after this query/mutation */
|
||||
pendingState?: Maybe<ArrayPendingState>;
|
||||
/** Array state before this query/mutation */
|
||||
previousState?: Maybe<ArrayState>;
|
||||
/** Current array state */
|
||||
state: ArrayState;
|
||||
};
|
||||
@@ -1373,18 +1409,17 @@ export type Uptime = {
|
||||
timestamp?: Maybe<Scalars['String']['output']>;
|
||||
};
|
||||
|
||||
export type Usb = {
|
||||
export type Usb = Node & {
|
||||
__typename?: 'Usb';
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
name?: Maybe<Scalars['String']['output']>;
|
||||
};
|
||||
|
||||
export type UserAccount = {
|
||||
export type UserAccount = Node & {
|
||||
__typename?: 'UserAccount';
|
||||
/** A description of the user */
|
||||
description: Scalars['String']['output'];
|
||||
/** A unique identifier for the user */
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
/** The name of the user */
|
||||
name: Scalars['String']['output'];
|
||||
/** The permissions of the user */
|
||||
@@ -1427,7 +1462,7 @@ export type Vars = Node & {
|
||||
fuseRememberDefault?: Maybe<Scalars['String']['output']>;
|
||||
fuseRememberStatus?: Maybe<Scalars['String']['output']>;
|
||||
hideDotFiles?: Maybe<Scalars['Boolean']['output']>;
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
joinStatus?: Maybe<Scalars['String']['output']>;
|
||||
localMaster?: Maybe<Scalars['Boolean']['output']>;
|
||||
localTld?: Maybe<Scalars['String']['output']>;
|
||||
@@ -1565,7 +1600,7 @@ export type Versions = Node & {
|
||||
git?: Maybe<Scalars['String']['output']>;
|
||||
grunt?: Maybe<Scalars['String']['output']>;
|
||||
gulp?: Maybe<Scalars['String']['output']>;
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
kernel?: Maybe<Scalars['String']['output']>;
|
||||
mongodb?: Maybe<Scalars['String']['output']>;
|
||||
mysql?: Maybe<Scalars['String']['output']>;
|
||||
@@ -1588,13 +1623,72 @@ export type Versions = Node & {
|
||||
yarn?: Maybe<Scalars['String']['output']>;
|
||||
};
|
||||
|
||||
export type VmDomain = {
|
||||
export type VmDomain = Node & {
|
||||
__typename?: 'VmDomain';
|
||||
/** The unique identifier for the vm (uuid) */
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
/** A friendly name for the vm */
|
||||
name?: Maybe<Scalars['String']['output']>;
|
||||
/** Current domain vm state */
|
||||
state: VmState;
|
||||
uuid: Scalars['ID']['output'];
|
||||
/**
|
||||
* The UUID of the vm
|
||||
* @deprecated Use id instead
|
||||
*/
|
||||
uuid?: Maybe<Scalars['String']['output']>;
|
||||
};
|
||||
|
||||
export type VmMutations = {
|
||||
__typename?: 'VmMutations';
|
||||
/** Force stop a virtual machine */
|
||||
forceStop: Scalars['Boolean']['output'];
|
||||
/** Pause a virtual machine */
|
||||
pause: Scalars['Boolean']['output'];
|
||||
/** Reboot a virtual machine */
|
||||
reboot: Scalars['Boolean']['output'];
|
||||
/** Reset a virtual machine */
|
||||
reset: Scalars['Boolean']['output'];
|
||||
/** Resume a virtual machine */
|
||||
resume: Scalars['Boolean']['output'];
|
||||
/** Start a virtual machine */
|
||||
start: Scalars['Boolean']['output'];
|
||||
/** Stop a virtual machine */
|
||||
stop: Scalars['Boolean']['output'];
|
||||
};
|
||||
|
||||
|
||||
export type VmMutationsForceStopArgs = {
|
||||
id: Scalars['PrefixedID']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type VmMutationsPauseArgs = {
|
||||
id: Scalars['PrefixedID']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type VmMutationsRebootArgs = {
|
||||
id: Scalars['PrefixedID']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type VmMutationsResetArgs = {
|
||||
id: Scalars['PrefixedID']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type VmMutationsResumeArgs = {
|
||||
id: Scalars['PrefixedID']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type VmMutationsStartArgs = {
|
||||
id: Scalars['PrefixedID']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type VmMutationsStopArgs = {
|
||||
id: Scalars['PrefixedID']['input'];
|
||||
};
|
||||
|
||||
/** The state of a virtual machine */
|
||||
@@ -1609,10 +1703,11 @@ export enum VmState {
|
||||
SHUTOFF = 'SHUTOFF'
|
||||
}
|
||||
|
||||
export type Vms = {
|
||||
export type Vms = Node & {
|
||||
__typename?: 'Vms';
|
||||
domain?: Maybe<Array<VmDomain>>;
|
||||
domains?: Maybe<Array<VmDomain>>;
|
||||
id: Scalars['ID']['output'];
|
||||
id: Scalars['PrefixedID']['output'];
|
||||
};
|
||||
|
||||
export enum WanAccessType {
|
||||
@@ -1685,7 +1780,7 @@ export type NotificationsQuery = { __typename?: 'Query', notifications: { __type
|
||||
)> } };
|
||||
|
||||
export type ArchiveNotificationMutationVariables = Exact<{
|
||||
id: Scalars['String']['input'];
|
||||
id: Scalars['PrefixedID']['input'];
|
||||
}>;
|
||||
|
||||
|
||||
@@ -1700,7 +1795,7 @@ export type ArchiveAllNotificationsMutationVariables = Exact<{ [key: string]: ne
|
||||
export type ArchiveAllNotificationsMutation = { __typename?: 'Mutation', archiveAll: { __typename?: 'NotificationOverview', unread: { __typename?: 'NotificationCounts', total: number }, archive: { __typename?: 'NotificationCounts', info: number, warning: number, alert: number, total: number } } };
|
||||
|
||||
export type DeleteNotificationMutationVariables = Exact<{
|
||||
id: Scalars['String']['input'];
|
||||
id: Scalars['PrefixedID']['input'];
|
||||
type: NotificationType;
|
||||
}>;
|
||||
|
||||
@@ -1802,9 +1897,9 @@ export const LogFilesDocument = {"kind":"Document","definitions":[{"kind":"Opera
|
||||
export const LogFileContentDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"LogFileContent"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"path"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"lines"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"startLine"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"logFile"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}},{"kind":"Argument","name":{"kind":"Name","value":"lines"},"value":{"kind":"Variable","name":{"kind":"Name","value":"lines"}}},{"kind":"Argument","name":{"kind":"Name","value":"startLine"},"value":{"kind":"Variable","name":{"kind":"Name","value":"startLine"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"content"}},{"kind":"Field","name":{"kind":"Name","value":"totalLines"}},{"kind":"Field","name":{"kind":"Name","value":"startLine"}}]}}]}}]} as unknown as DocumentNode<LogFileContentQuery, LogFileContentQueryVariables>;
|
||||
export const LogFileSubscriptionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"subscription","name":{"kind":"Name","value":"LogFileSubscription"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"path"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"logFile"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"content"}},{"kind":"Field","name":{"kind":"Name","value":"totalLines"}}]}}]}}]} as unknown as DocumentNode<LogFileSubscriptionSubscription, LogFileSubscriptionSubscriptionVariables>;
|
||||
export const NotificationsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Notifications"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"NotificationFilter"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"notifications"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"list"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"NotificationFragment"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"NotificationFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Notification"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"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":"type"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"formattedTimestamp"}}]}}]} as unknown as DocumentNode<NotificationsQuery, NotificationsQueryVariables>;
|
||||
export const ArchiveNotificationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ArchiveNotification"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archiveNotification"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"NotificationFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"NotificationFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Notification"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"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":"type"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"formattedTimestamp"}}]}}]} as unknown as DocumentNode<ArchiveNotificationMutation, ArchiveNotificationMutationVariables>;
|
||||
export const ArchiveNotificationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ArchiveNotification"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PrefixedID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archiveNotification"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"NotificationFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"NotificationFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Notification"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"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":"type"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"formattedTimestamp"}}]}}]} as unknown as DocumentNode<ArchiveNotificationMutation, ArchiveNotificationMutationVariables>;
|
||||
export const ArchiveAllNotificationsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ArchiveAllNotifications"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archiveAll"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"unread"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}}]}},{"kind":"Field","name":{"kind":"Name","value":"archive"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"info"}},{"kind":"Field","name":{"kind":"Name","value":"warning"}},{"kind":"Field","name":{"kind":"Name","value":"alert"}},{"kind":"Field","name":{"kind":"Name","value":"total"}}]}}]}}]}}]} as unknown as DocumentNode<ArchiveAllNotificationsMutation, ArchiveAllNotificationsMutationVariables>;
|
||||
export const DeleteNotificationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteNotification"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"type"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"NotificationType"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteNotification"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"type"},"value":{"kind":"Variable","name":{"kind":"Name","value":"type"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archive"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}}]}}]}}]}}]} as unknown as DocumentNode<DeleteNotificationMutation, DeleteNotificationMutationVariables>;
|
||||
export const DeleteNotificationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteNotification"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PrefixedID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"type"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"NotificationType"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteNotification"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"type"},"value":{"kind":"Variable","name":{"kind":"Name","value":"type"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archive"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}}]}}]}}]}}]} as unknown as DocumentNode<DeleteNotificationMutation, DeleteNotificationMutationVariables>;
|
||||
export const DeleteAllNotificationsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteAllNotifications"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteArchivedNotifications"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archive"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}}]}},{"kind":"Field","name":{"kind":"Name","value":"unread"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}}]}}]}}]}}]} as unknown as DocumentNode<DeleteAllNotificationsMutation, DeleteAllNotificationsMutationVariables>;
|
||||
export const OverviewDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Overview"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"notifications"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"overview"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"unread"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"info"}},{"kind":"Field","name":{"kind":"Name","value":"warning"}},{"kind":"Field","name":{"kind":"Name","value":"alert"}},{"kind":"Field","name":{"kind":"Name","value":"total"}}]}},{"kind":"Field","name":{"kind":"Name","value":"archive"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}}]}}]}}]}}]}}]} as unknown as DocumentNode<OverviewQuery, OverviewQueryVariables>;
|
||||
export const RecomputeOverviewDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RecomputeOverview"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"recalculateOverview"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archive"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"NotificationCountFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"unread"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"NotificationCountFragment"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"NotificationCountFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"NotificationCounts"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"info"}},{"kind":"Field","name":{"kind":"Name","value":"warning"}},{"kind":"Field","name":{"kind":"Name","value":"alert"}}]}}]} as unknown as DocumentNode<RecomputeOverviewMutation, RecomputeOverviewMutationVariables>;
|
||||
|
||||
Reference in New Issue
Block a user