refactor: cleanedup core importing and added better worker crashing

This commit is contained in:
Alexis Tyler
2020-05-31 12:11:39 +09:30
parent 07e3cd9020
commit 3dc9bd2db7
44 changed files with 332 additions and 704 deletions
+2
View File
@@ -0,0 +1,2 @@
export * from './resolvers';
export * from './type-defs';
+186
View File
@@ -0,0 +1,186 @@
/*!
* Copyright 2019-2020 Lime Technology Inc. All rights reserved.
* Written by: Alexis Tyler
*/
import { pluginManager, pubsub, utils, bus, errors, states, modules } from '@unraid/core';
import dee from '@gridplus/docker-events';
import { setIntervalAsync } from 'set-interval-async/dynamic';
import GraphQLJSON from 'graphql-type-json';
import GraphQLLong from 'graphql-type-long';
import GraphQLUUID from 'graphql-type-uuid';
import { run, publish } from '../../run';
import { cache } from '../../cache';
import { hasSubscribedToChannel, canPublishToChannel } from '../../ws';
const { ensurePermission } = utils;
const { usersState } = states;
const { AppError, PluginError } = errors;
// Update array values when slots change
bus.on('slots', async () => {
// @todo: Create a system user for this
const user = usersState.findOne({ name: 'root' });
await run('array', 'UPDATED', {
moduleToRun: modules.getArray,
context: {
user
}
});
});
// On Docker event update info with { apps: { installed, started } }
dee.on('*', async (data) => {
// Only listen to container events
if (data.Type !== 'container') {
return;
}
// Don't publish when we have no clients
if (!canPublishToChannel('info')) {
return;
}
const { json } = await modules.getApps();
publish('info', 'UPDATED', json);
});
dee.listen();
// This needs to be fixed to run from events
setIntervalAsync(async () => {
if (!canPublishToChannel('services')) {
return;
}
// @todo: Create a system user for this
const user = usersState.findOne({ name: 'root' });
await run('services', 'UPDATED', {
moduleToRun: modules.getServices,
context: {
user
}
});
}, 1000);
/**
* Create a pubsub subscription.
* @param channel The pubsub channel to subscribe to.
* @param resource The access-control permission resource to check against.
*/
const createSubscription = (channel, resource?) => ({
subscribe(_, __, context) {
if (!context.user) {
throw new AppError('<ws> No user found in context.', 500);
}
// Check the user has permissison to subscribe to this endpoint
ensurePermission(context.user, {
resource: resource || channel,
action: 'read',
possession: 'any'
});
hasSubscribedToChannel(context.websocketId, channel);
return pubsub.asyncIterator(channel);
}
});
export const resolvers = {
Query: {
info: () => ({}),
vms: () => ({}),
server(name: string) {
// Single server
// return cache.get();
},
servers() {
// All servers
return cache.data;
}
},
Subscription: {
apikeys: {
// Not sure how we're going to secure this
// ...createSubscription('apikeys')
},
array: {
...createSubscription('array')
},
// devices: {
// ...createSubscription('device')
// },
dockerContainers: {
...createSubscription('docker/container')
},
dockerNetworks: {
...createSubscription('docker/network')
},
info: {
...createSubscription('info')
},
ping: {
// subscribe: (_, __, context) => {
// // startPing();
// hasSubscribedToChannel(context.websocketId, 'ping');
// return pubsub.asyncIterator('ping');
// }
},
services: {
...createSubscription('services')
},
servers: {
...createSubscription('servers')
},
shares: {
...createSubscription('shares')
},
unassignedDevices: {
...createSubscription('devices/unassigned')
},
users: {
...createSubscription('users')
},
vars: {
...createSubscription('vars')
},
vms: {
...createSubscription('vms/domains')
},
pluginModule: {
subscribe: async (_, directiveArgs, context) => {
const {plugin: pluginName, module: pluginModuleName} = directiveArgs;
const channel = `${pluginName}/${pluginModuleName}`;
// Verify plugin is installed and active
if (!pluginManager.isInstalled(pluginName, pluginModuleName)) {
throw new PluginError('Plugin not installed.', 500);
}
if (!pluginManager.isActive(pluginName, pluginModuleName)) {
throw new PluginError('Plugin disabled.', 500);
}
// It's up to the plugin to publish new data as needed
// so we'll just return the Iterator
hasSubscribedToChannel(context.websocketId, channel);
return pubsub.asyncIterator(channel);
}
}
},
JSON: GraphQLJSON,
Long: GraphQLLong,
UUID: GraphQLUUID,
UserAccount: {
__resolveType(obj) {
// Only a user has a password field, the current user aka "me" doesn't.
if (obj.password) {
return 'User';
}
return 'Me';
}
}
};
+13
View File
@@ -0,0 +1,13 @@
/*!
* Copyright 2019-2020 Lime Technology Inc. All rights reserved.
* Written by: Alexis Tyler
*/
import { join } from 'path';
import { fileLoader, mergeTypes } from 'merge-graphql-schemas';
const files = fileLoader(join(__dirname, './types/**/*.graphql'));
export const typeDefs = mergeTypes(files, {
all: true
});
@@ -0,0 +1,47 @@
input authenticateInput {
password: String!
}
input addApiKeyInput {
name: String
key: String
userId: String
}
input updateApikeyInput {
description: String
expiresAt: Long!
}
type Query {
"""Get all API keys"""
apiKeys: [ApiKey] @func(module: "getApikeys")
}
type Mutation {
"""Get an existing API key"""
getApiKey(name: String!, input: authenticateInput): ApiKey @func(module: "getApikey")
"""Create a new API key"""
addApikey(name: String!, input: updateApikeyInput): ApiKey @func(module: "addApikey")
"""Update an existing API key"""
updateApikey(name: String!, input: updateApikeyInput): ApiKey @func(module: "updateApikey")
}
type ApikeysSubscription {
mutation: MutationType!
node: ApiKey
}
type Subscription {
apikeys: ApikeysSubscription
}
type ApiKey {
name: String!
key: String!
description: String
scopes: JSON!
expiresAt: Long!
}
@@ -0,0 +1,146 @@
type Query {
"""An Unraid array consisting of 1 or 2 Parity disks and a number of Data disks."""
array: Array @func(module: "getArray")
}
type Mutation {
"""Start array"""
startArray: Array @func(module: "updateArray", data: { state: "start" })
"""Stop array"""
stopArray: Array @func(module: "updateArray", data: { state: "stop" })
"""Add new disk to array"""
addDiskToArray(input: arrayDiskInput): Array @func(module: "addDiskToArray")
"""Remove existing disk from array. NOTE: The array must be stopped before running this otherwise it'll throw an error."""
removeDiskFromArray(input: arrayDiskInput): Array @func(module: "removeDiskFromArray")
mountArrayDisk(id: ID!): Disk
unmountArrayDisk(id: ID!): Disk
clearArrayDiskStatistics(id: ID!): JSON
}
type ArraySubscription {
mutation: UpdateOnlyMutationType!
node: Array!
}
type Subscription {
array: ArraySubscription
}
input arrayDiskInput {
"""Disk ID"""
id: ID!
"""The slot for the disk"""
slot: Int
}
type Array {
"""Array state before this query/mutation"""
previousState: ArrayState
"""Array state after this query/mutation"""
pendingState: ArrayPendingState
"""Current array state"""
state: ArrayState!
"""Current array capacity"""
capacity: ArrayCapacity!
"""Current boot disk"""
boot: ArrayDataDisk
"""Parity disks in the current array"""
parities: [ArrayDataDisk]
"""Data disks in the current array"""
disks: [ArrayDataDisk]
"""Caches in the current array"""
caches: [ArrayDataDisk]
}
enum ArrayState {
"""Array is running"""
started
"""Array has stopped"""
stopped
}
enum ArrayPendingState {
"""Array is starting"""
starting
"""Array is stopping"""
stopping
}
type ArrayCapacity {
bytes: Capacity
disks: Capacity
}
type Capacity {
free: Long
used: Long
total: Long
}
type ArrayDataDisk {
"""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."""
slot: Long!
name: String!
device: String!
id: ID!
size: Long!
status: ArrayDiskStatus!
rotational: Boolean!
format: String!
temp: Int!
"""Count of I/O read requests sent to the device I/O drivers. These statistics may be cleared at any time."""
numReads: Int!
"""Count of I/O writes requests sent to the device I/O drivers. These statistics may be cleared at any time."""
numWrites: Int!
"""Number of unrecoverable errors reported by the device I/O drivers. Missing data due to unrecoverable array read errors is filled in on-the-fly using parity reconstruct (and we attempt to write this data back to the sector(s) which failed). Any unrecoverable write error results in disabling the disk."""
numErrors: Int!
type: ArrayDiskType!
color: String!
fsStatus: String
luksState: String
comment: String
"""Indicates if the disk should be exported as a network share."""
exportable: Boolean!
"""Indicates the file system detected in partition 1 of the device."""
fsType: DiskFsType
fsColor: ArrayDiskFsColor
fsSize: Long
fsFree: Long
spindownDelay: String
spinupGroup: String
deviceSb: String
idSb: String
sizeSb: Long
}
# type ArrayParityDisk {}
# type ArrayCacheDisk {}
enum ArrayDiskStatus {
DISK_OK
}
enum ArrayDiskType {
"""Data disk"""
Data
"""Parity disk"""
Parity
"""Flash disk"""
Flash
"""Cache disk"""
Cache
}
enum ArrayDiskFsColor {
"""Disk is OK and running"""
green_on
"""Disk is OK and not running"""
green_off
yellow_on
yellow_off
red_on
red_off
}
@@ -0,0 +1,31 @@
type Query {
parityHistory: [ParityCheck] @func(module: "getParityHistory")
}
type Mutation {
"""Start parity check"""
startParityCheck(correct: Boolean): JSON @func(module: "updateParityCheck", data: { state: "start" })
"""Pause parity check"""
pauseParityCheck: JSON @func(module: "updateParityCheck", data: { state: "pause" })
"""Resume parity check"""
resumeParityCheck: JSON @func(module: "updateParityCheck", data: { state: "resume" })
"""Cancel parity check"""
cancelParityCheck: JSON @func(module: "updateParityCheck", data: { state: "cancel" })
}
type ParityHistorySubscription {
mutation: MutationType!
node: ParityCheck!
}
type Subscription {
parityHistory: ParityHistorySubscription
}
type ParityCheck {
date: String!
duration: Int!
speed: String!
status: String!
errors: String!
}
@@ -0,0 +1,23 @@
type Query {
# @todo fix this
device(id: ID!): Device @func(module: "devices/device/get-device")
devices: [Device]! @func(module: "getDevices")
}
type Device {
id: ID!
tag: String
device: String
sectors: String
sectorSize: String
}
type DevicesSubscription {
mutation: MutationType!
node: [Device!]
}
type Subscription {
devices: DevicesSubscription!
device(id: ID!): DevicesSubscription!
}
@@ -0,0 +1,64 @@
type Query {
"""Single disk"""
disk(id: ID!): Disk @func(module: "getDisk")
"""Mulitiple disks"""
disks: [Disk]! @func(module: "getDisks")
}
type Disk {
# /dev/sdb
device: String!
# SSD
type: String!
# Samsung_SSD_860_QVO_1TB
name: String!
# Samsung
vendor: String!
# 1000204886016
size: Long!
# -1
bytesPerSector: Long!
# -1
totalCylinders: Long!
# -1
totalHeads: Long!
# -1
totalSectors: Long!
# -1
totalTracks: Long!
# -1
tracksPerCylinder: Long!
# -1
sectorsPerTrack: Long!
# 1B6Q
firmwareRevision: String!
# S4CZNF0M807232N
serialNum: String!
interfaceType: DiskInterfaceType!
smartStatus: DiskSmartStatus!
temperature: Long!
partitions: [DiskPartition!]
}
type DiskPartition {
name: String!
fsType: DiskFsType!
size: Long!
}
enum DiskFsType {
xfs
btrfs
vfat
}
enum DiskInterfaceType {
SAS
SATA
USB
UNKNOWN
}
enum DiskSmartStatus {
Ok
Unknown
}
@@ -0,0 +1,70 @@
type Query {
"""Docker container"""
dockerContainer(id: ID!): DockerContainer! @func(module: "getDockerContainer")
"""All Docker containers"""
dockerContainers(all: Boolean): [DockerContainer]! @func(module: "getDockerContainers")
}
type DockerContainerSubscription {
mutation: MutationType!
node: DockerContainer!
}
type DockerContainersSubscription {
mutation: MutationType!
node: [DockerContainer]!
}
type Subscription {
dockerContainer(id: ID!): DockerContainerSubscription
dockerContainers: DockerContainersSubscription
}
enum ContainerPortType {
tcp
udp
}
type ContainerPort {
ip: String!
privatePort: Int
publicPort: Int
type: ContainerPortType
}
enum ContainerState {
running
exited
}
type ContainerHostConfig {
networkMode: String!
}
type ContainerMount {
type: String!
name: String!
source: String!
destination: String!
driver: String!
mode: String!
rw: Boolean!
propagation: String!
}
type DockerContainer {
id: ID!
names: [String!]
image: String!
imageId: String!
command: String!
created: Int!
ports: [ContainerPort]
sizeRootFs: Int!
labels: JSON
state: ContainerState
status: String!
hostConfig: ContainerHostConfig
networkSettings: JSON
mounts: [JSON]
autoStart: Boolean!
}
@@ -0,0 +1,38 @@
type Query {
"""Docker network"""
dockerNetwork(id: ID!): DockerNetwork! @func(module: "getDockerNetwork")
"""All Docker networks"""
dockerNetworks(all: Boolean): [DockerNetwork]! @func(module: "getDockerNetworks")
}
type DockerNetworkSubscription {
mutation: MutationType!
node: DockerNetwork!
}
type DockerNetworksSubscription {
mutation: MutationType!
node: [DockerNetwork]!
}
type Subscription {
dockerNetwork(id: ID!): DockerNetworkSubscription
dockerNetworks: DockerNetworksSubscription
}
type DockerNetwork {
name: String
id: ID
created: String
scope: String
driver: String
enableIPv6: Boolean!
ipam: JSON
internal: Boolean!
attachable: Boolean!
ingress: Boolean!
configFrom: JSON
configOnly: Boolean!
containers: JSON
options: JSON
labels: JSON
}
@@ -0,0 +1,11 @@
type Info {
"""Count of docker containers"""
apps: InfoApps @func(module: "getAppCount")
}
type InfoApps {
"""How many docker containers are installed"""
installed: Int
"""How many docker containers are running"""
started: Int
}
@@ -0,0 +1,14 @@
type Info {
baseboard: Baseboard @func(module: "getBaseboard")
}
type Baseboard {
# Dell Inc.
manufacturer: String!
# 0MD99X
model: String
# A07
version: String
serial: String
assetTag: String
}
+39
View File
@@ -0,0 +1,39 @@
type Info {
cpu: InfoCpu @func(module: "getCpu")
}
type InfoCpu {
# 'Intel®'
manufacturer: String!
# 'Xeon® L5640'
brand: String!
# 'GenuineIntel'
vendor: String!
# '6'
family: String!
# '44'
model: String!
# '2'
stepping: Int!
# ''
revision: String!
# ''
voltage: String
# '2.27'
speed: Float!
# '1.60'
speedmin: Float!
# '2.26'
speedmax: Float!
# 12
threads: Int!
# 6
cores: Int!
# 1
processors: Long!
# 'LGA1366'
socket: String!
# { l1d: 196608, l1i: 196608, l2: 1, l3: 12 }
cache: JSON!
flags: [String!]
}
@@ -0,0 +1,53 @@
type Info {
# @todo finish this
devices: Devices @func(module: "info/get-devices")
}
type Devices {
gpu: [Gpu]
network: [Network]
pci: [Pci]
usb: [Usb]
}
type Gpu {
id: ID!
type: String!
typeid: String!
vendorname: String!
productid: String!
blacklisted: Boolean!
class: String!
}
type Network {
iface: String
ifaceName: String
ipv4: String
ipv6: String
mac: String
internal: String
operstate: String
type: String
duplex: String
mtu: String
speed: String
carrierChanges: String
}
type Pci {
id: ID!
type: String
typeid: String
vendorname: String
vendorid: String
productname: String
productid: String
blacklisted: String
class: String
}
type Usb {
id: ID!
name: String
}
@@ -0,0 +1,33 @@
type Info {
display: Display @func(module: "getDisplay")
}
type Display {
date: String
number: String
scale: Boolean
tabs: Boolean
users: String
resize: Boolean
wwn: Boolean
total: Boolean
usage: Boolean
banner: String
dashapps: String
theme: Theme
text: Boolean
unit: Temperature
warning: Int
critical: Int
hot: Int
max: Int
}
enum Temperature {
C
F
}
enum Theme {
white
}
@@ -0,0 +1,4 @@
type Info {
"""Machine ID"""
machineId: ID @func(module: "getMachineId")
}
@@ -0,0 +1,41 @@
type Info {
memory: InfoMemory @func(module: "getMemory")
}
type InfoMemory {
max: Long!
total: Long!
free: Long!
used: Long!
active: Long!
available: Long!
buffcache: Long!
swaptotal: Long!
swapused: Long!
swapfree: Long!
layout: [MemoryLayout!]
}
type MemoryLayout {
size: Long!
bank: String
type: MemoryType
clockSpeed: Long
formFactor: MemoryFormFactor
manufacturer: String
partNum: String
serialNum: String
voltageConfigured: Long
voltageMin: Long
voltageMax: Long
}
enum MemoryType {
DDR2
DDR3
DDR4
}
enum MemoryFormFactor {
DIMM
}
+17
View File
@@ -0,0 +1,17 @@
type Info {
os: Os @func(module: "getOs")
}
type Os {
platform: String
distro: String
release: String
codename: String
kernel: String
arch: String
hostname: String
codepage: String
logofile: String
serial: String
build: String
}
@@ -0,0 +1,12 @@
type Info {
system: System @func(module: "getSystem")
}
type System {
manufacturer: String
model: String
version: String
serial: String
uuid: String
sku: String
}
@@ -0,0 +1,32 @@
type Info {
versions: Versions @func(module: "getVersions")
}
type Versions {
kernel: String
openssl: String
systemOpenssl: String
systemOpensslLib: String
node: String
v8: String
npm: String
yarn: String
pm2: String
gulp: String
grunt: String
git: String
tsc: String
mysql: String
redis: String
mongodb: String
apache: String
nginx: String
php: String
docker: String
postfix: String
postgresql: String
perl: String
python: String
gcc: String
unraid: String
}
@@ -0,0 +1,28 @@
type Query {
"""Node plugins"""
plugins: [Plugin] @func(module: "getPlugins")
}
type Mutation {
"""Install plugin via npm"""
addPlugin(name: String!, version: String): JSON @func(module: "addPlugin")
"""Update plugin installed via npm"""
updatePlugin(name: String!, version: String): JSON
"""Uninstall plugin"""
removePlugin(name: String!): JSON
}
type Plugin {
name: String
isActive: Boolean
disabled: Boolean!
modules: [PluginModule]
}
type PluginModule {
name: String!
filePath: String
isActive: Boolean!
}
@@ -0,0 +1,38 @@
type Permissions {
scopes: JSON
grants: JSON
}
type Query {
permissions: Permissions @func(module: "getPermissions")
}
input addScopeInput {
"""Scope name"""
name: String!
"""Scope description"""
description: String
}
input addScopeToApiKeyInput {
"""Scope name"""
name: String!
apiKey: String!
}
type Mutation {
# @todo finish adding this to core
"""Add a new permission scope"""
addScope(input: addScopeInput!): Scope @func(module: "add-scope")
# @todo finish adding this to core
"""Add a new permission scope to apiKey"""
addScopeToApiKey(input: addScopeToApiKeyInput!): Scope @func(module: "apikeys/name/add-scope")
}
"""A permission scope"""
type Scope {
"""A unique name for the scope"""
name: String
"""A user friendly description"""
description: String
}
@@ -0,0 +1,28 @@
type Query {
server(name: String!): Server @func(module: "servers/name/get-server")
servers: [Server] @func(module: "getServers")
}
type ServerSubscription {
mutation: UpdateOnlyMutationType!
node: Server!
}
type ServersSubscription {
mutation: UpdateOnlyMutationType!
node: [Server!]
}
type Subscription {
server(name: String!): ServerSubscription
servers: ServersSubscription
}
# type Status = 'Online' | 'Offline';
type Server {
name: String!
status: String!
wanIp: String!
localUrl: String!
remoteUrl: String!
}
@@ -0,0 +1,26 @@
type Query {
# @todo finish this
service(name: String!): Service @func(module: "services/name/get-service")
services: [Service] @func(module: "getServices")
}
type ServiceSubscription {
mutation: UpdateOnlyMutationType!
node: Service!
}
type ServicesSubscription {
mutation: UpdateOnlyMutationType!
node: [Service!]
}
type Subscription {
service(name: String!): ServiceSubscription
services: ServicesSubscription
}
type Service {
name: String!
online: Boolean
uptime: Int
version: String
}
@@ -0,0 +1,42 @@
type Query {
"""Network Shares"""
shares: [Share] @func(module: "getShares")
}
type ShareSubscription {
mutation: MutationType!
node: Share!
}
type SharesSubscription {
mutation: MutationType!
node: [Share!]
}
type Subscription {
share(id: ID!): ShareSubscription
shares: SharesSubscription
}
"""Network Share"""
type Share {
"""Display name"""
name: String
"""Free space in bytes"""
free: Int
"""Total size in bytes"""
size: Int
"""Disks that're included in this share"""
include: [String]
"""Disks that're excluded from this share"""
exclude: [String]
cache: Boolean
nameOrig: String
"""User comment"""
comment: String
allocator: String
splitLevel: String
floor: String
cow: String
color: String
luksStatus: String
}
@@ -0,0 +1,6 @@
type Mount {
name: String
directory: String
type: String
permissions: String
}
@@ -0,0 +1,60 @@
type Partition {
devlinks: String
devname: String
devpath: String
devtype: String
idAta: String
idAtaDownloadMicrocode: String
idAtaFeatureSetAam: String
idAtaFeatureSetAamCurrentValue: String
idAtaFeatureSetAamEnabled: String
idAtaFeatureSetAamVendorRecommendedValue: String
idAtaFeatureSetApm: String
idAtaFeatureSetApmCurrentValue: String
idAtaFeatureSetApmEnabled: String
idAtaFeatureSetHpa: String
idAtaFeatureSetHpaEnabled: String
idAtaFeatureSetPm: String
idAtaFeatureSetPmEnabled: String
idAtaFeatureSetPuis: String
idAtaFeatureSetPuisEnabled: String
idAtaFeatureSetSecurity: String
idAtaFeatureSetSecurityEnabled: String
idAtaFeatureSetSecurityEnhancedEraseUnitMin: String
idAtaFeatureSetSecurityEraseUnitMin: String
idAtaFeatureSetSmart: String
idAtaFeatureSetSmartEnabled: String
idAtaRotationRateRpm: String
idAtaSata: String
idAtaSataSignalRateGen1: String
idAtaSataSignalRateGen2: String
idAtaWriteCache: String
idAtaWriteCacheEnabled: String
idBus: String
idFsType: String
idFsUsage: String
idFsUuid: String
idFsUuidEnc: String
idModel: String
idModelEnc: String
idPartEntryDisk: String
idPartEntryNumber: String
idPartEntryOffset: String
idPartEntryScheme: String
idPartEntrySize: String
idPartEntryType: String
idPartTableType: String
idPath: String
idPathTag: String
idRevision: String
idSerial: String
idSerialShort: String
idType: String
idWwn: String
idWwnWithExtension: String
major: String
minor: String
partn: String
subsystem: String
usecInitialized: String
}
@@ -0,0 +1,67 @@
type Query {
unassignedDevices: [UnassignedDevice] @func(module: "getUnassignedDevices")
}
type UnassignedDevicesSubscription {
mutation: MutationType!
node: [UnassignedDevice!]
}
type Subscription {
unassignedDevices: UnassignedDevicesSubscription
}
type UnassignedDevice {
devlinks: String
devname: String
devpath: String
devtype: String
idAta: String
idAtaDownloadMicrocode: String
idAtaFeatureSetAam: String
idAtaFeatureSetAamCurrentValue: String
idAtaFeatureSetAamEnabled: String
idAtaFeatureSetAamVendorRecommendedValue: String
idAtaFeatureSetApm: String
idAtaFeatureSetApmCurrentValue: String
idAtaFeatureSetApmEnabled: String
idAtaFeatureSetHpa: String
idAtaFeatureSetHpaEnabled: String
idAtaFeatureSetPm: String
idAtaFeatureSetPmEnabled: String
idAtaFeatureSetPuis: String
idAtaFeatureSetPuisEnabled: String
idAtaFeatureSetSecurity: String
idAtaFeatureSetSecurityEnabled: String
idAtaFeatureSetSecurityEnhancedEraseUnitMin: String
idAtaFeatureSetSecurityEraseUnitMin: String
idAtaFeatureSetSmart: String
idAtaFeatureSetSmartEnabled: String
idAtaRotationRateRpm: String
idAtaSata: String
idAtaSataSignalRateGen1: String
idAtaSataSignalRateGen2: String
idAtaWriteCache: String
idAtaWriteCacheEnabled: String
idBus: String
idModel: String
idModelEnc: String
idPartTableType: String
idPath: String
idPathTag: String
idRevision: String
idSerial: String
idSerialShort: String
idType: String
idWwn: String
idWwnWithExtension: String
major: String
minor: String
subsystem: String
usecInitialized: String
partitions: [Partition]
temp: Int
name: String
mounted: Boolean
mount: Mount
}
+25
View File
@@ -0,0 +1,25 @@
type Query {
"""Current user account"""
me: Me @func(module: "getMe")
}
# type Mutation {
# }
"""The current user"""
type Me implements UserAccount {
id: ID!
name: String!
description: String!
role: String!
permissions: JSON
}
type MeSubscription {
mutation: UpdateOnlyMutationType!
node: Me
}
type Subscription {
me: MeSubscription
}
@@ -0,0 +1,59 @@
interface UserAccount {
id: ID!
name: String!
description: String!
role: String!
}
input usersInput {
slim: Boolean
}
type Query {
"""User account"""
user(id: ID!): User @func(module: "getUser")
"""User accounts"""
users(input: usersInput): [User!]! @func(module: "getUsers", query: { slim: false })
}
input addUserInput {
name: String!
password: String!
description: String
}
input deleteUserInput {
name: String!
}
type Mutation {
"""Add a new user"""
addUser(input: addUserInput!): User @func(module: "addUser")
"""Delete a user"""
deleteUser(input: deleteUserInput!): User @func(module: "deleteUser")
}
type UserSubscription {
mutation: MutationType!
node: User!
}
type UsersSubscription {
mutation: MutationType!
node: [User]!
}
type Subscription {
user(id: ID!): UserSubscription
users: UsersSubscription
}
"""A local user account"""
type User implements UserAccount {
id: ID!
"""A unique name for the user"""
name: String!
description: String!
role: String!
"""If the account has a password set"""
password: Boolean
}
+185
View File
@@ -0,0 +1,185 @@
type Query {
vars: Vars @func(module: "getVars")
}
type VarsSubscription {
mutation: UpdateOnlyMutationType!
node: Vars!
}
type Subscription {
vars(id: ID!): VarsSubscription
}
type Vars {
"""Unraid version"""
version: String
maxArraysz: Int
maxCachesz: Int
"""Machine hostname"""
name: String
timeZone: String
comment: String
security: String
workgroup: String
domain: String
domainShort: String
hideDotFiles: Boolean
localMaster: Boolean
enableFruit: String
"""Should a NTP server be used for time sync?"""
useNtp: Boolean
"""NTP Server 1"""
ntpServer1: String
"""NTP Server 2"""
ntpServer2: String
"""NTP Server 3"""
ntpServer3: String
"""NTP Server 4"""
ntpServer4: String
domainLogin: String
sysModel: String
sysArraySlots: Int
sysCacheSlots: Int
sysFlashSlots: Int
useSsl: Boolean
"""Port for the webui via HTTP"""
port: Int
"""Port for the webui via HTTPS"""
portssl: Int
localTld: String
bindMgt: Boolean
"""Should telnet be enabled?"""
useTelnet: Boolean
porttelnet: Int
useSsh: Boolean
portssh: Int
startPage: String
startArray: Boolean
spindownDelay: String
queueDepth: String
spinupGroups: Boolean
defaultFormat: String
defaultFsType: String
shutdownTimeout: Int
luksKeyfile: String
pollAttributes: String
pollAttributesDefault: String
pollAttributesStatus: String
nrRequests: Int
nrRequestsDefault: Int
nrRequestsStatus: String
mdNumStripes: Int
mdNumStripesDefault: Int
mdNumStripesStatus: String
mdSyncWindow: Int
mdSyncWindowDefault: Int
mdSyncWindowStatus: String
mdSyncThresh: Int
mdSyncThreshDefault: Int
mdSyncThreshStatus: String
mdWriteMethod: Int
mdWriteMethodDefault: String
mdWriteMethodStatus: String
shareDisk: String
shareUser: String
shareUserInclude: String
shareUserExclude: String
shareSmbEnabled: Boolean
shareNfsEnabled: Boolean
shareAfpEnabled: Boolean
shareInitialOwner: String
shareInitialGroup: String
shareCacheEnabled: Boolean
shareCacheFloor: String
shareMoverSchedule: String
shareMoverLogging: Boolean
fuseRemember: String
fuseRememberDefault: String
fuseRememberStatus: String
fuseDirectio: String
fuseDirectioDefault: String
fuseDirectioStatus: String
shareAvahiEnabled: Boolean
shareAvahiSmbName: String
shareAvahiSmbModel: String
shareAvahiAfpName: String
shareAvahiAfpModel: String
safeMode: Boolean
startMode: String
configValid: Boolean
joinStatus: String
deviceCount: Int
flashGuid: String
flashProduct: String
flashVendor: String
regCheck: String
regFile: String
regGuid: String
"""Registation type"""
regTy: registationType
"""Registation owner"""
regTo: String
regTm: String
regTm2: String
regGen: String
sbName: String
sbVersion: String
sbUpdated: String
sbEvents: Int
sbState: String
sbClean: Boolean
sbSynced: Int
sbSyncErrs: Int
sbSynced2: Int
sbSyncExit: String
sbNumDisks: Int
mdColor: String
mdNumDisks: Int
mdNumDisabled: Int
mdNumInvalid: Int
mdNumMissing: Int
mdNumNew: Int
mdNumErased: Int
mdResync: Int
mdResyncCorr: String
mdResyncPos: String
mdResyncDb: String
mdResyncDt: String
mdResyncAction: String
mdResyncSize: Int
mdState: String
mdVersion: String
cacheNumDevices: Int
cacheSbNumDisks: Int
fsState: String
"""Human friendly string of array events happening"""
fsProgress: String
"""Percentage from 0 - 100 while upgrading a disk or swapping parity drives"""
fsCopyPrcnt: Int
fsNumMounted: Int
fsNumUnmountable: Int
fsUnmountableMask: String
"""Total amount of user shares"""
shareCount: Int
"""Total amount shares with SMB enabled"""
shareSmbCount: Int
"""Total amount shares with NFS enabled"""
shareNfsCount: Int
"""Total amount shares with AFP enabled"""
shareAfpCount: Int
shareMoverActive: Boolean
csrfToken: String
uptime: String!
}
enum mdState {
SWAP_DSBL
STARTED
}
enum registationType {
Basic
Plus
Pro
}
@@ -0,0 +1,63 @@
type Query {
"""Virtual machine"""
vm(name: String!): VmDomain! @func(module: "getDomain")
"""Virtual machines"""
vms: Vms
}
type Vms {
domains: [VmDomain!] @func(module: "getDomains")
}
type VmDomainSubscription {
mutation: MutationType!
node: [VmDomain!]
}
type Subscription {
vms: VmDomainSubscription!
}
enum VmState {
"""Machine is stopped"""
shut_off
"""Machine is running"""
running
}
"""A virtual machine"""
type VmDomain {
uuid: ID!
"""Operating system type"""
osType: String
"""If the vm should auto-start when the server boots"""
autostart: Boolean
"""Max memory in bytes"""
maxMemory: Int
schedulerType: String
schedulerParameters: SchedulerParameters
securityLabel: SecurityLabel
"""A friendly name for the vm"""
name: String
state: VmState
memory: Int
vcpus: Int
cpuTime: Int
}
type SchedulerParameters {
cpu_shares: Int
vcpu_period: Int
vcpu_quota: Int
emulator_period: Int
emulator_quota: Int
global_period: Int
global_quota: Int
iothread_period: Int
iothread_quota: Int
}
type SecurityLabel {
label: String
enforcing: Boolean
}
@@ -0,0 +1,20 @@
type Query {
# @todo finish this
"""Virtual network for vms"""
vmNetwork(name: String!): JSON @func(module: "vms/domains/network/get-network")
# """Virtual networks for vms"""
# vmNetworks: [VmNetwork]
}
type VmNetwork {
_placeholderType: String
}
type VmNetworksSubscription {
mutation: MutationType!
node: [VmNetwork!]
}
type Subscription {
vmNetworks: VmNetworksSubscription!
}