mirror of
https://github.com/unraid/api.git
synced 2025-12-31 05:29:48 -06:00
feat: code first graphql (#1347)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Enhanced API capabilities with improved GraphQL interfaces for remote access, parity checks, notifications, and virtual machine controls. - Introduction of dynamic remote access settings and refined online status and service monitoring. - New `ParityCheckMutationsResolver` for managing parity check operations through GraphQL. - **Refactor** - Consolidated and renamed internal types and schema definitions to improve consistency and performance. - Removed deprecated legacy schemas to streamline the API. - Updated import paths for various types to reflect new module structures. - **Chore** - Updated environment configurations and test setups to support the new logging and configuration mechanisms. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
11
.cursor/rules/api-rules.mdc
Normal file
11
.cursor/rules/api-rules.mdc
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
description:
|
||||
globs: api/*
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
* pnpm ONLY
|
||||
* always run scripts from api/package.json unless requested
|
||||
* prefer adding new files to the nest repo located at api/src/unraid-api/ instead of the legacy code
|
||||
* Test suite is VITEST, do not use jest
|
||||
* Prefer to not mock simple dependencies
|
||||
@@ -9,6 +9,7 @@ PATHS_MY_SERVERS_CONFIG=./dev/Unraid.net/myservers.cfg # My servers config file
|
||||
PATHS_MY_SERVERS_FB=./dev/Unraid.net/fb_keepalive # My servers flashbackup timekeeper file
|
||||
PATHS_KEYFILE_BASE=./dev/Unraid.net # Keyfile location
|
||||
PATHS_MACHINE_ID=./dev/data/machine-id
|
||||
PATHS_PARITY_CHECKS=./dev/states/parity-checks.log
|
||||
ENVIRONMENT="development"
|
||||
NODE_ENV="development"
|
||||
PORT="3001"
|
||||
|
||||
@@ -9,5 +9,6 @@ PATHS_MY_SERVERS_CONFIG=./dev/Unraid.net/myservers.cfg # My servers config file
|
||||
PATHS_MY_SERVERS_FB=./dev/Unraid.net/fb_keepalive # My servers flashbackup timekeeper file
|
||||
PATHS_KEYFILE_BASE=./dev/Unraid.net # Keyfile location
|
||||
PATHS_MACHINE_ID=./dev/data/machine-id
|
||||
PATHS_PARITY_CHECKS=./dev/states/parity-checks.log
|
||||
PORT=5000
|
||||
NODE_ENV="test"
|
||||
@@ -1,18 +1,13 @@
|
||||
import type { CodegenConfig } from '@graphql-codegen/cli';
|
||||
|
||||
|
||||
|
||||
import { getAuthEnumTypeDefs } from './src/unraid-api/graph/utils/auth-enum.utils.js';
|
||||
|
||||
|
||||
const config: CodegenConfig = {
|
||||
overwrite: true,
|
||||
emitLegacyCommonJSImports: false,
|
||||
verbose: true,
|
||||
config: {
|
||||
namingConvention: {
|
||||
typeNames: './fix-array-type.cjs',
|
||||
enumValues: 'change-case#upperCase',
|
||||
enumValues: 'change-case-all#upperCase',
|
||||
transformUnderscore: true,
|
||||
useTypeImports: true,
|
||||
},
|
||||
scalars: {
|
||||
@@ -32,56 +27,6 @@ const config: CodegenConfig = {
|
||||
},
|
||||
},
|
||||
generates: {
|
||||
'./generated-schema.graphql': {
|
||||
plugins: ['schema-ast'],
|
||||
schema: [
|
||||
'./src/graphql/types.ts',
|
||||
'./src/graphql/schema/types/**/*.graphql',
|
||||
getAuthEnumTypeDefs(),
|
||||
],
|
||||
},
|
||||
// Generate Types for the API Server
|
||||
'src/graphql/generated/api/types.ts': {
|
||||
schema: [
|
||||
'./src/graphql/types.ts',
|
||||
'./src/graphql/schema/types/**/*.graphql',
|
||||
getAuthEnumTypeDefs(),
|
||||
],
|
||||
plugins: [
|
||||
'typescript',
|
||||
'typescript-resolvers',
|
||||
{ add: { content: '/* eslint-disable */\n/* @ts-nocheck */' } },
|
||||
],
|
||||
config: {
|
||||
contextType: '@app/graphql/schema/utils.js#Context',
|
||||
useIndexSignature: true,
|
||||
},
|
||||
},
|
||||
// Generate Operations for any built-in API Server Operations (e.g., report.ts)
|
||||
'src/graphql/generated/api/operations.ts': {
|
||||
documents: './src/graphql/client/api/*.ts',
|
||||
schema: [
|
||||
'./src/graphql/types.ts',
|
||||
'./src/graphql/schema/types/**/*.graphql',
|
||||
getAuthEnumTypeDefs(),
|
||||
],
|
||||
preset: 'import-types',
|
||||
presetConfig: {
|
||||
typesPath: '@app/graphql/generated/api/types.js',
|
||||
},
|
||||
plugins: [
|
||||
'typescript-validation-schema',
|
||||
'typescript-operations',
|
||||
'typed-document-node',
|
||||
{ add: { content: '/* eslint-disable */' } },
|
||||
],
|
||||
config: {
|
||||
importFrom: '@app/graphql/generated/api/types.js',
|
||||
strictScalars: true,
|
||||
schema: 'zod',
|
||||
withObjectType: true,
|
||||
},
|
||||
},
|
||||
// Generate Types for Mothership GraphQL Client
|
||||
'src/graphql/generated/client/': {
|
||||
documents: './src/graphql/mothership/*.ts',
|
||||
@@ -120,4 +65,4 @@ const config: CodegenConfig = {
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
export default config;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[api]
|
||||
version="4.4.1"
|
||||
version="4.6.6"
|
||||
extraOrigins="https://google.com,https://test.com"
|
||||
[local]
|
||||
sandbox="yes"
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
/**
|
||||
* This function wraps constant case, that turns any string into CONSTANT_CASE
|
||||
* However, this function has a bug that, if you pass _ to it it will return an empty
|
||||
* string. This small module fixes that
|
||||
*
|
||||
* @param {string*} str
|
||||
* @return {string}
|
||||
*/
|
||||
function FixArrayType(str) {
|
||||
if (str === 'Array') {
|
||||
return 'ArrayType';
|
||||
}
|
||||
|
||||
// If result is an empty string, just return the original string
|
||||
return str;
|
||||
}
|
||||
|
||||
module.exports = FixArrayType;
|
||||
1563
api/generated-schema-new.graphql
Normal file
1563
api/generated-schema-new.graphql
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
3
api/legacy/README.md
Normal file
3
api/legacy/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Legacy Assets
|
||||
|
||||
This folder will store legacy types / functionality that may be useful but is not currently a part of the API
|
||||
1365
api/legacy/generated-schema-legacy.graphql
Normal file
1365
api/legacy/generated-schema-legacy.graphql
Normal file
File diff suppressed because it is too large
Load Diff
@@ -43,7 +43,9 @@
|
||||
"container:start": "pnpm run container:stop && ./scripts/dc.sh run --rm --service-ports dev",
|
||||
"container:stop": "./scripts/dc.sh stop dev",
|
||||
"container:test": "./scripts/dc.sh run --rm builder pnpm run test",
|
||||
"container:enter": "./scripts/dc.sh exec dev /bin/bash"
|
||||
"container:enter": "./scripts/dc.sh exec dev /bin/bash",
|
||||
"// Migration Scripts": "",
|
||||
"migration:codefirst": "tsx ./src/unraid-api/graph/migration-script.ts"
|
||||
},
|
||||
"bin": {
|
||||
"unraid-api": "dist/cli.js"
|
||||
@@ -81,6 +83,8 @@
|
||||
"casbin": "^5.32.0",
|
||||
"change-case": "^5.4.4",
|
||||
"chokidar": "^4.0.1",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.1",
|
||||
"cli-table": "^0.3.11",
|
||||
"command-exists": "^1.2.9",
|
||||
"convert": "^5.8.0",
|
||||
|
||||
@@ -8,202 +8,211 @@ test('Returns both disk and user shares', async () => {
|
||||
await store.dispatch(loadStateFiles());
|
||||
|
||||
expect(getShares()).toMatchInlineSnapshot(`
|
||||
{
|
||||
"disks": [],
|
||||
"users": [
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "appdata",
|
||||
"nameOrig": "appdata",
|
||||
"nfs": {},
|
||||
"size": 0,
|
||||
"smb": {},
|
||||
"splitLevel": "",
|
||||
"type": "user",
|
||||
"used": 33619300,
|
||||
},
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "saved VM instances",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "domains",
|
||||
"nameOrig": "domains",
|
||||
"nfs": {},
|
||||
"size": 0,
|
||||
"smb": {},
|
||||
"splitLevel": "1",
|
||||
"type": "user",
|
||||
"used": 33619300,
|
||||
},
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "ISO images",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "isos",
|
||||
"nameOrig": "isos",
|
||||
"nfs": {},
|
||||
"size": 0,
|
||||
"smb": {},
|
||||
"splitLevel": "",
|
||||
"type": "user",
|
||||
"used": 33619300,
|
||||
},
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "system data",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "system",
|
||||
"nameOrig": "system",
|
||||
"nfs": {},
|
||||
"size": 0,
|
||||
"smb": {},
|
||||
"splitLevel": "1",
|
||||
"type": "user",
|
||||
"used": 33619300,
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
{
|
||||
"disks": [],
|
||||
"users": [
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"id": "appdata",
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "appdata",
|
||||
"nameOrig": "appdata",
|
||||
"nfs": {},
|
||||
"size": 0,
|
||||
"smb": {},
|
||||
"splitLevel": "",
|
||||
"type": "user",
|
||||
"used": 33619300,
|
||||
},
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "saved VM instances",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"id": "domains",
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "domains",
|
||||
"nameOrig": "domains",
|
||||
"nfs": {},
|
||||
"size": 0,
|
||||
"smb": {},
|
||||
"splitLevel": "1",
|
||||
"type": "user",
|
||||
"used": 33619300,
|
||||
},
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "ISO images",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"id": "isos",
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "isos",
|
||||
"nameOrig": "isos",
|
||||
"nfs": {},
|
||||
"size": 0,
|
||||
"smb": {},
|
||||
"splitLevel": "",
|
||||
"type": "user",
|
||||
"used": 33619300,
|
||||
},
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "system data",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"id": "system",
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "system",
|
||||
"nameOrig": "system",
|
||||
"nfs": {},
|
||||
"size": 0,
|
||||
"smb": {},
|
||||
"splitLevel": "1",
|
||||
"type": "user",
|
||||
"used": 33619300,
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('Returns shares by type', async () => {
|
||||
await store.dispatch(loadStateFiles());
|
||||
expect(getShares('user')).toMatchInlineSnapshot(`
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "appdata",
|
||||
"nameOrig": "appdata",
|
||||
"nfs": {},
|
||||
"size": 0,
|
||||
"smb": {},
|
||||
"splitLevel": "",
|
||||
"type": "user",
|
||||
"used": 33619300,
|
||||
}
|
||||
`);
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"id": "appdata",
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "appdata",
|
||||
"nameOrig": "appdata",
|
||||
"nfs": {},
|
||||
"size": 0,
|
||||
"smb": {},
|
||||
"splitLevel": "",
|
||||
"type": "user",
|
||||
"used": 33619300,
|
||||
}
|
||||
`);
|
||||
expect(getShares('users')).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "appdata",
|
||||
"nameOrig": "appdata",
|
||||
"nfs": {},
|
||||
"size": 0,
|
||||
"smb": {},
|
||||
"splitLevel": "",
|
||||
"type": "user",
|
||||
"used": 33619300,
|
||||
},
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "saved VM instances",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "domains",
|
||||
"nameOrig": "domains",
|
||||
"nfs": {},
|
||||
"size": 0,
|
||||
"smb": {},
|
||||
"splitLevel": "1",
|
||||
"type": "user",
|
||||
"used": 33619300,
|
||||
},
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "ISO images",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "isos",
|
||||
"nameOrig": "isos",
|
||||
"nfs": {},
|
||||
"size": 0,
|
||||
"smb": {},
|
||||
"splitLevel": "",
|
||||
"type": "user",
|
||||
"used": 33619300,
|
||||
},
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "system data",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "system",
|
||||
"nameOrig": "system",
|
||||
"nfs": {},
|
||||
"size": 0,
|
||||
"smb": {},
|
||||
"splitLevel": "1",
|
||||
"type": "user",
|
||||
"used": 33619300,
|
||||
},
|
||||
]
|
||||
`);
|
||||
[
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"id": "appdata",
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "appdata",
|
||||
"nameOrig": "appdata",
|
||||
"nfs": {},
|
||||
"size": 0,
|
||||
"smb": {},
|
||||
"splitLevel": "",
|
||||
"type": "user",
|
||||
"used": 33619300,
|
||||
},
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "saved VM instances",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"id": "domains",
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "domains",
|
||||
"nameOrig": "domains",
|
||||
"nfs": {},
|
||||
"size": 0,
|
||||
"smb": {},
|
||||
"splitLevel": "1",
|
||||
"type": "user",
|
||||
"used": 33619300,
|
||||
},
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "ISO images",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"id": "isos",
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "isos",
|
||||
"nameOrig": "isos",
|
||||
"nfs": {},
|
||||
"size": 0,
|
||||
"smb": {},
|
||||
"splitLevel": "",
|
||||
"type": "user",
|
||||
"used": 33619300,
|
||||
},
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "system data",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"id": "system",
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "system",
|
||||
"nameOrig": "system",
|
||||
"nfs": {},
|
||||
"size": 0,
|
||||
"smb": {},
|
||||
"splitLevel": "1",
|
||||
"type": "user",
|
||||
"used": 33619300,
|
||||
},
|
||||
]
|
||||
`);
|
||||
expect(getShares('disk')).toMatchInlineSnapshot('null');
|
||||
expect(getShares('disks')).toMatchInlineSnapshot('[]');
|
||||
});
|
||||
@@ -211,27 +220,28 @@ test('Returns shares by type', async () => {
|
||||
test('Returns shares by name', async () => {
|
||||
await store.dispatch(loadStateFiles());
|
||||
expect(getShares('user', { name: 'domains' })).toMatchInlineSnapshot(`
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "saved VM instances",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "domains",
|
||||
"nameOrig": "domains",
|
||||
"nfs": {},
|
||||
"size": 0,
|
||||
"smb": {},
|
||||
"splitLevel": "1",
|
||||
"type": "user",
|
||||
"used": 33619300,
|
||||
}
|
||||
`);
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "saved VM instances",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"id": "domains",
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "domains",
|
||||
"nameOrig": "domains",
|
||||
"nfs": {},
|
||||
"size": 0,
|
||||
"smb": {},
|
||||
"splitLevel": "1",
|
||||
"type": "user",
|
||||
"used": 33619300,
|
||||
}
|
||||
`);
|
||||
expect(getShares('user', { name: 'non-existent-user-share' })).toMatchInlineSnapshot('null');
|
||||
// @TODO: disk shares need to be added to the dev ini files
|
||||
expect(getShares('disk', { name: 'disk1' })).toMatchInlineSnapshot('null');
|
||||
|
||||
@@ -2,7 +2,6 @@ import { expect, test, vi } from 'vitest';
|
||||
|
||||
import type { NginxUrlFields } from '@app/graphql/resolvers/subscription/network.js';
|
||||
import { type Nginx } from '@app/core/types/states/nginx.js';
|
||||
import { URL_TYPE } from '@app/graphql/generated/client/graphql.js';
|
||||
import {
|
||||
getServerIps,
|
||||
getUrlForField,
|
||||
@@ -11,6 +10,7 @@ import {
|
||||
import { store } from '@app/store/index.js';
|
||||
import { loadConfigFile } from '@app/store/modules/config.js';
|
||||
import { loadStateFiles } from '@app/store/modules/emhttp.js';
|
||||
import { URL_TYPE } from '@app/unraid-api/graph/resolvers/connect/connect.model.js';
|
||||
|
||||
test.each([
|
||||
[{ httpPort: 80, httpsPort: 443, url: 'my-default-url.com' }],
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import { beforeEach, expect, test, vi } from 'vitest';
|
||||
|
||||
import { pubsub, PUBSUB_CHANNEL } from '@app/core/pubsub.js';
|
||||
import { MinigraphStatus, WAN_ACCESS_TYPE, WAN_FORWARD_TYPE } from '@app/graphql/generated/api/types.js';
|
||||
import { GraphQLClient } from '@app/mothership/graphql-client.js';
|
||||
import { stopPingTimeoutJobs } from '@app/mothership/jobs/ping-timeout-jobs.js';
|
||||
import { setGraphqlConnectionStatus } from '@app/store/actions/set-minigraph-status.js';
|
||||
import { setupRemoteAccessThunk } from '@app/store/actions/setup-remote-access.js';
|
||||
import { store } from '@app/store/index.js';
|
||||
import { MyServersConfigMemory } from '@app/types/my-servers-config.js';
|
||||
import { MinigraphStatus } from '@app/unraid-api/graph/resolvers/cloud/cloud.model.js';
|
||||
import {
|
||||
WAN_ACCESS_TYPE,
|
||||
WAN_FORWARD_TYPE,
|
||||
} from '@app/unraid-api/graph/resolvers/connect/connect.model.js';
|
||||
|
||||
// Mock dependencies
|
||||
vi.mock('@app/core/pubsub.js', () => {
|
||||
@@ -145,6 +149,7 @@ test('loginUser updates state and publishes to pubsub', async () => {
|
||||
expect(pubsub.publish).toHaveBeenCalledWith(PUBSUB_CHANNEL.OWNER, {
|
||||
owner: {
|
||||
username: userInfo.username,
|
||||
url: '',
|
||||
avatar: userInfo.avatar,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -197,253 +197,257 @@ test('After init returns values from cfg file for all fields', async () => {
|
||||
}
|
||||
`);
|
||||
expect(disks).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"comment": null,
|
||||
"critical": null,
|
||||
"device": "sdh",
|
||||
"exportable": false,
|
||||
"format": "GPT: 4KiB-aligned",
|
||||
"fsFree": null,
|
||||
"fsSize": null,
|
||||
"fsType": null,
|
||||
"fsUsed": null,
|
||||
"id": "ST18000NM000J-2TV103_ZR585CPY",
|
||||
"idx": 0,
|
||||
"name": "parity",
|
||||
"numErrors": 0,
|
||||
"numReads": 0,
|
||||
"numWrites": 0,
|
||||
"rotational": true,
|
||||
"size": 17578328012,
|
||||
"status": "DISK_OK",
|
||||
"temp": 25,
|
||||
"transport": "ata",
|
||||
"type": "Parity",
|
||||
"warning": null,
|
||||
},
|
||||
{
|
||||
"comment": "Seagate Exos",
|
||||
"critical": 75,
|
||||
"device": "sdf",
|
||||
"exportable": false,
|
||||
"format": "GPT: 4KiB-aligned",
|
||||
"fsFree": 13882739732,
|
||||
"fsSize": 17998742753,
|
||||
"fsType": "xfs",
|
||||
"fsUsed": 4116003021,
|
||||
"id": "ST18000NM000J-2TV103_ZR5B1W9X",
|
||||
"idx": 1,
|
||||
"name": "disk1",
|
||||
"numErrors": 0,
|
||||
"numReads": 0,
|
||||
"numWrites": 0,
|
||||
"rotational": true,
|
||||
"size": 17578328012,
|
||||
"status": "DISK_OK",
|
||||
"temp": 30,
|
||||
"transport": "ata",
|
||||
"type": "Data",
|
||||
"warning": 50,
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"critical": null,
|
||||
"device": "sdj",
|
||||
"exportable": false,
|
||||
"format": "GPT: 4KiB-aligned",
|
||||
"fsFree": 93140746,
|
||||
"fsSize": 11998001574,
|
||||
"fsType": "xfs",
|
||||
"fsUsed": 11904860828,
|
||||
"id": "WDC_WD120EDAZ-11F3RA0_5PJRD45C",
|
||||
"idx": 2,
|
||||
"name": "disk2",
|
||||
"numErrors": 0,
|
||||
"numReads": 0,
|
||||
"numWrites": 0,
|
||||
"rotational": true,
|
||||
"size": 11718885324,
|
||||
"status": "DISK_OK",
|
||||
"temp": 30,
|
||||
"transport": "ata",
|
||||
"type": "Data",
|
||||
"warning": null,
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"critical": null,
|
||||
"device": "sde",
|
||||
"exportable": false,
|
||||
"format": "GPT: 4KiB-aligned",
|
||||
"fsFree": 5519945093,
|
||||
"fsSize": 11998001574,
|
||||
"fsType": "xfs",
|
||||
"fsUsed": 6478056481,
|
||||
"id": "WDC_WD120EMAZ-11BLFA0_5PH8BTYD",
|
||||
"idx": 3,
|
||||
"name": "disk3",
|
||||
"numErrors": 0,
|
||||
"numReads": 0,
|
||||
"numWrites": 0,
|
||||
"rotational": true,
|
||||
"size": 11718885324,
|
||||
"status": "DISK_OK",
|
||||
"temp": 30,
|
||||
"transport": "ata",
|
||||
"type": "Data",
|
||||
"warning": null,
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"critical": null,
|
||||
"device": "sdi",
|
||||
"exportable": false,
|
||||
"format": "MBR: 4KiB-aligned",
|
||||
"fsFree": 111810683,
|
||||
"fsSize": 250059317,
|
||||
"fsType": "btrfs",
|
||||
"fsUsed": 137273827,
|
||||
"id": "Samsung_SSD_850_EVO_250GB_S2R5NX0H643734Z",
|
||||
"idx": 30,
|
||||
"name": "cache",
|
||||
"numErrors": 0,
|
||||
"numReads": 0,
|
||||
"numWrites": 0,
|
||||
"rotational": false,
|
||||
"size": 244198552,
|
||||
"status": "DISK_OK",
|
||||
"temp": 22,
|
||||
"transport": "ata",
|
||||
"type": "Cache",
|
||||
"warning": null,
|
||||
},
|
||||
{
|
||||
"comment": null,
|
||||
"critical": null,
|
||||
"device": "nvme0n1",
|
||||
"exportable": false,
|
||||
"format": "MBR: 4KiB-aligned",
|
||||
"fsFree": null,
|
||||
"fsSize": null,
|
||||
"fsType": null,
|
||||
"fsUsed": null,
|
||||
"id": "KINGSTON_SA2000M8250G_50026B7282669D9E",
|
||||
"idx": 31,
|
||||
"name": "cache2",
|
||||
"numErrors": 0,
|
||||
"numReads": 0,
|
||||
"numWrites": 0,
|
||||
"rotational": false,
|
||||
"size": 244198552,
|
||||
"status": "DISK_OK",
|
||||
"temp": 27,
|
||||
"transport": "nvme",
|
||||
"type": "Cache",
|
||||
"warning": null,
|
||||
},
|
||||
{
|
||||
"comment": "Unraid OS boot device",
|
||||
"critical": null,
|
||||
"device": "sda",
|
||||
"exportable": true,
|
||||
"format": "unknown",
|
||||
"fsFree": 3191407,
|
||||
"fsSize": 4042732,
|
||||
"fsType": "vfat",
|
||||
"fsUsed": 851325,
|
||||
"id": "Cruzer",
|
||||
"idx": 32,
|
||||
"name": "flash",
|
||||
"numErrors": 0,
|
||||
"numReads": 0,
|
||||
"numWrites": 0,
|
||||
"rotational": true,
|
||||
"size": 3956700,
|
||||
"status": "DISK_OK",
|
||||
"temp": null,
|
||||
"transport": "usb",
|
||||
"type": "Flash",
|
||||
"warning": null,
|
||||
},
|
||||
]
|
||||
`);
|
||||
[
|
||||
{
|
||||
"comment": null,
|
||||
"critical": null,
|
||||
"device": "sdh",
|
||||
"exportable": false,
|
||||
"format": "GPT: 4KiB-aligned",
|
||||
"fsFree": null,
|
||||
"fsSize": null,
|
||||
"fsType": null,
|
||||
"fsUsed": null,
|
||||
"id": "ST18000NM000J-2TV103_ZR585CPY",
|
||||
"idx": 0,
|
||||
"name": "parity",
|
||||
"numErrors": 0,
|
||||
"numReads": 0,
|
||||
"numWrites": 0,
|
||||
"rotational": true,
|
||||
"size": 17578328012,
|
||||
"status": "DISK_OK",
|
||||
"temp": 25,
|
||||
"transport": "ata",
|
||||
"type": "PARITY",
|
||||
"warning": null,
|
||||
},
|
||||
{
|
||||
"comment": "Seagate Exos",
|
||||
"critical": 75,
|
||||
"device": "sdf",
|
||||
"exportable": false,
|
||||
"format": "GPT: 4KiB-aligned",
|
||||
"fsFree": 13882739732,
|
||||
"fsSize": 17998742753,
|
||||
"fsType": "xfs",
|
||||
"fsUsed": 4116003021,
|
||||
"id": "ST18000NM000J-2TV103_ZR5B1W9X",
|
||||
"idx": 1,
|
||||
"name": "disk1",
|
||||
"numErrors": 0,
|
||||
"numReads": 0,
|
||||
"numWrites": 0,
|
||||
"rotational": true,
|
||||
"size": 17578328012,
|
||||
"status": "DISK_OK",
|
||||
"temp": 30,
|
||||
"transport": "ata",
|
||||
"type": "DATA",
|
||||
"warning": 50,
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"critical": null,
|
||||
"device": "sdj",
|
||||
"exportable": false,
|
||||
"format": "GPT: 4KiB-aligned",
|
||||
"fsFree": 93140746,
|
||||
"fsSize": 11998001574,
|
||||
"fsType": "xfs",
|
||||
"fsUsed": 11904860828,
|
||||
"id": "WDC_WD120EDAZ-11F3RA0_5PJRD45C",
|
||||
"idx": 2,
|
||||
"name": "disk2",
|
||||
"numErrors": 0,
|
||||
"numReads": 0,
|
||||
"numWrites": 0,
|
||||
"rotational": true,
|
||||
"size": 11718885324,
|
||||
"status": "DISK_OK",
|
||||
"temp": 30,
|
||||
"transport": "ata",
|
||||
"type": "DATA",
|
||||
"warning": null,
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"critical": null,
|
||||
"device": "sde",
|
||||
"exportable": false,
|
||||
"format": "GPT: 4KiB-aligned",
|
||||
"fsFree": 5519945093,
|
||||
"fsSize": 11998001574,
|
||||
"fsType": "xfs",
|
||||
"fsUsed": 6478056481,
|
||||
"id": "WDC_WD120EMAZ-11BLFA0_5PH8BTYD",
|
||||
"idx": 3,
|
||||
"name": "disk3",
|
||||
"numErrors": 0,
|
||||
"numReads": 0,
|
||||
"numWrites": 0,
|
||||
"rotational": true,
|
||||
"size": 11718885324,
|
||||
"status": "DISK_OK",
|
||||
"temp": 30,
|
||||
"transport": "ata",
|
||||
"type": "DATA",
|
||||
"warning": null,
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"critical": null,
|
||||
"device": "sdi",
|
||||
"exportable": false,
|
||||
"format": "MBR: 4KiB-aligned",
|
||||
"fsFree": 111810683,
|
||||
"fsSize": 250059317,
|
||||
"fsType": "btrfs",
|
||||
"fsUsed": 137273827,
|
||||
"id": "Samsung_SSD_850_EVO_250GB_S2R5NX0H643734Z",
|
||||
"idx": 30,
|
||||
"name": "cache",
|
||||
"numErrors": 0,
|
||||
"numReads": 0,
|
||||
"numWrites": 0,
|
||||
"rotational": false,
|
||||
"size": 244198552,
|
||||
"status": "DISK_OK",
|
||||
"temp": 22,
|
||||
"transport": "ata",
|
||||
"type": "CACHE",
|
||||
"warning": null,
|
||||
},
|
||||
{
|
||||
"comment": null,
|
||||
"critical": null,
|
||||
"device": "nvme0n1",
|
||||
"exportable": false,
|
||||
"format": "MBR: 4KiB-aligned",
|
||||
"fsFree": null,
|
||||
"fsSize": null,
|
||||
"fsType": null,
|
||||
"fsUsed": null,
|
||||
"id": "KINGSTON_SA2000M8250G_50026B7282669D9E",
|
||||
"idx": 31,
|
||||
"name": "cache2",
|
||||
"numErrors": 0,
|
||||
"numReads": 0,
|
||||
"numWrites": 0,
|
||||
"rotational": false,
|
||||
"size": 244198552,
|
||||
"status": "DISK_OK",
|
||||
"temp": 27,
|
||||
"transport": "nvme",
|
||||
"type": "CACHE",
|
||||
"warning": null,
|
||||
},
|
||||
{
|
||||
"comment": "Unraid OS boot device",
|
||||
"critical": null,
|
||||
"device": "sda",
|
||||
"exportable": true,
|
||||
"format": "unknown",
|
||||
"fsFree": 3191407,
|
||||
"fsSize": 4042732,
|
||||
"fsType": "vfat",
|
||||
"fsUsed": 851325,
|
||||
"id": "Cruzer",
|
||||
"idx": 32,
|
||||
"name": "flash",
|
||||
"numErrors": 0,
|
||||
"numReads": 0,
|
||||
"numWrites": 0,
|
||||
"rotational": true,
|
||||
"size": 3956700,
|
||||
"status": "DISK_OK",
|
||||
"temp": null,
|
||||
"transport": "usb",
|
||||
"type": "FLASH",
|
||||
"warning": null,
|
||||
},
|
||||
]
|
||||
`);
|
||||
expect(shares).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cache": false,
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "appdata",
|
||||
"nameOrig": "appdata",
|
||||
"size": 0,
|
||||
"splitLevel": "",
|
||||
"used": 33619300,
|
||||
},
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cache": false,
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "saved VM instances",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "domains",
|
||||
"nameOrig": "domains",
|
||||
"size": 0,
|
||||
"splitLevel": "1",
|
||||
"used": 33619300,
|
||||
},
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cache": true,
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "ISO images",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "isos",
|
||||
"nameOrig": "isos",
|
||||
"size": 0,
|
||||
"splitLevel": "",
|
||||
"used": 33619300,
|
||||
},
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cache": false,
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "system data",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "system",
|
||||
"nameOrig": "system",
|
||||
"size": 0,
|
||||
"splitLevel": "1",
|
||||
"used": 33619300,
|
||||
},
|
||||
]
|
||||
`);
|
||||
[
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cache": false,
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"id": "appdata",
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "appdata",
|
||||
"nameOrig": "appdata",
|
||||
"size": 0,
|
||||
"splitLevel": "",
|
||||
"used": 33619300,
|
||||
},
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cache": false,
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "saved VM instances",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"id": "domains",
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "domains",
|
||||
"nameOrig": "domains",
|
||||
"size": 0,
|
||||
"splitLevel": "1",
|
||||
"used": 33619300,
|
||||
},
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cache": true,
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "ISO images",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"id": "isos",
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "isos",
|
||||
"nameOrig": "isos",
|
||||
"size": 0,
|
||||
"splitLevel": "",
|
||||
"used": 33619300,
|
||||
},
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cache": false,
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "system data",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"id": "system",
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "system",
|
||||
"nameOrig": "system",
|
||||
"size": 0,
|
||||
"splitLevel": "1",
|
||||
"used": 33619300,
|
||||
},
|
||||
]
|
||||
`);
|
||||
expect(nfsShares).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
@@ -957,7 +961,7 @@ test('After init returns values from cfg file for all fields', async () => {
|
||||
"configErrorState": "INELIGIBLE",
|
||||
"configValid": false,
|
||||
"csrfToken": "0000000000000000",
|
||||
"defaultFsType": "xfs",
|
||||
"defaultFsType": "XFS",
|
||||
"deviceCount": 4,
|
||||
"domain": "",
|
||||
"domainLogin": "Administrator",
|
||||
|
||||
@@ -15,79 +15,83 @@ test('Returns parsed state file', async () => {
|
||||
type: 'ini',
|
||||
});
|
||||
expect(parse(stateFile)).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cache": false,
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "appdata",
|
||||
"nameOrig": "appdata",
|
||||
"size": 0,
|
||||
"splitLevel": "",
|
||||
"used": 33619300,
|
||||
},
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cache": false,
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "saved VM instances",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "domains",
|
||||
"nameOrig": "domains",
|
||||
"size": 0,
|
||||
"splitLevel": "1",
|
||||
"used": 33619300,
|
||||
},
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cache": true,
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "ISO images",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "isos",
|
||||
"nameOrig": "isos",
|
||||
"size": 0,
|
||||
"splitLevel": "",
|
||||
"used": 33619300,
|
||||
},
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cache": false,
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "system data",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "system",
|
||||
"nameOrig": "system",
|
||||
"size": 0,
|
||||
"splitLevel": "1",
|
||||
"used": 33619300,
|
||||
},
|
||||
]
|
||||
`);
|
||||
[
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cache": false,
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"id": "appdata",
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "appdata",
|
||||
"nameOrig": "appdata",
|
||||
"size": 0,
|
||||
"splitLevel": "",
|
||||
"used": 33619300,
|
||||
},
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cache": false,
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "saved VM instances",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"id": "domains",
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "domains",
|
||||
"nameOrig": "domains",
|
||||
"size": 0,
|
||||
"splitLevel": "1",
|
||||
"used": 33619300,
|
||||
},
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cache": true,
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "ISO images",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"id": "isos",
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "isos",
|
||||
"nameOrig": "isos",
|
||||
"size": 0,
|
||||
"splitLevel": "",
|
||||
"used": 33619300,
|
||||
},
|
||||
{
|
||||
"allocator": "highwater",
|
||||
"cache": false,
|
||||
"cachePool": "cache",
|
||||
"color": "yellow-on",
|
||||
"comment": "system data",
|
||||
"cow": "auto",
|
||||
"exclude": [],
|
||||
"floor": "0",
|
||||
"free": 9309372,
|
||||
"id": "system",
|
||||
"include": [],
|
||||
"luksStatus": "0",
|
||||
"name": "system",
|
||||
"nameOrig": "system",
|
||||
"size": 0,
|
||||
"splitLevel": "1",
|
||||
"used": 33619300,
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
@@ -15,175 +15,175 @@ test('Returns parsed state file', async () => {
|
||||
type: 'ini',
|
||||
});
|
||||
expect(parse(stateFile)).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"comment": null,
|
||||
"critical": null,
|
||||
"device": "sdh",
|
||||
"exportable": false,
|
||||
"format": "GPT: 4KiB-aligned",
|
||||
"fsFree": null,
|
||||
"fsSize": null,
|
||||
"fsType": null,
|
||||
"fsUsed": null,
|
||||
"id": "ST18000NM000J-2TV103_ZR585CPY",
|
||||
"idx": 0,
|
||||
"name": "parity",
|
||||
"numErrors": 0,
|
||||
"numReads": 0,
|
||||
"numWrites": 0,
|
||||
"rotational": true,
|
||||
"size": 17578328012,
|
||||
"status": "DISK_OK",
|
||||
"temp": 25,
|
||||
"transport": "ata",
|
||||
"type": "Parity",
|
||||
"warning": null,
|
||||
},
|
||||
{
|
||||
"comment": "Seagate Exos",
|
||||
"critical": 75,
|
||||
"device": "sdf",
|
||||
"exportable": false,
|
||||
"format": "GPT: 4KiB-aligned",
|
||||
"fsFree": 13882739732,
|
||||
"fsSize": 17998742753,
|
||||
"fsType": "xfs",
|
||||
"fsUsed": 4116003021,
|
||||
"id": "ST18000NM000J-2TV103_ZR5B1W9X",
|
||||
"idx": 1,
|
||||
"name": "disk1",
|
||||
"numErrors": 0,
|
||||
"numReads": 0,
|
||||
"numWrites": 0,
|
||||
"rotational": true,
|
||||
"size": 17578328012,
|
||||
"status": "DISK_OK",
|
||||
"temp": 30,
|
||||
"transport": "ata",
|
||||
"type": "Data",
|
||||
"warning": 50,
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"critical": null,
|
||||
"device": "sdj",
|
||||
"exportable": false,
|
||||
"format": "GPT: 4KiB-aligned",
|
||||
"fsFree": 93140746,
|
||||
"fsSize": 11998001574,
|
||||
"fsType": "xfs",
|
||||
"fsUsed": 11904860828,
|
||||
"id": "WDC_WD120EDAZ-11F3RA0_5PJRD45C",
|
||||
"idx": 2,
|
||||
"name": "disk2",
|
||||
"numErrors": 0,
|
||||
"numReads": 0,
|
||||
"numWrites": 0,
|
||||
"rotational": true,
|
||||
"size": 11718885324,
|
||||
"status": "DISK_OK",
|
||||
"temp": 30,
|
||||
"transport": "ata",
|
||||
"type": "Data",
|
||||
"warning": null,
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"critical": null,
|
||||
"device": "sde",
|
||||
"exportable": false,
|
||||
"format": "GPT: 4KiB-aligned",
|
||||
"fsFree": 5519945093,
|
||||
"fsSize": 11998001574,
|
||||
"fsType": "xfs",
|
||||
"fsUsed": 6478056481,
|
||||
"id": "WDC_WD120EMAZ-11BLFA0_5PH8BTYD",
|
||||
"idx": 3,
|
||||
"name": "disk3",
|
||||
"numErrors": 0,
|
||||
"numReads": 0,
|
||||
"numWrites": 0,
|
||||
"rotational": true,
|
||||
"size": 11718885324,
|
||||
"status": "DISK_OK",
|
||||
"temp": 30,
|
||||
"transport": "ata",
|
||||
"type": "Data",
|
||||
"warning": null,
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"critical": null,
|
||||
"device": "sdi",
|
||||
"exportable": false,
|
||||
"format": "MBR: 4KiB-aligned",
|
||||
"fsFree": 111810683,
|
||||
"fsSize": 250059317,
|
||||
"fsType": "btrfs",
|
||||
"fsUsed": 137273827,
|
||||
"id": "Samsung_SSD_850_EVO_250GB_S2R5NX0H643734Z",
|
||||
"idx": 30,
|
||||
"name": "cache",
|
||||
"numErrors": 0,
|
||||
"numReads": 0,
|
||||
"numWrites": 0,
|
||||
"rotational": false,
|
||||
"size": 244198552,
|
||||
"status": "DISK_OK",
|
||||
"temp": 22,
|
||||
"transport": "ata",
|
||||
"type": "Cache",
|
||||
"warning": null,
|
||||
},
|
||||
{
|
||||
"comment": null,
|
||||
"critical": null,
|
||||
"device": "nvme0n1",
|
||||
"exportable": false,
|
||||
"format": "MBR: 4KiB-aligned",
|
||||
"fsFree": null,
|
||||
"fsSize": null,
|
||||
"fsType": null,
|
||||
"fsUsed": null,
|
||||
"id": "KINGSTON_SA2000M8250G_50026B7282669D9E",
|
||||
"idx": 31,
|
||||
"name": "cache2",
|
||||
"numErrors": 0,
|
||||
"numReads": 0,
|
||||
"numWrites": 0,
|
||||
"rotational": false,
|
||||
"size": 244198552,
|
||||
"status": "DISK_OK",
|
||||
"temp": 27,
|
||||
"transport": "nvme",
|
||||
"type": "Cache",
|
||||
"warning": null,
|
||||
},
|
||||
{
|
||||
"comment": "Unraid OS boot device",
|
||||
"critical": null,
|
||||
"device": "sda",
|
||||
"exportable": true,
|
||||
"format": "unknown",
|
||||
"fsFree": 3191407,
|
||||
"fsSize": 4042732,
|
||||
"fsType": "vfat",
|
||||
"fsUsed": 851325,
|
||||
"id": "Cruzer",
|
||||
"idx": 32,
|
||||
"name": "flash",
|
||||
"numErrors": 0,
|
||||
"numReads": 0,
|
||||
"numWrites": 0,
|
||||
"rotational": true,
|
||||
"size": 3956700,
|
||||
"status": "DISK_OK",
|
||||
"temp": null,
|
||||
"transport": "usb",
|
||||
"type": "Flash",
|
||||
"warning": null,
|
||||
},
|
||||
]
|
||||
`);
|
||||
[
|
||||
{
|
||||
"comment": null,
|
||||
"critical": null,
|
||||
"device": "sdh",
|
||||
"exportable": false,
|
||||
"format": "GPT: 4KiB-aligned",
|
||||
"fsFree": null,
|
||||
"fsSize": null,
|
||||
"fsType": null,
|
||||
"fsUsed": null,
|
||||
"id": "ST18000NM000J-2TV103_ZR585CPY",
|
||||
"idx": 0,
|
||||
"name": "parity",
|
||||
"numErrors": 0,
|
||||
"numReads": 0,
|
||||
"numWrites": 0,
|
||||
"rotational": true,
|
||||
"size": 17578328012,
|
||||
"status": "DISK_OK",
|
||||
"temp": 25,
|
||||
"transport": "ata",
|
||||
"type": "PARITY",
|
||||
"warning": null,
|
||||
},
|
||||
{
|
||||
"comment": "Seagate Exos",
|
||||
"critical": 75,
|
||||
"device": "sdf",
|
||||
"exportable": false,
|
||||
"format": "GPT: 4KiB-aligned",
|
||||
"fsFree": 13882739732,
|
||||
"fsSize": 17998742753,
|
||||
"fsType": "xfs",
|
||||
"fsUsed": 4116003021,
|
||||
"id": "ST18000NM000J-2TV103_ZR5B1W9X",
|
||||
"idx": 1,
|
||||
"name": "disk1",
|
||||
"numErrors": 0,
|
||||
"numReads": 0,
|
||||
"numWrites": 0,
|
||||
"rotational": true,
|
||||
"size": 17578328012,
|
||||
"status": "DISK_OK",
|
||||
"temp": 30,
|
||||
"transport": "ata",
|
||||
"type": "DATA",
|
||||
"warning": 50,
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"critical": null,
|
||||
"device": "sdj",
|
||||
"exportable": false,
|
||||
"format": "GPT: 4KiB-aligned",
|
||||
"fsFree": 93140746,
|
||||
"fsSize": 11998001574,
|
||||
"fsType": "xfs",
|
||||
"fsUsed": 11904860828,
|
||||
"id": "WDC_WD120EDAZ-11F3RA0_5PJRD45C",
|
||||
"idx": 2,
|
||||
"name": "disk2",
|
||||
"numErrors": 0,
|
||||
"numReads": 0,
|
||||
"numWrites": 0,
|
||||
"rotational": true,
|
||||
"size": 11718885324,
|
||||
"status": "DISK_OK",
|
||||
"temp": 30,
|
||||
"transport": "ata",
|
||||
"type": "DATA",
|
||||
"warning": null,
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"critical": null,
|
||||
"device": "sde",
|
||||
"exportable": false,
|
||||
"format": "GPT: 4KiB-aligned",
|
||||
"fsFree": 5519945093,
|
||||
"fsSize": 11998001574,
|
||||
"fsType": "xfs",
|
||||
"fsUsed": 6478056481,
|
||||
"id": "WDC_WD120EMAZ-11BLFA0_5PH8BTYD",
|
||||
"idx": 3,
|
||||
"name": "disk3",
|
||||
"numErrors": 0,
|
||||
"numReads": 0,
|
||||
"numWrites": 0,
|
||||
"rotational": true,
|
||||
"size": 11718885324,
|
||||
"status": "DISK_OK",
|
||||
"temp": 30,
|
||||
"transport": "ata",
|
||||
"type": "DATA",
|
||||
"warning": null,
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"critical": null,
|
||||
"device": "sdi",
|
||||
"exportable": false,
|
||||
"format": "MBR: 4KiB-aligned",
|
||||
"fsFree": 111810683,
|
||||
"fsSize": 250059317,
|
||||
"fsType": "btrfs",
|
||||
"fsUsed": 137273827,
|
||||
"id": "Samsung_SSD_850_EVO_250GB_S2R5NX0H643734Z",
|
||||
"idx": 30,
|
||||
"name": "cache",
|
||||
"numErrors": 0,
|
||||
"numReads": 0,
|
||||
"numWrites": 0,
|
||||
"rotational": false,
|
||||
"size": 244198552,
|
||||
"status": "DISK_OK",
|
||||
"temp": 22,
|
||||
"transport": "ata",
|
||||
"type": "CACHE",
|
||||
"warning": null,
|
||||
},
|
||||
{
|
||||
"comment": null,
|
||||
"critical": null,
|
||||
"device": "nvme0n1",
|
||||
"exportable": false,
|
||||
"format": "MBR: 4KiB-aligned",
|
||||
"fsFree": null,
|
||||
"fsSize": null,
|
||||
"fsType": null,
|
||||
"fsUsed": null,
|
||||
"id": "KINGSTON_SA2000M8250G_50026B7282669D9E",
|
||||
"idx": 31,
|
||||
"name": "cache2",
|
||||
"numErrors": 0,
|
||||
"numReads": 0,
|
||||
"numWrites": 0,
|
||||
"rotational": false,
|
||||
"size": 244198552,
|
||||
"status": "DISK_OK",
|
||||
"temp": 27,
|
||||
"transport": "nvme",
|
||||
"type": "CACHE",
|
||||
"warning": null,
|
||||
},
|
||||
{
|
||||
"comment": "Unraid OS boot device",
|
||||
"critical": null,
|
||||
"device": "sda",
|
||||
"exportable": true,
|
||||
"format": "unknown",
|
||||
"fsFree": 3191407,
|
||||
"fsSize": 4042732,
|
||||
"fsType": "vfat",
|
||||
"fsUsed": 851325,
|
||||
"id": "Cruzer",
|
||||
"idx": 32,
|
||||
"name": "flash",
|
||||
"numErrors": 0,
|
||||
"numReads": 0,
|
||||
"numWrites": 0,
|
||||
"rotational": true,
|
||||
"size": 3956700,
|
||||
"status": "DISK_OK",
|
||||
"temp": null,
|
||||
"transport": "usb",
|
||||
"type": "FLASH",
|
||||
"warning": null,
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
@@ -24,7 +24,7 @@ test('Returns parsed state file', async () => {
|
||||
"configErrorState": "INELIGIBLE",
|
||||
"configValid": false,
|
||||
"csrfToken": "0000000000000000",
|
||||
"defaultFsType": "xfs",
|
||||
"defaultFsType": "XFS",
|
||||
"deviceCount": 4,
|
||||
"domain": "",
|
||||
"domainLogin": "Administrator",
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import { GraphQLError } from 'graphql';
|
||||
import { sum } from 'lodash-es';
|
||||
|
||||
import type { ArrayCapacity, ArrayType } from '@app/graphql/generated/api/types.js';
|
||||
import { ArrayDiskType } from '@app/graphql/generated/api/types.js';
|
||||
import { store } from '@app/store/index.js';
|
||||
import { FileLoadStatus } from '@app/store/types.js';
|
||||
import {
|
||||
ArrayCapacity,
|
||||
ArrayDiskType,
|
||||
ArrayState,
|
||||
UnraidArray,
|
||||
} from '@app/unraid-api/graph/resolvers/array/array.model.js';
|
||||
|
||||
export const getArrayData = (getState = store.getState): ArrayType => {
|
||||
export const getArrayData = (getState = store.getState): UnraidArray => {
|
||||
// Var state isn't loaded
|
||||
const state = getState();
|
||||
if (
|
||||
@@ -51,7 +55,7 @@ export const getArrayData = (getState = store.getState): ArrayType => {
|
||||
|
||||
return {
|
||||
id: 'array',
|
||||
state: emhttp.var.mdState,
|
||||
state: emhttp.var.mdState as ArrayState,
|
||||
capacity,
|
||||
boot,
|
||||
parities,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { AppError } from '@app/core/errors/app-error.js';
|
||||
import { type CoreContext, type CoreResult } from '@app/core/types/index.js';
|
||||
import { Disk } from '@app/graphql/generated/api/types.js';
|
||||
import { ArrayDisk } from '@app/unraid-api/graph/resolvers/array/array.model.js';
|
||||
|
||||
interface Context extends CoreContext {
|
||||
params: {
|
||||
@@ -11,7 +11,7 @@ interface Context extends CoreContext {
|
||||
/**
|
||||
* Get a single disk.
|
||||
*/
|
||||
export const getDisk = async (context: Context, Disks: Disk[]): Promise<CoreResult> => {
|
||||
export const getDisk = async (context: Context, Disks: ArrayDisk[]): Promise<CoreResult> => {
|
||||
const { params } = context;
|
||||
|
||||
const { id } = params;
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
import { promises as fs } from 'fs';
|
||||
|
||||
import Table from 'cli-table';
|
||||
|
||||
import { FileMissingError } from '@app/core/errors/file-missing-error.js';
|
||||
import { type CoreContext, type CoreResult } from '@app/core/types/index.js';
|
||||
import { ensurePermission } from '@app/core/utils/permissions/ensure-permission.js';
|
||||
import { getters } from '@app/store/index.js';
|
||||
|
||||
/**
|
||||
* Get parity history.
|
||||
* @returns All parity checks with their respective date, duration, speed, status and errors.
|
||||
*/
|
||||
export const getParityHistory = async (context: CoreContext): Promise<CoreResult> => {
|
||||
const { user } = context;
|
||||
|
||||
// Bail if the user doesn't have permission
|
||||
ensurePermission(user, {
|
||||
resource: 'parity-history',
|
||||
action: 'read',
|
||||
possession: 'any',
|
||||
});
|
||||
|
||||
const historyFilePath = getters.paths()['parity-checks'];
|
||||
const history = await fs.readFile(historyFilePath).catch(() => {
|
||||
throw new FileMissingError(historyFilePath);
|
||||
});
|
||||
|
||||
// Convert checks into array of objects
|
||||
const lines = history.toString().trim().split('\n').reverse();
|
||||
const parityChecks = lines.map((line) => {
|
||||
const [date, duration, speed, status, errors = '0'] = line.split('|');
|
||||
return {
|
||||
date,
|
||||
duration: Number.parseInt(duration, 10),
|
||||
speed,
|
||||
status,
|
||||
errors: Number.parseInt(errors, 10),
|
||||
};
|
||||
});
|
||||
|
||||
// Create table for text output
|
||||
const table = new Table({
|
||||
head: ['Date', 'Duration', 'Speed', 'Status', 'Errors'],
|
||||
});
|
||||
// Update raw values with strings
|
||||
parityChecks.forEach((check) => {
|
||||
const array = Object.values({
|
||||
date: check.date,
|
||||
speed: check.speed ? check.speed : 'Unavailable',
|
||||
duration: check.duration >= 0 ? check.duration.toString() : 'Unavailable',
|
||||
status: check.status === '-4' ? 'Cancelled' : 'OK',
|
||||
errors: check.errors.toString(),
|
||||
});
|
||||
table.push(array);
|
||||
});
|
||||
|
||||
return {
|
||||
text: table.toString(),
|
||||
json: parityChecks,
|
||||
};
|
||||
};
|
||||
@@ -10,7 +10,6 @@ export * from './add-share.js';
|
||||
export * from './add-user.js';
|
||||
export * from './get-apps.js';
|
||||
export * from './get-devices.js';
|
||||
export * from './get-parity-history.js';
|
||||
export * from './get-services.js';
|
||||
export * from './get-users.js';
|
||||
export * from './get-welcome.js';
|
||||
|
||||
@@ -19,6 +19,7 @@ export enum PUBSUB_CHANNEL {
|
||||
VMS = 'VMS',
|
||||
REGISTRATION = 'REGISTRATION',
|
||||
LOG_FILE = 'LOG_FILE',
|
||||
PARITY = 'PARITY',
|
||||
}
|
||||
|
||||
export const pubsub = new PubSub({ eventEmitter });
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type {
|
||||
ArrayState,
|
||||
DiskFsType,
|
||||
import { ArrayState } from '@app/unraid-api/graph/resolvers/array/array.model.js';
|
||||
import { DiskFsType } from '@app/unraid-api/graph/resolvers/disks/disks.model.js';
|
||||
import {
|
||||
RegistrationState,
|
||||
registrationType,
|
||||
} from '@app/graphql/generated/api/types.js';
|
||||
import { ConfigErrorState } from '@app/graphql/generated/api/types.js';
|
||||
RegistrationType,
|
||||
} from '@app/unraid-api/graph/resolvers/registration/registration.model.js';
|
||||
import { ConfigErrorState } from '@app/unraid-api/graph/resolvers/vars/vars.model.js';
|
||||
|
||||
/**
|
||||
* Global vars
|
||||
@@ -128,7 +128,7 @@ export type Var = {
|
||||
/** Who the current Unraid key is registered to. */
|
||||
regTo: string;
|
||||
/** Which type of key this is. */
|
||||
regTy: registrationType;
|
||||
regTy: RegistrationType;
|
||||
/** Is the server currently in safe mode. */
|
||||
safeMode: boolean;
|
||||
sbClean: boolean;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ArrayState } from '@app/graphql/generated/api/types.js';
|
||||
import { getters } from '@app/store/index.js';
|
||||
import { ArrayState } from '@app/unraid-api/graph/resolvers/array/array.model.js';
|
||||
|
||||
/**
|
||||
* Is the array running?
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { DiskShare, Share, UserShare } from '@app/core/types/states/share.js';
|
||||
import type { ArrayDisk } from '@app/graphql/generated/api/types.js';
|
||||
import { getters } from '@app/store/index.js';
|
||||
import { ArrayDisk } from '@app/unraid-api/graph/resolvers/array/array.model.js';
|
||||
|
||||
const processors = {
|
||||
user(share: Share) {
|
||||
|
||||
@@ -2,4 +2,3 @@
|
||||
|
||||
export * from './filter-devices.js';
|
||||
export * from './get-pci-devices.js';
|
||||
export * from './system-network-interfaces.js';
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
import { networkInterfaces } from 'systeminformation';
|
||||
|
||||
export const systemNetworkInterfaces = networkInterfaces();
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -14,14 +14,14 @@ import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-
|
||||
* Learn more about it here: https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#reducing-bundle-size
|
||||
*/
|
||||
type Documents = {
|
||||
"\n mutation sendRemoteGraphQLResponse($input: RemoteGraphQLServerInput!) {\n remoteGraphQLResponse(input: $input)\n }\n": typeof types.sendRemoteGraphQLResponseDocument,
|
||||
"\n fragment RemoteGraphQLEventFragment on RemoteGraphQLEvent {\n remoteGraphQLEventData: data {\n type\n body\n sha256\n }\n }\n": typeof types.RemoteGraphQLEventFragmentFragmentDoc,
|
||||
"\n subscription events {\n events {\n __typename\n ... on ClientConnectedEvent {\n connectedData: data {\n type\n version\n apiKey\n }\n connectedEvent: type\n }\n ... on ClientDisconnectedEvent {\n disconnectedData: data {\n type\n version\n apiKey\n }\n disconnectedEvent: type\n }\n ...RemoteGraphQLEventFragment\n }\n }\n": typeof types.eventsDocument,
|
||||
"\n mutation sendRemoteGraphQLResponse($input: RemoteGraphQLServerInput!) {\n remoteGraphQLResponse(input: $input)\n }\n": typeof types.SendRemoteGraphQlResponseDocument,
|
||||
"\n fragment RemoteGraphQLEventFragment on RemoteGraphQLEvent {\n remoteGraphQLEventData: data {\n type\n body\n sha256\n }\n }\n": typeof types.RemoteGraphQlEventFragmentFragmentDoc,
|
||||
"\n subscription events {\n events {\n __typename\n ... on ClientConnectedEvent {\n connectedData: data {\n type\n version\n apiKey\n }\n connectedEvent: type\n }\n ... on ClientDisconnectedEvent {\n disconnectedData: data {\n type\n version\n apiKey\n }\n disconnectedEvent: type\n }\n ...RemoteGraphQLEventFragment\n }\n }\n": typeof types.EventsDocument,
|
||||
};
|
||||
const documents: Documents = {
|
||||
"\n mutation sendRemoteGraphQLResponse($input: RemoteGraphQLServerInput!) {\n remoteGraphQLResponse(input: $input)\n }\n": types.sendRemoteGraphQLResponseDocument,
|
||||
"\n fragment RemoteGraphQLEventFragment on RemoteGraphQLEvent {\n remoteGraphQLEventData: data {\n type\n body\n sha256\n }\n }\n": types.RemoteGraphQLEventFragmentFragmentDoc,
|
||||
"\n subscription events {\n events {\n __typename\n ... on ClientConnectedEvent {\n connectedData: data {\n type\n version\n apiKey\n }\n connectedEvent: type\n }\n ... on ClientDisconnectedEvent {\n disconnectedData: data {\n type\n version\n apiKey\n }\n disconnectedEvent: type\n }\n ...RemoteGraphQLEventFragment\n }\n }\n": types.eventsDocument,
|
||||
"\n mutation sendRemoteGraphQLResponse($input: RemoteGraphQLServerInput!) {\n remoteGraphQLResponse(input: $input)\n }\n": types.SendRemoteGraphQlResponseDocument,
|
||||
"\n fragment RemoteGraphQLEventFragment on RemoteGraphQLEvent {\n remoteGraphQLEventData: data {\n type\n body\n sha256\n }\n }\n": types.RemoteGraphQlEventFragmentFragmentDoc,
|
||||
"\n subscription events {\n events {\n __typename\n ... on ClientConnectedEvent {\n connectedData: data {\n type\n version\n apiKey\n }\n connectedEvent: type\n }\n ... on ClientDisconnectedEvent {\n disconnectedData: data {\n type\n version\n apiKey\n }\n disconnectedEvent: type\n }\n ...RemoteGraphQLEventFragment\n }\n }\n": types.EventsDocument,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,14 +35,14 @@ export type AccessUrl = {
|
||||
ipv4?: Maybe<Scalars['URL']['output']>;
|
||||
ipv6?: Maybe<Scalars['URL']['output']>;
|
||||
name?: Maybe<Scalars['String']['output']>;
|
||||
type: URL_TYPE;
|
||||
type: UrlType;
|
||||
};
|
||||
|
||||
export type AccessUrlInput = {
|
||||
ipv4?: InputMaybe<Scalars['URL']['input']>;
|
||||
ipv6?: InputMaybe<Scalars['URL']['input']>;
|
||||
name?: InputMaybe<Scalars['String']['input']>;
|
||||
type: URL_TYPE;
|
||||
type: UrlType;
|
||||
};
|
||||
|
||||
export type ArrayCapacity = {
|
||||
@@ -305,7 +305,7 @@ export type DashboardVmsInput = {
|
||||
started: Scalars['Int']['input'];
|
||||
};
|
||||
|
||||
export type Event = ClientConnectedEvent | ClientDisconnectedEvent | ClientPingEvent | RemoteAccessEvent | RemoteGraphQLEvent | UpdateEvent;
|
||||
export type Event = ClientConnectedEvent | ClientDisconnectedEvent | ClientPingEvent | RemoteAccessEvent | RemoteGraphQlEvent | UpdateEvent;
|
||||
|
||||
export enum EventType {
|
||||
CLIENT_CONNECTED_EVENT = 'CLIENT_CONNECTED_EVENT',
|
||||
@@ -373,32 +373,32 @@ export type Mutation = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationremoteGraphQLResponseArgs = {
|
||||
input: RemoteGraphQLServerInput;
|
||||
export type MutationRemoteGraphQlResponseArgs = {
|
||||
input: RemoteGraphQlServerInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationremoteMutationArgs = {
|
||||
input: RemoteGraphQLClientInput;
|
||||
export type MutationRemoteMutationArgs = {
|
||||
input: RemoteGraphQlClientInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationremoteSessionArgs = {
|
||||
export type MutationRemoteSessionArgs = {
|
||||
remoteAccess: RemoteAccessInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationsendNotificationArgs = {
|
||||
export type MutationSendNotificationArgs = {
|
||||
notification: NotificationInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationupdateDashboardArgs = {
|
||||
export type MutationUpdateDashboardArgs = {
|
||||
data: DashboardInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationupdateNetworkArgs = {
|
||||
export type MutationUpdateNetworkArgs = {
|
||||
data: NetworkInput;
|
||||
};
|
||||
|
||||
@@ -474,17 +474,17 @@ export type Query = {
|
||||
};
|
||||
|
||||
|
||||
export type QuerydashboardArgs = {
|
||||
export type QueryDashboardArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type QueryremoteQueryArgs = {
|
||||
input: RemoteGraphQLClientInput;
|
||||
export type QueryRemoteQueryArgs = {
|
||||
input: RemoteGraphQlClientInput;
|
||||
};
|
||||
|
||||
|
||||
export type QueryserverStatusArgs = {
|
||||
export type QueryServerStatusArgs = {
|
||||
apiKey: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
@@ -557,7 +557,7 @@ export type RemoteAccessInput = {
|
||||
url?: InputMaybe<AccessUrlInput>;
|
||||
};
|
||||
|
||||
export type RemoteGraphQLClientInput = {
|
||||
export type RemoteGraphQlClientInput = {
|
||||
apiKey: Scalars['String']['input'];
|
||||
body: Scalars['String']['input'];
|
||||
/** Time in milliseconds to wait for a response from the remote server (defaults to 15000) */
|
||||
@@ -566,34 +566,34 @@ export type RemoteGraphQLClientInput = {
|
||||
ttl?: InputMaybe<Scalars['Int']['input']>;
|
||||
};
|
||||
|
||||
export type RemoteGraphQLEvent = {
|
||||
export type RemoteGraphQlEvent = {
|
||||
__typename?: 'RemoteGraphQLEvent';
|
||||
data: RemoteGraphQLEventData;
|
||||
data: RemoteGraphQlEventData;
|
||||
type: EventType;
|
||||
};
|
||||
|
||||
export type RemoteGraphQLEventData = {
|
||||
export type RemoteGraphQlEventData = {
|
||||
__typename?: 'RemoteGraphQLEventData';
|
||||
/** Contains mutation / subscription / query data in the form of body: JSON, variables: JSON */
|
||||
body: Scalars['String']['output'];
|
||||
/** sha256 hash of the body */
|
||||
sha256: Scalars['String']['output'];
|
||||
type: RemoteGraphQLEventType;
|
||||
type: RemoteGraphQlEventType;
|
||||
};
|
||||
|
||||
export enum RemoteGraphQLEventType {
|
||||
export enum RemoteGraphQlEventType {
|
||||
REMOTE_MUTATION_EVENT = 'REMOTE_MUTATION_EVENT',
|
||||
REMOTE_QUERY_EVENT = 'REMOTE_QUERY_EVENT',
|
||||
REMOTE_SUBSCRIPTION_EVENT = 'REMOTE_SUBSCRIPTION_EVENT',
|
||||
REMOTE_SUBSCRIPTION_EVENT_PING = 'REMOTE_SUBSCRIPTION_EVENT_PING'
|
||||
}
|
||||
|
||||
export type RemoteGraphQLServerInput = {
|
||||
export type RemoteGraphQlServerInput = {
|
||||
/** Body - contains an object containing data: (GQL response data) or errors: (GQL Errors) */
|
||||
body: Scalars['String']['input'];
|
||||
/** sha256 hash of the body */
|
||||
sha256: Scalars['String']['input'];
|
||||
type: RemoteGraphQLEventType;
|
||||
type: RemoteGraphQlEventType;
|
||||
};
|
||||
|
||||
export type Server = {
|
||||
@@ -654,8 +654,8 @@ export type Subscription = {
|
||||
};
|
||||
|
||||
|
||||
export type SubscriptionremoteSubscriptionArgs = {
|
||||
input: RemoteGraphQLClientInput;
|
||||
export type SubscriptionRemoteSubscriptionArgs = {
|
||||
input: RemoteGraphQlClientInput;
|
||||
};
|
||||
|
||||
export type TwoFactorLocal = {
|
||||
@@ -681,7 +681,7 @@ export type TwoFactorWithoutToken = {
|
||||
remote?: Maybe<TwoFactorRemote>;
|
||||
};
|
||||
|
||||
export enum URL_TYPE {
|
||||
export enum UrlType {
|
||||
DEFAULT = 'DEFAULT',
|
||||
LAN = 'LAN',
|
||||
MDNS = 'MDNS',
|
||||
@@ -726,23 +726,23 @@ export type Vars = {
|
||||
regTy?: Maybe<Scalars['String']['output']>;
|
||||
};
|
||||
|
||||
export type sendRemoteGraphQLResponseMutationVariables = Exact<{
|
||||
input: RemoteGraphQLServerInput;
|
||||
export type SendRemoteGraphQlResponseMutationVariables = Exact<{
|
||||
input: RemoteGraphQlServerInput;
|
||||
}>;
|
||||
|
||||
|
||||
export type sendRemoteGraphQLResponseMutation = { __typename?: 'Mutation', remoteGraphQLResponse: boolean };
|
||||
export type SendRemoteGraphQlResponseMutation = { __typename?: 'Mutation', remoteGraphQLResponse: boolean };
|
||||
|
||||
export type RemoteGraphQLEventFragmentFragment = { __typename?: 'RemoteGraphQLEvent', remoteGraphQLEventData: { __typename?: 'RemoteGraphQLEventData', type: RemoteGraphQLEventType, body: string, sha256: string } } & { ' $fragmentName'?: 'RemoteGraphQLEventFragmentFragment' };
|
||||
export type RemoteGraphQlEventFragmentFragment = { __typename?: 'RemoteGraphQLEvent', remoteGraphQLEventData: { __typename?: 'RemoteGraphQLEventData', type: RemoteGraphQlEventType, body: string, sha256: string } } & { ' $fragmentName'?: 'RemoteGraphQlEventFragmentFragment' };
|
||||
|
||||
export type eventsSubscriptionVariables = Exact<{ [key: string]: never; }>;
|
||||
export type EventsSubscriptionVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type eventsSubscription = { __typename?: 'Subscription', events?: Array<{ __typename: 'ClientConnectedEvent', connectedEvent: EventType, connectedData: { __typename?: 'ClientConnectionEventData', type: ClientType, version: string, apiKey: string } } | { __typename: 'ClientDisconnectedEvent', disconnectedEvent: EventType, disconnectedData: { __typename?: 'ClientConnectionEventData', type: ClientType, version: string, apiKey: string } } | { __typename: 'ClientPingEvent' } | { __typename: 'RemoteAccessEvent' } | (
|
||||
export type EventsSubscription = { __typename?: 'Subscription', events?: Array<{ __typename: 'ClientConnectedEvent', connectedEvent: EventType, connectedData: { __typename?: 'ClientConnectionEventData', type: ClientType, version: string, apiKey: string } } | { __typename: 'ClientDisconnectedEvent', disconnectedEvent: EventType, disconnectedData: { __typename?: 'ClientConnectionEventData', type: ClientType, version: string, apiKey: string } } | { __typename: 'ClientPingEvent' } | { __typename: 'RemoteAccessEvent' } | (
|
||||
{ __typename: 'RemoteGraphQLEvent' }
|
||||
& { ' $fragmentRefs'?: { 'RemoteGraphQLEventFragmentFragment': RemoteGraphQLEventFragmentFragment } }
|
||||
& { ' $fragmentRefs'?: { 'RemoteGraphQlEventFragmentFragment': RemoteGraphQlEventFragmentFragment } }
|
||||
) | { __typename: 'UpdateEvent' }> | null };
|
||||
|
||||
export const RemoteGraphQLEventFragmentFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteGraphQLEventFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteGraphQLEvent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"remoteGraphQLEventData"},"name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"body"}},{"kind":"Field","name":{"kind":"Name","value":"sha256"}}]}}]}}]} as unknown as DocumentNode<RemoteGraphQLEventFragmentFragment, unknown>;
|
||||
export const sendRemoteGraphQLResponseDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"sendRemoteGraphQLResponse"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteGraphQLServerInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"remoteGraphQLResponse"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode<sendRemoteGraphQLResponseMutation, sendRemoteGraphQLResponseMutationVariables>;
|
||||
export const eventsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"subscription","name":{"kind":"Name","value":"events"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"events"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ClientConnectedEvent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"connectedData"},"name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"version"}},{"kind":"Field","name":{"kind":"Name","value":"apiKey"}}]}},{"kind":"Field","alias":{"kind":"Name","value":"connectedEvent"},"name":{"kind":"Name","value":"type"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ClientDisconnectedEvent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"disconnectedData"},"name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"version"}},{"kind":"Field","name":{"kind":"Name","value":"apiKey"}}]}},{"kind":"Field","alias":{"kind":"Name","value":"disconnectedEvent"},"name":{"kind":"Name","value":"type"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"RemoteGraphQLEventFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteGraphQLEventFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteGraphQLEvent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"remoteGraphQLEventData"},"name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"body"}},{"kind":"Field","name":{"kind":"Name","value":"sha256"}}]}}]}}]} as unknown as DocumentNode<eventsSubscription, eventsSubscriptionVariables>;
|
||||
export const RemoteGraphQlEventFragmentFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteGraphQLEventFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteGraphQLEvent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"remoteGraphQLEventData"},"name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"body"}},{"kind":"Field","name":{"kind":"Name","value":"sha256"}}]}}]}}]} as unknown as DocumentNode<RemoteGraphQlEventFragmentFragment, unknown>;
|
||||
export const SendRemoteGraphQlResponseDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"sendRemoteGraphQLResponse"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteGraphQLServerInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"remoteGraphQLResponse"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode<SendRemoteGraphQlResponseMutation, SendRemoteGraphQlResponseMutationVariables>;
|
||||
export const EventsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"subscription","name":{"kind":"Name","value":"events"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"events"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ClientConnectedEvent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"connectedData"},"name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"version"}},{"kind":"Field","name":{"kind":"Name","value":"apiKey"}}]}},{"kind":"Field","alias":{"kind":"Name","value":"connectedEvent"},"name":{"kind":"Name","value":"type"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ClientDisconnectedEvent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"disconnectedData"},"name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"version"}},{"kind":"Field","name":{"kind":"Name","value":"apiKey"}}]}},{"kind":"Field","alias":{"kind":"Name","value":"disconnectedEvent"},"name":{"kind":"Name","value":"type"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"RemoteGraphQLEventFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteGraphQLEventFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteGraphQLEvent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"remoteGraphQLEventData"},"name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"body"}},{"kind":"Field","name":{"kind":"Name","value":"sha256"}}]}}]}}]} as unknown as DocumentNode<EventsSubscription, EventsSubscriptionVariables>;
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable */
|
||||
import { z } from 'zod'
|
||||
import { AccessUrlInput, ArrayCapacityBytesInput, ArrayCapacityInput, ClientType, ConfigErrorState, DashboardAppsInput, DashboardArrayInput, DashboardCaseInput, DashboardConfigInput, DashboardDisplayInput, DashboardInput, DashboardOsInput, DashboardServiceInput, DashboardServiceUptimeInput, DashboardTwoFactorInput, DashboardTwoFactorLocalInput, DashboardTwoFactorRemoteInput, DashboardVarsInput, DashboardVersionsInput, DashboardVmsInput, EventType, Importance, NetworkInput, NotificationInput, NotificationStatus, PingEventSource, RegistrationState, RemoteAccessEventActionType, RemoteAccessInput, RemoteGraphQLClientInput, RemoteGraphQLEventType, RemoteGraphQLServerInput, ServerStatus, URL_TYPE, UpdateType } from '@app/graphql/generated/client/graphql.js'
|
||||
import { AccessUrlInput, ArrayCapacityBytesInput, ArrayCapacityInput, ClientType, ConfigErrorState, DashboardAppsInput, DashboardArrayInput, DashboardCaseInput, DashboardConfigInput, DashboardDisplayInput, DashboardInput, DashboardOsInput, DashboardServiceInput, DashboardServiceUptimeInput, DashboardTwoFactorInput, DashboardTwoFactorLocalInput, DashboardTwoFactorRemoteInput, DashboardVarsInput, DashboardVersionsInput, DashboardVmsInput, EventType, Importance, NetworkInput, NotificationInput, NotificationStatus, PingEventSource, RegistrationState, RemoteAccessEventActionType, RemoteAccessInput, RemoteGraphQlClientInput, RemoteGraphQlEventType, RemoteGraphQlServerInput, ServerStatus, UrlType, UpdateType } from '@app/graphql/generated/client/graphql.js'
|
||||
|
||||
type Properties<T> = Required<{
|
||||
[K in keyof T]: z.ZodType<T[K], any, T[K]>;
|
||||
@@ -28,11 +28,11 @@ export const RegistrationStateSchema = z.nativeEnum(RegistrationState);
|
||||
|
||||
export const RemoteAccessEventActionTypeSchema = z.nativeEnum(RemoteAccessEventActionType);
|
||||
|
||||
export const RemoteGraphQLEventTypeSchema = z.nativeEnum(RemoteGraphQLEventType);
|
||||
export const RemoteGraphQlEventTypeSchema = z.nativeEnum(RemoteGraphQlEventType);
|
||||
|
||||
export const ServerStatusSchema = z.nativeEnum(ServerStatus);
|
||||
|
||||
export const URL_TYPESchema = z.nativeEnum(URL_TYPE);
|
||||
export const UrlTypeSchema = z.nativeEnum(UrlType);
|
||||
|
||||
export const UpdateTypeSchema = z.nativeEnum(UpdateType);
|
||||
|
||||
@@ -41,7 +41,7 @@ export function AccessUrlInputSchema(): z.ZodObject<Properties<AccessUrlInput>>
|
||||
ipv4: z.instanceof(URL).nullish(),
|
||||
ipv6: z.instanceof(URL).nullish(),
|
||||
name: z.string().nullish(),
|
||||
type: URL_TYPESchema
|
||||
type: UrlTypeSchema
|
||||
})
|
||||
}
|
||||
|
||||
@@ -198,7 +198,7 @@ export function RemoteAccessInputSchema(): z.ZodObject<Properties<RemoteAccessIn
|
||||
})
|
||||
}
|
||||
|
||||
export function RemoteGraphQLClientInputSchema(): z.ZodObject<Properties<RemoteGraphQLClientInput>> {
|
||||
export function RemoteGraphQlClientInputSchema(): z.ZodObject<Properties<RemoteGraphQlClientInput>> {
|
||||
return z.object({
|
||||
apiKey: z.string(),
|
||||
body: z.string(),
|
||||
@@ -207,10 +207,10 @@ export function RemoteGraphQLClientInputSchema(): z.ZodObject<Properties<RemoteG
|
||||
})
|
||||
}
|
||||
|
||||
export function RemoteGraphQLServerInputSchema(): z.ZodObject<Properties<RemoteGraphQLServerInput>> {
|
||||
export function RemoteGraphQlServerInputSchema(): z.ZodObject<Properties<RemoteGraphQlServerInput>> {
|
||||
return z.object({
|
||||
body: z.string(),
|
||||
sha256: z.string(),
|
||||
type: RemoteGraphQLEventTypeSchema
|
||||
type: RemoteGraphQlEventTypeSchema
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
import { decodeJwt } from 'jose';
|
||||
|
||||
import type { ConnectSignInInput } from '@app/graphql/generated/api/types.js';
|
||||
import { getters, store } from '@app/store/index.js';
|
||||
import { loginUser } from '@app/store/modules/config.js';
|
||||
import { FileLoadStatus } from '@app/store/types.js';
|
||||
import { ApiKeyService } from '@app/unraid-api/auth/api-key.service.js';
|
||||
|
||||
export const connectSignIn = async (input: ConnectSignInInput): Promise<boolean> => {
|
||||
if (getters.emhttp().status === FileLoadStatus.LOADED) {
|
||||
const userInfo = input.idToken ? decodeJwt(input.idToken) : (input.userInfo ?? null);
|
||||
|
||||
if (
|
||||
!userInfo ||
|
||||
!userInfo.preferred_username ||
|
||||
!userInfo.email ||
|
||||
typeof userInfo.preferred_username !== 'string' ||
|
||||
typeof userInfo.email !== 'string'
|
||||
) {
|
||||
throw new Error('Missing User Attributes');
|
||||
}
|
||||
|
||||
try {
|
||||
const { remote } = getters.config();
|
||||
const { localApiKey: localApiKeyFromConfig } = remote;
|
||||
|
||||
let localApiKeyToUse = localApiKeyFromConfig;
|
||||
|
||||
if (localApiKeyFromConfig == '') {
|
||||
const apiKeyService = new ApiKeyService();
|
||||
// Create local API key
|
||||
const localApiKey = await apiKeyService.createLocalConnectApiKey();
|
||||
|
||||
if (!localApiKey?.key) {
|
||||
throw new Error('Failed to create local API key');
|
||||
}
|
||||
|
||||
localApiKeyToUse = localApiKey.key;
|
||||
}
|
||||
|
||||
await store.dispatch(
|
||||
loginUser({
|
||||
avatar: typeof userInfo.avatar === 'string' ? userInfo.avatar : '',
|
||||
username: userInfo.preferred_username,
|
||||
email: userInfo.email,
|
||||
apikey: input.apiKey,
|
||||
localApiKey: localApiKeyToUse,
|
||||
})
|
||||
);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to login user: ${error}`);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import { logger } from '@app/core/log.js';
|
||||
import { type ApiKeyResponse } from '@app/graphql/generated/api/types.js';
|
||||
import { type ApiKeyResponse } from '@app/unraid-api/graph/resolvers/cloud/cloud.model.js';
|
||||
|
||||
export const checkApi = async (): Promise<ApiKeyResponse> => {
|
||||
logger.trace('Cloud endpoint: Checking API');
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { got } from 'got';
|
||||
|
||||
import type { CloudResponse } from '@app/graphql/generated/api/types.js';
|
||||
import { FIVE_DAYS_SECS, ONE_DAY_SECS } from '@app/consts.js';
|
||||
import { logger } from '@app/core/log.js';
|
||||
import { API_VERSION, MOTHERSHIP_GRAPHQL_LINK } from '@app/environment.js';
|
||||
import { MinigraphStatus } from '@app/graphql/generated/api/types.js';
|
||||
import { checkDNS } from '@app/graphql/resolvers/query/cloud/check-dns.js';
|
||||
import { checkMothershipAuthentication } from '@app/graphql/resolvers/query/cloud/check-mothership-authentication.js';
|
||||
import { getCloudCache, getDnsCache } from '@app/store/getters/index.js';
|
||||
import { getters, store } from '@app/store/index.js';
|
||||
import { setCloudCheck, setDNSCheck } from '@app/store/modules/cache.js';
|
||||
import { CloudResponse, MinigraphStatus } from '@app/unraid-api/graph/resolvers/cloud/cloud.model.js';
|
||||
|
||||
const mothershipBaseUrl = new URL(MOTHERSHIP_GRAPHQL_LINK).origin;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { logger } from '@app/core/log.js';
|
||||
import { type MinigraphqlResponse } from '@app/graphql/generated/api/types.js';
|
||||
import { getters } from '@app/store/index.js';
|
||||
import { MinigraphqlResponse } from '@app/unraid-api/graph/resolvers/cloud/cloud.model.js';
|
||||
|
||||
export const checkMinigraphql = (): MinigraphqlResponse => {
|
||||
logger.trace('Cloud endpoint: Checking mini-graphql');
|
||||
|
||||
@@ -19,20 +19,20 @@ import { sanitizeVendor } from '@app/core/utils/vms/domain/sanitize-vendor.js';
|
||||
import { vmRegExps } from '@app/core/utils/vms/domain/vm-regexps.js';
|
||||
import { filterDevices } from '@app/core/utils/vms/filter-devices.js';
|
||||
import { getPciDevices } from '@app/core/utils/vms/get-pci-devices.js';
|
||||
import {
|
||||
type Devices,
|
||||
type Display,
|
||||
type Gpu,
|
||||
type InfoApps,
|
||||
type InfoCpu,
|
||||
type InfoMemory,
|
||||
type Os as InfoOs,
|
||||
type MemoryLayout,
|
||||
type Temperature,
|
||||
type Theme,
|
||||
type Versions,
|
||||
} from '@app/graphql/generated/api/types.js';
|
||||
import { getters } from '@app/store/index.js';
|
||||
import {
|
||||
Devices,
|
||||
Display,
|
||||
Gpu,
|
||||
InfoApps,
|
||||
InfoCpu,
|
||||
InfoMemory,
|
||||
Os as InfoOs,
|
||||
MemoryLayout,
|
||||
Temperature,
|
||||
Theme,
|
||||
Versions,
|
||||
} from '@app/unraid-api/graph/resolvers/info/info.model.js';
|
||||
|
||||
export const generateApps = async (): Promise<InfoApps> => {
|
||||
const installed = await docker
|
||||
@@ -43,13 +43,14 @@ export const generateApps = async (): Promise<InfoApps> => {
|
||||
.listContainers()
|
||||
.catch(() => [])
|
||||
.then((containers) => containers.length);
|
||||
return { installed, started };
|
||||
return { id: 'info/apps', installed, started };
|
||||
};
|
||||
|
||||
export const generateOs = async (): Promise<InfoOs> => {
|
||||
const os = await osInfo();
|
||||
|
||||
return {
|
||||
id: 'info/os',
|
||||
...os,
|
||||
hostname: getters.emhttp().var.name,
|
||||
uptime: bootTimestamp.toISOString(),
|
||||
@@ -63,6 +64,7 @@ export const generateCpu = async (): Promise<InfoCpu> => {
|
||||
.catch(() => []);
|
||||
|
||||
return {
|
||||
id: 'info/cpu',
|
||||
...rest,
|
||||
cores: physicalCores,
|
||||
threads: cores,
|
||||
@@ -94,8 +96,8 @@ export const generateDisplay = async (): Promise<Display> => {
|
||||
}
|
||||
const { theme, unit, ...display } = state.display;
|
||||
return {
|
||||
...display,
|
||||
id: 'dynamix-config/display',
|
||||
...display,
|
||||
theme: theme as Theme,
|
||||
unit: unit as Temperature,
|
||||
scale: toBoolean(display.scale),
|
||||
@@ -118,6 +120,7 @@ export const generateVersions = async (): Promise<Versions> => {
|
||||
const softwareVersions = await versions();
|
||||
|
||||
return {
|
||||
id: 'info/versions',
|
||||
unraid,
|
||||
...softwareVersions,
|
||||
};
|
||||
@@ -165,6 +168,7 @@ export const generateMemory = async (): Promise<InfoMemory> => {
|
||||
}
|
||||
|
||||
return {
|
||||
id: 'info/memory',
|
||||
layout,
|
||||
max,
|
||||
...info,
|
||||
@@ -410,10 +414,9 @@ export const generateDevices = async (): Promise<Devices> => {
|
||||
};
|
||||
|
||||
return {
|
||||
id: 'info/devices',
|
||||
// Scsi: await scsiDevices,
|
||||
gpu: await systemGPUDevices,
|
||||
// Move this to interfaces
|
||||
// network: await si.networkInterfaces(),
|
||||
pci: await systemPciDevices(),
|
||||
usb: await getSystemUSBDevices(),
|
||||
};
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import type { AccessUrlInput } from '@app/graphql/generated/client/graphql.js';
|
||||
import type { RootState } from '@app/store/index.js';
|
||||
import { logger } from '@app/core/log.js';
|
||||
import { type Nginx } from '@app/core/types/states/nginx.js';
|
||||
import { type AccessUrl } from '@app/graphql/generated/api/types.js';
|
||||
import { URL_TYPE } from '@app/graphql/generated/client/graphql.js';
|
||||
import { AccessUrlInputSchema } from '@app/graphql/generated/client/validators.js';
|
||||
import { store } from '@app/store/index.js';
|
||||
import { AccessUrl, URL_TYPE } from '@app/unraid-api/graph/resolvers/connect/connect.model.js';
|
||||
|
||||
interface UrlForFieldInput {
|
||||
url: string;
|
||||
@@ -126,7 +123,7 @@ export const getServerIps = (
|
||||
}
|
||||
|
||||
const errors: Error[] = [];
|
||||
const urls: AccessUrlInput[] = [];
|
||||
const urls: AccessUrl[] = [];
|
||||
|
||||
try {
|
||||
// Default URL
|
||||
@@ -232,16 +229,5 @@ export const getServerIps = (
|
||||
}
|
||||
});
|
||||
|
||||
const safeUrls = urls
|
||||
.map((url) => AccessUrlInputSchema().safeParse(url))
|
||||
.reduce<AccessUrlInput[]>((acc, curr) => {
|
||||
if (curr.success) {
|
||||
acc.push(curr.data);
|
||||
} else {
|
||||
errors.push(curr.error);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
return { urls: safeUrls, errors };
|
||||
return { urls, errors };
|
||||
};
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import type { RemoteGraphQLEventFragmentFragment } from '@app/graphql/generated/client/graphql.js';
|
||||
import type { RemoteGraphQlEventFragmentFragment } from '@app/graphql/generated/client/graphql.js';
|
||||
import { remoteQueryLogger } from '@app/core/log.js';
|
||||
import { getApiApolloClient } from '@app/graphql/client/api/get-api-client.js';
|
||||
import { RemoteGraphQLEventType } from '@app/graphql/generated/client/graphql.js';
|
||||
import { RemoteGraphQlEventType } from '@app/graphql/generated/client/graphql.js';
|
||||
import { SEND_REMOTE_QUERY_RESPONSE } from '@app/graphql/mothership/mutations.js';
|
||||
import { parseGraphQLQuery } from '@app/graphql/resolvers/subscription/remote-graphql/remote-graphql-helpers.js';
|
||||
import { GraphQLClient } from '@app/mothership/graphql-client.js';
|
||||
import { getters } from '@app/store/index.js';
|
||||
|
||||
export const executeRemoteGraphQLQuery = async (
|
||||
data: RemoteGraphQLEventFragmentFragment['remoteGraphQLEventData']
|
||||
data: RemoteGraphQlEventFragmentFragment['remoteGraphQLEventData']
|
||||
) => {
|
||||
remoteQueryLogger.debug({ query: data }, 'Executing remote query');
|
||||
const client = GraphQLClient.getInstance();
|
||||
@@ -44,7 +44,7 @@ export const executeRemoteGraphQLQuery = async (
|
||||
input: {
|
||||
sha256: data.sha256,
|
||||
body: JSON.stringify({ data: localResult.data }),
|
||||
type: RemoteGraphQLEventType.REMOTE_QUERY_EVENT,
|
||||
type: RemoteGraphQlEventType.REMOTE_QUERY_EVENT,
|
||||
},
|
||||
},
|
||||
errorPolicy: 'none',
|
||||
@@ -57,7 +57,7 @@ export const executeRemoteGraphQLQuery = async (
|
||||
input: {
|
||||
sha256: data.sha256,
|
||||
body: JSON.stringify({ errors: localResult.error }),
|
||||
type: RemoteGraphQLEventType.REMOTE_QUERY_EVENT,
|
||||
type: RemoteGraphQlEventType.REMOTE_QUERY_EVENT,
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -70,7 +70,7 @@ export const executeRemoteGraphQLQuery = async (
|
||||
input: {
|
||||
sha256: data.sha256,
|
||||
body: JSON.stringify({ errors: err }),
|
||||
type: RemoteGraphQLEventType.REMOTE_QUERY_EVENT,
|
||||
type: RemoteGraphQlEventType.REMOTE_QUERY_EVENT,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { type RemoteGraphQLEventFragmentFragment } from '@app/graphql/generated/client/graphql.js';
|
||||
import { type RemoteGraphQlEventFragmentFragment } from '@app/graphql/generated/client/graphql.js';
|
||||
import { addRemoteSubscription } from '@app/store/actions/add-remote-subscription.js';
|
||||
import { store } from '@app/store/index.js';
|
||||
|
||||
export const createRemoteSubscription = async (
|
||||
data: RemoteGraphQLEventFragmentFragment['remoteGraphQLEventData']
|
||||
data: RemoteGraphQlEventFragmentFragment['remoteGraphQLEventData']
|
||||
) => {
|
||||
await store.dispatch(addRemoteSubscription(data));
|
||||
};
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
type Permission {
|
||||
resource: Resource!
|
||||
actions: [String!]!
|
||||
}
|
||||
|
||||
type ApiKey {
|
||||
id: ID!
|
||||
name: String!
|
||||
description: String
|
||||
roles: [Role!]!
|
||||
createdAt: DateTime!
|
||||
permissions: [Permission!]!
|
||||
}
|
||||
|
||||
type ApiKeyWithSecret {
|
||||
id: ID!
|
||||
key: String!
|
||||
name: String!
|
||||
description: String
|
||||
roles: [Role!]!
|
||||
createdAt: DateTime!
|
||||
permissions: [Permission!]!
|
||||
}
|
||||
|
||||
input CreateApiKeyInput {
|
||||
name: String!
|
||||
description: String
|
||||
roles: [Role!]
|
||||
permissions: [AddPermissionInput!]
|
||||
""" This will replace the existing key if one already exists with the same name, otherwise returns the existing key """
|
||||
overwrite: Boolean
|
||||
}
|
||||
|
||||
input AddPermissionInput {
|
||||
resource: Resource!
|
||||
actions: [String!]!
|
||||
}
|
||||
|
||||
input AddRoleForUserInput {
|
||||
userId: ID!
|
||||
role: Role!
|
||||
}
|
||||
|
||||
input AddRoleForApiKeyInput {
|
||||
apiKeyId: ID!
|
||||
role: Role!
|
||||
}
|
||||
|
||||
input RemoveRoleFromApiKeyInput {
|
||||
apiKeyId: ID!
|
||||
role: Role!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
createApiKey(input: CreateApiKeyInput!): ApiKeyWithSecret!
|
||||
addPermission(input: AddPermissionInput!): Boolean!
|
||||
addRoleForUser(input: AddRoleForUserInput!): Boolean!
|
||||
addRoleForApiKey(input: AddRoleForApiKeyInput!): Boolean!
|
||||
removeRoleFromApiKey(input: RemoveRoleFromApiKeyInput!): Boolean!
|
||||
}
|
||||
|
||||
type Query {
|
||||
apiKeys: [ApiKey!]!
|
||||
apiKey(id: ID!): ApiKey
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
"""
|
||||
Available resources for permissions
|
||||
"""
|
||||
enum Resource {
|
||||
API_KEY
|
||||
ARRAY
|
||||
CLOUD
|
||||
CONFIG
|
||||
CONNECT
|
||||
CONNECT__REMOTE_ACCESS
|
||||
CUSTOMIZATIONS
|
||||
DASHBOARD
|
||||
DISK
|
||||
DISPLAY
|
||||
DOCKER
|
||||
FLASH
|
||||
INFO
|
||||
LOGS
|
||||
ME
|
||||
NETWORK
|
||||
NOTIFICATIONS
|
||||
ONLINE
|
||||
OS
|
||||
OWNER
|
||||
PERMISSION
|
||||
REGISTRATION
|
||||
SERVERS
|
||||
SERVICES
|
||||
SHARE
|
||||
VARS
|
||||
VMS
|
||||
WELCOME
|
||||
}
|
||||
|
||||
"""
|
||||
Available roles for API keys and users
|
||||
"""
|
||||
enum Role {
|
||||
ADMIN
|
||||
CONNECT
|
||||
GUEST
|
||||
}
|
||||
@@ -1,214 +0,0 @@
|
||||
type Query {
|
||||
"""An Unraid array consisting of 1 or 2 Parity disks and a number of Data disks."""
|
||||
array: Array!
|
||||
}
|
||||
|
||||
enum ArrayStateInputState {
|
||||
"""Start array"""
|
||||
START
|
||||
"""Stop array"""
|
||||
STOP
|
||||
}
|
||||
|
||||
input ArrayStateInput {
|
||||
"""Array state"""
|
||||
desiredState: ArrayStateInputState!
|
||||
}
|
||||
|
||||
type ArrayMutations {
|
||||
"""Set array state"""
|
||||
setState(input: ArrayStateInput): Array
|
||||
|
||||
"""Add new disk to array"""
|
||||
addDiskToArray(input: ArrayDiskInput): Array
|
||||
"""Remove existing disk from array. NOTE: The array must be stopped before running this otherwise it'll throw an error."""
|
||||
removeDiskFromArray(input: ArrayDiskInput): Array
|
||||
|
||||
mountArrayDisk(id: ID!): Disk
|
||||
unmountArrayDisk(id: ID!): Disk
|
||||
|
||||
clearArrayDiskStatistics(id: ID!): JSON
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
array: ArrayMutations
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
array: Array!
|
||||
}
|
||||
|
||||
input ArrayDiskInput {
|
||||
"""Disk ID"""
|
||||
id: ID!
|
||||
"""The slot for the disk"""
|
||||
slot: Int
|
||||
}
|
||||
|
||||
type Array implements Node {
|
||||
id: ID!
|
||||
"""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: ArrayDisk
|
||||
"""Parity disks in the current array"""
|
||||
parities: [ArrayDisk!]!
|
||||
"""Data disks in the current array"""
|
||||
disks: [ArrayDisk!]!
|
||||
"""Caches in the current array"""
|
||||
caches: [ArrayDisk!]!
|
||||
}
|
||||
|
||||
# /usr/src/linux-5.9.13-Unraid/drivers/md/md_unraid.c
|
||||
enum ArrayState {
|
||||
"""Array is running"""
|
||||
STARTED
|
||||
"""Array has stopped"""
|
||||
STOPPED
|
||||
"""Array has new disks"""
|
||||
NEW_ARRAY
|
||||
"""A disk is being reconstructed"""
|
||||
RECON_DISK
|
||||
"""A disk is disabled in the array"""
|
||||
DISABLE_DISK
|
||||
"""Array is disabled"""
|
||||
SWAP_DSBL
|
||||
"""Too many changes to array at the same time"""
|
||||
INVALID_EXPANSION
|
||||
"""Parity isn't the biggest, can't start array"""
|
||||
PARITY_NOT_BIGGEST
|
||||
"""Array has too many missing data disks"""
|
||||
TOO_MANY_MISSING_DISKS
|
||||
"""Array has new disks they're too small"""
|
||||
NEW_DISK_TOO_SMALL
|
||||
"""Array has no data disks"""
|
||||
NO_DATA_DISKS
|
||||
}
|
||||
|
||||
enum ArrayDiskStatus {
|
||||
""" no disk present, no disk configured """
|
||||
DISK_NP
|
||||
""" enabled, disk present, correct, valid """
|
||||
DISK_OK
|
||||
""" enabled, but missing """
|
||||
DISK_NP_MISSING
|
||||
""" enabled, disk present, but not valid """
|
||||
DISK_INVALID
|
||||
""" enablled, disk present, but not correct disk """
|
||||
DISK_WRONG
|
||||
""" disabled, old disk still present """
|
||||
DISK_DSBL
|
||||
""" disabled, no disk present """
|
||||
DISK_NP_DSBL
|
||||
""" disabled, new disk present """
|
||||
DISK_DSBL_NEW
|
||||
""" new disk """
|
||||
DISK_NEW
|
||||
}
|
||||
|
||||
enum ArrayPendingState {
|
||||
"""Array is starting"""
|
||||
starting
|
||||
"""Array is stopping"""
|
||||
stopping
|
||||
"""Array has no data disks"""
|
||||
no_data_disks
|
||||
"""Array has too many missing data disks"""
|
||||
too_many_missing_disks
|
||||
}
|
||||
|
||||
type ArrayCapacity {
|
||||
kilobytes: Capacity!
|
||||
disks: Capacity!
|
||||
}
|
||||
|
||||
type Capacity {
|
||||
free: String!
|
||||
used: String!
|
||||
total: String!
|
||||
}
|
||||
|
||||
type ArrayDisk {
|
||||
""" Disk indentifier, only set for present disks on the system """
|
||||
id: ID!
|
||||
""" 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: Int!
|
||||
name: String
|
||||
device: String
|
||||
""" (KB) Disk Size total """
|
||||
size: Long!
|
||||
status: ArrayDiskStatus
|
||||
""" Is the disk a HDD or SSD. """
|
||||
rotational: Boolean
|
||||
""" Disk temp - will be NaN if array is not started or DISK_NP """
|
||||
temp: Int
|
||||
"""Count of I/O read requests sent to the device I/O drivers. These statistics may be cleared at any time."""
|
||||
numReads: Long!
|
||||
"""Count of I/O writes requests sent to the device I/O drivers. These statistics may be cleared at any time."""
|
||||
numWrites: Long!
|
||||
"""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: Long!
|
||||
""" (KB) Total Size of the FS (Not present on Parity type drive) """
|
||||
fsSize: Long
|
||||
""" (KB) Free Size on the FS (Not present on Parity type drive)"""
|
||||
fsFree: Long
|
||||
""" (KB) Used Size on the FS (Not present on Parity type drive)"""
|
||||
fsUsed: Long
|
||||
exportable: Boolean
|
||||
""" Type of Disk - used to differentiate Cache / Flash / Array / Parity """
|
||||
type: ArrayDiskType!
|
||||
""" (%) Disk space left to warn """
|
||||
warning: Int
|
||||
""" (%) Disk space left for critical """
|
||||
critical: Int
|
||||
""" File system type for the disk """
|
||||
fsType: String
|
||||
""" User comment on disk """
|
||||
comment: String
|
||||
""" File format (ex MBR: 4KiB-aligned) """
|
||||
format: String
|
||||
""" ata | nvme | usb | (others)"""
|
||||
transport: String
|
||||
color: ArrayDiskFsColor
|
||||
}
|
||||
|
||||
# type ArrayParityDisk {}
|
||||
# type ArrayCacheDisk {}
|
||||
|
||||
enum ArrayDiskType {
|
||||
"""Data disk"""
|
||||
Data
|
||||
"""Parity disk"""
|
||||
Parity
|
||||
"""Flash disk"""
|
||||
Flash
|
||||
"""Cache disk"""
|
||||
Cache
|
||||
}
|
||||
|
||||
enum ArrayDiskFsColor {
|
||||
"""Normal operation, device is active"""
|
||||
green_on
|
||||
"""Device is in standby mode (spun-down)"""
|
||||
green_blink
|
||||
"""New device"""
|
||||
blue_on
|
||||
"""New device, in standby mode (spun-down)"""
|
||||
blue_blink
|
||||
"""Device contents invalid or emulated / Parity is invalid"""
|
||||
yellow_on
|
||||
"""Device contents invalid or emulated / Parity is invalid, in standby mode (spun-down)"""
|
||||
yellow_blink
|
||||
"""Device is disabled or contents emulated / Parity device is disabled"""
|
||||
red_on
|
||||
"""Device is missing (disabled) or contents emulated / Parity device is missing"""
|
||||
red_off
|
||||
"""Device not present"""
|
||||
grey_off
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
type Query {
|
||||
parityHistory: [ParityCheck]
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
"""Start parity check"""
|
||||
startParityCheck(correct: Boolean): JSON
|
||||
"""Pause parity check"""
|
||||
pauseParityCheck: JSON
|
||||
"""Resume parity check"""
|
||||
resumeParityCheck: JSON
|
||||
"""Cancel parity check"""
|
||||
cancelParityCheck: JSON
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
parityHistory: ParityCheck!
|
||||
}
|
||||
|
||||
type ParityCheck {
|
||||
date: String!
|
||||
duration: Int!
|
||||
speed: String!
|
||||
status: String!
|
||||
errors: String!
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
scalar JSON
|
||||
scalar Long
|
||||
scalar UUID
|
||||
scalar DateTime
|
||||
scalar Port
|
||||
scalar URL
|
||||
|
||||
directive @auth(action: AuthActionVerb!, resource: Resource!, possession: AuthPossession!) on FIELD_DEFINITION
|
||||
|
||||
type Welcome {
|
||||
message: String!
|
||||
}
|
||||
|
||||
type Query {
|
||||
# This should always be available even for guest users
|
||||
online: Boolean
|
||||
info: Info
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
login(username: String!, password: String!): String
|
||||
shutdown: String
|
||||
reboot: String
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
ping: String!
|
||||
info: Info!
|
||||
online: Boolean!
|
||||
}
|
||||
|
||||
# An object with a Globally Unique ID: see https://graphql.org/learn/global-object-identification/
|
||||
interface Node {
|
||||
id: ID!
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
type ApiKeyResponse {
|
||||
valid: Boolean!
|
||||
error: String
|
||||
}
|
||||
|
||||
enum MinigraphStatus {
|
||||
PRE_INIT
|
||||
CONNECTING
|
||||
CONNECTED
|
||||
PING_FAILURE
|
||||
ERROR_RETRYING
|
||||
}
|
||||
|
||||
type MinigraphqlResponse {
|
||||
status: MinigraphStatus!
|
||||
timeout: Int
|
||||
error: String
|
||||
}
|
||||
|
||||
type CloudResponse {
|
||||
status: String!
|
||||
ip: String
|
||||
error: String
|
||||
}
|
||||
|
||||
type RelayResponse {
|
||||
status: String!
|
||||
timeout: String
|
||||
error: String
|
||||
}
|
||||
|
||||
type Cloud {
|
||||
error: String
|
||||
apiKey: ApiKeyResponse!
|
||||
relay: RelayResponse
|
||||
minigraphql: MinigraphqlResponse!
|
||||
cloud: CloudResponse!
|
||||
allowedOrigins: [String!]!
|
||||
}
|
||||
|
||||
type Query {
|
||||
cloud: Cloud
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
type Config implements Node {
|
||||
id: ID!
|
||||
valid: Boolean
|
||||
error: ConfigErrorState
|
||||
}
|
||||
|
||||
type Query {
|
||||
config: Config!
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
config: Config!
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
input ConnectUserInfoInput {
|
||||
preferred_username: String!
|
||||
email: String!
|
||||
avatar: String
|
||||
}
|
||||
|
||||
input ConnectSignInInput {
|
||||
apiKey: String!
|
||||
idToken: String
|
||||
userInfo: ConnectUserInfoInput
|
||||
accessToken: String
|
||||
refreshToken: String
|
||||
}
|
||||
|
||||
input AllowedOriginInput {
|
||||
origins: [String!]!
|
||||
}
|
||||
|
||||
enum WAN_ACCESS_TYPE {
|
||||
DYNAMIC
|
||||
ALWAYS
|
||||
DISABLED
|
||||
}
|
||||
|
||||
enum WAN_FORWARD_TYPE {
|
||||
UPNP
|
||||
STATIC
|
||||
}
|
||||
|
||||
type RemoteAccess {
|
||||
accessType: WAN_ACCESS_TYPE!
|
||||
forwardType: WAN_FORWARD_TYPE
|
||||
port: Port
|
||||
}
|
||||
|
||||
input SetupRemoteAccessInput {
|
||||
accessType: WAN_ACCESS_TYPE!
|
||||
forwardType: WAN_FORWARD_TYPE
|
||||
port: Port
|
||||
}
|
||||
|
||||
input EnableDynamicRemoteAccessInput {
|
||||
url: AccessUrlInput!
|
||||
enabled: Boolean!
|
||||
}
|
||||
|
||||
enum DynamicRemoteAccessType {
|
||||
STATIC
|
||||
UPNP
|
||||
DISABLED
|
||||
}
|
||||
|
||||
type DynamicRemoteAccessStatus {
|
||||
enabledType: DynamicRemoteAccessType!
|
||||
runningType: DynamicRemoteAccessType!
|
||||
error: String
|
||||
}
|
||||
|
||||
"""
|
||||
Intersection type of ApiSettings and RemoteAccess
|
||||
"""
|
||||
type ConnectSettingsValues {
|
||||
"""
|
||||
If true, the GraphQL sandbox is enabled and available at /graphql.
|
||||
If false, the GraphQL sandbox is disabled and only the production API will be available.
|
||||
"""
|
||||
sandbox: Boolean!
|
||||
"""
|
||||
A list of origins allowed to interact with the API.
|
||||
"""
|
||||
extraOrigins: [String!]!
|
||||
"""
|
||||
The type of WAN access used for Remote Access.
|
||||
"""
|
||||
accessType: WAN_ACCESS_TYPE!
|
||||
"""
|
||||
The type of port forwarding used for Remote Access.
|
||||
"""
|
||||
forwardType: WAN_FORWARD_TYPE
|
||||
"""
|
||||
The port used for Remote Access.
|
||||
"""
|
||||
port: Port
|
||||
"""
|
||||
A list of Unique Unraid Account ID's.
|
||||
"""
|
||||
ssoUserIds: [String!]!
|
||||
}
|
||||
|
||||
"""
|
||||
Input should be a subset of ApiSettings that can be updated.
|
||||
Some field combinations may be required or disallowed. Please refer to each field for more information.
|
||||
"""
|
||||
input ApiSettingsInput {
|
||||
"""
|
||||
If true, the GraphQL sandbox will be enabled and available at /graphql.
|
||||
If false, the GraphQL sandbox will be disabled and only the production API will be available.
|
||||
"""
|
||||
sandbox: Boolean
|
||||
"""
|
||||
A list of origins allowed to interact with the API.
|
||||
"""
|
||||
extraOrigins: [String!]
|
||||
"""
|
||||
The type of WAN access to use for Remote Access.
|
||||
"""
|
||||
accessType: WAN_ACCESS_TYPE
|
||||
"""
|
||||
The type of port forwarding to use for Remote Access.
|
||||
"""
|
||||
forwardType: WAN_FORWARD_TYPE
|
||||
"""
|
||||
The port to use for Remote Access. Not required for UPNP forwardType. Required for STATIC forwardType.
|
||||
Ignored if accessType is DISABLED or forwardType is UPNP.
|
||||
"""
|
||||
port: Port
|
||||
"""
|
||||
A list of Unique Unraid Account ID's.
|
||||
"""
|
||||
ssoUserIds: [String!]
|
||||
}
|
||||
|
||||
type ConnectSettings implements Node {
|
||||
id: ID!
|
||||
dataSchema: JSON!
|
||||
uiSchema: JSON!
|
||||
values: ConnectSettingsValues!
|
||||
}
|
||||
|
||||
type Connect implements Node {
|
||||
id: ID!
|
||||
dynamicRemoteAccess: DynamicRemoteAccessStatus!
|
||||
settings: ConnectSettings!
|
||||
}
|
||||
|
||||
type Query {
|
||||
remoteAccess: RemoteAccess!
|
||||
extraAllowedOrigins: [String!]!
|
||||
connect: Connect!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
connectSignIn(input: ConnectSignInInput!): Boolean!
|
||||
connectSignOut: Boolean!
|
||||
enableDynamicRemoteAccess(input: EnableDynamicRemoteAccessInput!): Boolean!
|
||||
setAdditionalAllowedOrigins(input: AllowedOriginInput!): [String!]!
|
||||
setupRemoteAccess(input: SetupRemoteAccessInput!): Boolean!
|
||||
"""
|
||||
Update the API settings.
|
||||
Some setting combinations may be required or disallowed. Please refer to each setting for more information.
|
||||
"""
|
||||
updateApiSettings(input: ApiSettingsInput!): ConnectSettingsValues!
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
type Query {
|
||||
"""Single disk"""
|
||||
disk(id: ID!): Disk
|
||||
"""Mulitiple disks"""
|
||||
disks: [Disk]!
|
||||
}
|
||||
|
||||
type Disk {
|
||||
id: ID!
|
||||
# /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
|
||||
zfs
|
||||
ext4
|
||||
ntfs
|
||||
}
|
||||
|
||||
enum DiskInterfaceType {
|
||||
SAS
|
||||
SATA
|
||||
USB
|
||||
PCIe
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
enum DiskSmartStatus {
|
||||
OK
|
||||
UNKNOWN
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
type Query {
|
||||
display: Display
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
display: Display
|
||||
}
|
||||
|
||||
type Display {
|
||||
id: ID!
|
||||
case: Case
|
||||
}
|
||||
|
||||
type Case {
|
||||
icon: String
|
||||
url: String
|
||||
error: String
|
||||
base64: String
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
|
||||
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!]!
|
||||
""" (B) Total size of all the files in the container """
|
||||
sizeRootFs: Long
|
||||
labels: JSON
|
||||
state: ContainerState!
|
||||
status: String!
|
||||
hostConfig: ContainerHostConfig
|
||||
networkSettings: JSON
|
||||
mounts: [JSON]
|
||||
autoStart: Boolean!
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
type Docker implements Node {
|
||||
id: ID!
|
||||
containers: [DockerContainer!]
|
||||
networks: [DockerNetwork!]
|
||||
}
|
||||
|
||||
type Query {
|
||||
docker: Docker!
|
||||
}
|
||||
|
||||
type DockerMutations {
|
||||
""" Stop a container """
|
||||
stop(id: ID!): DockerContainer!
|
||||
""" Start a container """
|
||||
start(id: ID!): DockerContainer!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
docker: DockerMutations
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
type Query {
|
||||
"""Docker network"""
|
||||
dockerNetwork(id: ID!): DockerNetwork!
|
||||
"""All Docker networks"""
|
||||
dockerNetworks(all: Boolean): [DockerNetwork]!
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
dockerNetwork(id: ID!): DockerNetwork!
|
||||
dockerNetworks: [DockerNetwork]!
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
type Query {
|
||||
flash: Flash
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
flash: Flash!
|
||||
}
|
||||
|
||||
type Flash {
|
||||
guid: String
|
||||
vendor: String
|
||||
product: String
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
type Info {
|
||||
"""Count of docker containers"""
|
||||
apps: InfoApps
|
||||
}
|
||||
|
||||
type InfoApps {
|
||||
"""How many docker containers are installed"""
|
||||
installed: Int
|
||||
"""How many docker containers are running"""
|
||||
started: Int
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
type Info {
|
||||
baseboard: Baseboard
|
||||
}
|
||||
|
||||
type Baseboard {
|
||||
# Dell Inc.
|
||||
manufacturer: String!
|
||||
# 0MD99X
|
||||
model: String
|
||||
# A07
|
||||
version: String
|
||||
serial: String
|
||||
assetTag: String
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
type Info {
|
||||
cpu: InfoCpu
|
||||
}
|
||||
|
||||
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!]
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
type Info {
|
||||
devices: 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
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
type Info {
|
||||
display: Display
|
||||
}
|
||||
|
||||
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
|
||||
locale: String
|
||||
}
|
||||
|
||||
enum Temperature {
|
||||
C
|
||||
F
|
||||
}
|
||||
|
||||
enum Theme {
|
||||
white
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
type Info implements Node {
|
||||
id: ID!
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
type Info {
|
||||
"""Machine ID"""
|
||||
machineId: ID
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
type Info {
|
||||
memory: InfoMemory
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
type Info {
|
||||
os: Os
|
||||
}
|
||||
|
||||
type Os {
|
||||
platform: String
|
||||
distro: String
|
||||
release: String
|
||||
codename: String
|
||||
kernel: String
|
||||
arch: String
|
||||
hostname: String
|
||||
codepage: String
|
||||
logofile: String
|
||||
serial: String
|
||||
build: String
|
||||
uptime: DateTime
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
type Info {
|
||||
system: System
|
||||
}
|
||||
|
||||
type System {
|
||||
manufacturer: String
|
||||
model: String
|
||||
version: String
|
||||
serial: String
|
||||
uuid: String
|
||||
sku: String
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
type Info {
|
||||
time: DateTime!
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
type Info {
|
||||
versions: Versions
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
type Query {
|
||||
"""
|
||||
List all available log files
|
||||
"""
|
||||
logFiles: [LogFile!]!
|
||||
|
||||
"""
|
||||
Get the content of a specific log file
|
||||
@param path Path to the log file
|
||||
@param lines Number of lines to read from the end of the file (default: 100)
|
||||
@param startLine Optional starting line number (1-indexed)
|
||||
"""
|
||||
logFile(path: String!, lines: Int, startLine: Int): LogFileContent!
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
"""
|
||||
Subscribe to changes in a log file
|
||||
@param path Path to the log file
|
||||
"""
|
||||
logFile(path: String!): LogFileContent!
|
||||
}
|
||||
|
||||
"""
|
||||
Represents a log file in the system
|
||||
"""
|
||||
type LogFile {
|
||||
"""
|
||||
Name of the log file
|
||||
"""
|
||||
name: String!
|
||||
|
||||
"""
|
||||
Full path to the log file
|
||||
"""
|
||||
path: String!
|
||||
|
||||
"""
|
||||
Size of the log file in bytes
|
||||
"""
|
||||
size: Int!
|
||||
|
||||
"""
|
||||
Last modified timestamp
|
||||
"""
|
||||
modifiedAt: DateTime!
|
||||
}
|
||||
|
||||
"""
|
||||
Content of a log file
|
||||
"""
|
||||
type LogFileContent {
|
||||
"""
|
||||
Path to the log file
|
||||
"""
|
||||
path: String!
|
||||
|
||||
"""
|
||||
Content of the log file
|
||||
"""
|
||||
content: String!
|
||||
|
||||
"""
|
||||
Total number of lines in the file
|
||||
"""
|
||||
totalLines: Int!
|
||||
|
||||
"""
|
||||
Starting line number of the content (1-indexed)
|
||||
"""
|
||||
startLine: Int
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
enum URL_TYPE {
|
||||
LAN
|
||||
WIREGUARD
|
||||
WAN
|
||||
MDNS
|
||||
OTHER
|
||||
DEFAULT
|
||||
}
|
||||
|
||||
|
||||
input AccessUrlInput {
|
||||
type: URL_TYPE!
|
||||
name: String
|
||||
ipv4: URL
|
||||
ipv6: URL
|
||||
}
|
||||
|
||||
type AccessUrl {
|
||||
type: URL_TYPE!
|
||||
name: String
|
||||
ipv4: URL
|
||||
ipv6: URL
|
||||
}
|
||||
|
||||
type Query {
|
||||
network: Network
|
||||
}
|
||||
|
||||
type Network implements Node {
|
||||
id: ID!
|
||||
accessUrls: [AccessUrl!]
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
enum NotificationType {
|
||||
UNREAD
|
||||
ARCHIVE
|
||||
}
|
||||
|
||||
input NotificationFilter {
|
||||
importance: Importance
|
||||
type: NotificationType
|
||||
offset: Int!
|
||||
limit: Int!
|
||||
}
|
||||
|
||||
type Query {
|
||||
notifications: Notifications!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
createNotification(input: NotificationData!): Notification!
|
||||
deleteNotification(id: String!, type: NotificationType!): NotificationOverview!
|
||||
"""
|
||||
Deletes all archived notifications on server.
|
||||
"""
|
||||
deleteArchivedNotifications: NotificationOverview!
|
||||
"""
|
||||
Marks a notification as archived.
|
||||
"""
|
||||
archiveNotification(id: String!): Notification!
|
||||
"""
|
||||
Marks a notification as unread.
|
||||
"""
|
||||
unreadNotification(id: String!): Notification!
|
||||
archiveNotifications(ids: [String!]): NotificationOverview!
|
||||
unarchiveNotifications(ids: [String!]): NotificationOverview!
|
||||
archiveAll(importance: Importance): NotificationOverview!
|
||||
unarchiveAll(importance: Importance): NotificationOverview!
|
||||
"""
|
||||
Reads each notification to recompute & update the overview.
|
||||
"""
|
||||
recalculateOverview: NotificationOverview!
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
notificationAdded: Notification!
|
||||
notificationsOverview: NotificationOverview!
|
||||
}
|
||||
|
||||
enum Importance {
|
||||
ALERT
|
||||
INFO
|
||||
WARNING
|
||||
}
|
||||
|
||||
type Notifications implements Node {
|
||||
id: ID!
|
||||
"""
|
||||
A cached overview of the notifications in the system & their severity.
|
||||
"""
|
||||
overview: NotificationOverview!
|
||||
list(filter: NotificationFilter!): [Notification!]!
|
||||
}
|
||||
|
||||
type Notification implements Node {
|
||||
id: ID!
|
||||
"""
|
||||
Also known as 'event'
|
||||
"""
|
||||
title: String!
|
||||
subject: String!
|
||||
description: String!
|
||||
importance: Importance!
|
||||
link: String
|
||||
type: NotificationType!
|
||||
"""
|
||||
ISO Timestamp for when the notification occurred
|
||||
"""
|
||||
timestamp: String
|
||||
formattedTimestamp: String
|
||||
}
|
||||
|
||||
input NotificationData {
|
||||
title: String!
|
||||
subject: String!
|
||||
description: String!
|
||||
importance: Importance!
|
||||
link: String
|
||||
}
|
||||
|
||||
type NotificationOverview {
|
||||
unread: NotificationCounts!
|
||||
archive: NotificationCounts!
|
||||
}
|
||||
|
||||
type NotificationCounts {
|
||||
info: Int!
|
||||
warning: Int!
|
||||
alert: Int!
|
||||
total: Int!
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
type Query {
|
||||
owner: Owner
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
owner: Owner!
|
||||
}
|
||||
|
||||
type Owner {
|
||||
username: String
|
||||
url: String
|
||||
avatar: String
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
type Query {
|
||||
registration: Registration
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
registration: Registration!
|
||||
}
|
||||
|
||||
type KeyFile {
|
||||
location: String
|
||||
contents: String
|
||||
}
|
||||
|
||||
type Registration {
|
||||
guid: String
|
||||
type: registrationType
|
||||
keyFile: KeyFile
|
||||
state: RegistrationState
|
||||
expiration: String
|
||||
updateExpiration: String
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
type Query {
|
||||
server: Server
|
||||
servers: [Server!]!
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
server: Server
|
||||
}
|
||||
|
||||
enum ServerStatus {
|
||||
online
|
||||
offline
|
||||
never_connected
|
||||
}
|
||||
|
||||
|
||||
type ProfileModel {
|
||||
userId: ID
|
||||
username: String
|
||||
url: String
|
||||
avatar: String
|
||||
}
|
||||
|
||||
type Server {
|
||||
owner: ProfileModel!
|
||||
guid: String!
|
||||
apikey: String!
|
||||
name: String!
|
||||
status: ServerStatus!
|
||||
wanip: String!
|
||||
lanip: String!
|
||||
localurl: String!
|
||||
remoteurl: String!
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
type Subscription {
|
||||
service(name: String!): [Service!]
|
||||
}
|
||||
|
||||
type Uptime {
|
||||
timestamp: String
|
||||
}
|
||||
|
||||
type Service implements Node {
|
||||
id: ID!
|
||||
name: String
|
||||
online: Boolean
|
||||
uptime: Uptime
|
||||
version: String
|
||||
}
|
||||
|
||||
type Query {
|
||||
services: [Service!]!
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
type Query {
|
||||
"""Network Shares"""
|
||||
shares: [Share]
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
share(id: ID!): Share!
|
||||
shares: [Share!]
|
||||
}
|
||||
|
||||
"""Network Share"""
|
||||
type Share {
|
||||
"""Display name"""
|
||||
name: String
|
||||
"""(KB) Free space"""
|
||||
free: Long
|
||||
"""(KB) Used Size"""
|
||||
used: Long
|
||||
"""(KB) Total size"""
|
||||
size: Long
|
||||
"""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
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
type Mount {
|
||||
name: String
|
||||
directory: String
|
||||
type: String
|
||||
permissions: String
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
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
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
type Query {
|
||||
unassignedDevices: [UnassignedDevice]
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
unassignedDevices: [UnassignedDevice!]
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
type Query {
|
||||
"""
|
||||
Current user account
|
||||
"""
|
||||
me: Me
|
||||
}
|
||||
|
||||
"""
|
||||
The current user
|
||||
"""
|
||||
type Me implements UserAccount {
|
||||
id: ID!
|
||||
name: String!
|
||||
description: String!
|
||||
roles: [Role!]!
|
||||
permissions: [Permission!]
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
me: Me
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
interface UserAccount {
|
||||
id: ID!
|
||||
name: String!
|
||||
description: String!
|
||||
roles: [Role!]!
|
||||
permissions: [Permission!]
|
||||
}
|
||||
|
||||
input usersInput {
|
||||
slim: Boolean
|
||||
}
|
||||
|
||||
type Query {
|
||||
"""
|
||||
User account
|
||||
"""
|
||||
user(id: ID!): User
|
||||
"""
|
||||
User accounts
|
||||
"""
|
||||
users(input: usersInput): [User!]!
|
||||
}
|
||||
|
||||
input addUserInput {
|
||||
name: String!
|
||||
password: String!
|
||||
description: String
|
||||
}
|
||||
|
||||
input deleteUserInput {
|
||||
name: String!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
"""
|
||||
Add a new user
|
||||
"""
|
||||
addUser(input: addUserInput!): User
|
||||
"""
|
||||
Delete a user
|
||||
"""
|
||||
deleteUser(input: deleteUserInput!): User
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
user(id: ID!): User!
|
||||
users: [User]!
|
||||
}
|
||||
|
||||
"""
|
||||
A local user account
|
||||
"""
|
||||
type User implements UserAccount {
|
||||
id: ID!
|
||||
"""
|
||||
A unique name for the user
|
||||
"""
|
||||
name: String!
|
||||
description: String!
|
||||
roles: [Role!]!
|
||||
"""
|
||||
If the account has a password set
|
||||
"""
|
||||
password: Boolean
|
||||
permissions: [Permission!]
|
||||
}
|
||||
@@ -1,293 +0,0 @@
|
||||
type Query {
|
||||
vars: Vars
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
vars: Vars!
|
||||
}
|
||||
|
||||
enum ConfigErrorState {
|
||||
UNKNOWN_ERROR
|
||||
INELIGIBLE
|
||||
INVALID
|
||||
NO_KEY_SERVER
|
||||
WITHDRAWN
|
||||
}
|
||||
|
||||
type Vars implements Node {
|
||||
id: ID!
|
||||
"""
|
||||
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
|
||||
configError: ConfigErrorState
|
||||
joinStatus: String
|
||||
deviceCount: Int
|
||||
flashGuid: String
|
||||
flashProduct: String
|
||||
flashVendor: String
|
||||
regCheck: String
|
||||
regFile: String
|
||||
regGuid: String
|
||||
regTy: String
|
||||
regState: RegistrationState
|
||||
"""
|
||||
Registration 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
|
||||
}
|
||||
|
||||
enum mdState {
|
||||
SWAP_DSBL
|
||||
STARTED
|
||||
}
|
||||
|
||||
enum registrationType {
|
||||
BASIC
|
||||
PLUS
|
||||
PRO
|
||||
STARTER
|
||||
UNLEASHED
|
||||
LIFETIME
|
||||
INVALID
|
||||
TRIAL
|
||||
}
|
||||
|
||||
enum RegistrationState {
|
||||
TRIAL
|
||||
BASIC
|
||||
PLUS
|
||||
PRO
|
||||
STARTER
|
||||
UNLEASHED
|
||||
LIFETIME
|
||||
"""
|
||||
Trial Expired
|
||||
"""
|
||||
EEXPIRED
|
||||
"""
|
||||
GUID Error
|
||||
"""
|
||||
EGUID
|
||||
"""
|
||||
Multiple License Keys Present
|
||||
"""
|
||||
EGUID1
|
||||
"""
|
||||
Invalid installation
|
||||
"""
|
||||
ETRIAL
|
||||
"""
|
||||
No Keyfile
|
||||
"""
|
||||
ENOKEYFILE
|
||||
"""
|
||||
No Keyfile
|
||||
"""
|
||||
ENOKEYFILE1
|
||||
"""
|
||||
Missing key file
|
||||
"""
|
||||
ENOKEYFILE2
|
||||
"""
|
||||
No Flash
|
||||
"""
|
||||
ENOFLASH
|
||||
ENOFLASH1
|
||||
ENOFLASH2
|
||||
ENOFLASH3
|
||||
ENOFLASH4
|
||||
ENOFLASH5
|
||||
ENOFLASH6
|
||||
ENOFLASH7
|
||||
"""
|
||||
BLACKLISTED
|
||||
"""
|
||||
EBLACKLISTED
|
||||
"""
|
||||
BLACKLISTED
|
||||
"""
|
||||
EBLACKLISTED1
|
||||
"""
|
||||
BLACKLISTED
|
||||
"""
|
||||
EBLACKLISTED2
|
||||
"""
|
||||
Trial Requires Internet Connection
|
||||
"""
|
||||
ENOCONN
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
type Query {
|
||||
"""Virtual machines"""
|
||||
vms: Vms
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
"""Virtual machine mutations"""
|
||||
vms: VmMutations
|
||||
}
|
||||
|
||||
type VmMutations {
|
||||
"""Start a virtual machine"""
|
||||
startVm(id: ID!): Boolean!
|
||||
"""Stop a virtual machine"""
|
||||
stopVm(id: ID!): Boolean!
|
||||
"""Pause a virtual machine"""
|
||||
pauseVm(id: ID!): Boolean!
|
||||
"""Resume a virtual machine"""
|
||||
resumeVm(id: ID!): Boolean!
|
||||
"""Force stop a virtual machine"""
|
||||
forceStopVm(id: ID!): Boolean!
|
||||
"""Reboot a virtual machine"""
|
||||
rebootVm(id: ID!): Boolean!
|
||||
"""Reset a virtual machine"""
|
||||
resetVm(id: ID!): Boolean!
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
vms: Vms
|
||||
}
|
||||
|
||||
type Vms {
|
||||
id: ID!
|
||||
domain: [VmDomain!]
|
||||
}
|
||||
|
||||
# https://libvirt.org/manpages/virsh.html#list
|
||||
enum VmState {
|
||||
NOSTATE
|
||||
RUNNING
|
||||
IDLE
|
||||
PAUSED
|
||||
SHUTDOWN
|
||||
SHUTOFF
|
||||
CRASHED
|
||||
PMSUSPENDED
|
||||
}
|
||||
|
||||
"""A virtual machine"""
|
||||
type VmDomain {
|
||||
uuid: ID!
|
||||
"""A friendly name for the vm"""
|
||||
name: String
|
||||
"""Current domain vm state"""
|
||||
state: VmState!
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
import type { Server } from '@app/graphql/generated/client/graphql.js';
|
||||
import { AppError } from '@app/core/errors/app-error.js';
|
||||
import { graphqlLogger } from '@app/core/log.js';
|
||||
import { pubsub } from '@app/core/pubsub.js';
|
||||
import { type User } from '@app/core/types/states/user.js';
|
||||
import { ensurePermission } from '@app/core/utils/permissions/ensure-permission.js';
|
||||
import { MinigraphStatus } from '@app/graphql/generated/api/types.js';
|
||||
import { ServerStatus } from '@app/graphql/generated/client/graphql.js';
|
||||
import { store } from '@app/store/index.js';
|
||||
import { MinigraphStatus } from '@app/unraid-api/graph/resolvers/cloud/cloud.model.js';
|
||||
import { Server, ServerStatus } from '@app/unraid-api/graph/resolvers/servers/server.model.js';
|
||||
|
||||
export interface Context {
|
||||
user?: User;
|
||||
|
||||
@@ -10,7 +10,6 @@ import { WebSocket } from 'ws';
|
||||
import { FIVE_MINUTES_MS } from '@app/consts.js';
|
||||
import { minigraphLogger } from '@app/core/log.js';
|
||||
import { API_VERSION, MOTHERSHIP_GRAPHQL_LINK } from '@app/environment.js';
|
||||
import { MinigraphStatus } from '@app/graphql/generated/api/types.js';
|
||||
import { buildDelayFunction } from '@app/mothership/utils/delay-function.js';
|
||||
import {
|
||||
getMothershipConnectionParams,
|
||||
@@ -20,6 +19,7 @@ import { setGraphqlConnectionStatus } from '@app/store/actions/set-minigraph-sta
|
||||
import { getters, store } from '@app/store/index.js';
|
||||
import { logoutUser } from '@app/store/modules/config.js';
|
||||
import { receivedMothershipPing, setMothershipTimeout } from '@app/store/modules/minigraph.js';
|
||||
import { MinigraphStatus } from '@app/unraid-api/graph/resolvers/cloud/cloud.model.js';
|
||||
|
||||
const getWebsocketWithMothershipHeaders = () => {
|
||||
return class WebsocketWithMothershipHeaders extends WebSocket {
|
||||
|
||||
@@ -2,12 +2,13 @@ import { CronJob } from 'cron';
|
||||
|
||||
import { KEEP_ALIVE_INTERVAL_MS, ONE_MINUTE_MS } from '@app/consts.js';
|
||||
import { minigraphLogger, mothershipLogger, remoteAccessLogger } from '@app/core/log.js';
|
||||
import { DynamicRemoteAccessType, MinigraphStatus } from '@app/graphql/generated/api/types.js';
|
||||
import { isAPIStateDataFullyLoaded } from '@app/mothership/graphql-client.js';
|
||||
import { setGraphqlConnectionStatus } from '@app/store/actions/set-minigraph-status.js';
|
||||
import { store } from '@app/store/index.js';
|
||||
import { setRemoteAccessRunningType } from '@app/store/modules/dynamic-remote-access.js';
|
||||
import { clearSubscription } from '@app/store/modules/remote-graphql.js';
|
||||
import { MinigraphStatus } from '@app/unraid-api/graph/resolvers/cloud/cloud.model.js';
|
||||
import { DynamicRemoteAccessType } from '@app/unraid-api/graph/resolvers/connect/connect.model.js';
|
||||
|
||||
class PingTimeoutJobs {
|
||||
private cronJob: CronJob;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { type AccessUrl } from '@app/graphql/generated/api/types.js';
|
||||
import { type AppDispatch, type RootState } from '@app/store/index.js';
|
||||
import { AccessUrl } from '@app/unraid-api/graph/resolvers/connect/connect.model.js';
|
||||
|
||||
export interface GenericRemoteAccess {
|
||||
beginRemoteAccess({
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import type { AccessUrl } from '@app/graphql/generated/api/types.js';
|
||||
import { remoteAccessLogger } from '@app/core/log.js';
|
||||
import { DynamicRemoteAccessType, URL_TYPE } from '@app/graphql/generated/api/types.js';
|
||||
import { getServerIps } from '@app/graphql/resolvers/subscription/network.js';
|
||||
import { type GenericRemoteAccess } from '@app/remoteAccess/handlers/remote-access-interface.js';
|
||||
import { setWanAccessAndReloadNginx } from '@app/store/actions/set-wan-access-with-reload.js';
|
||||
import { type AppDispatch, type RootState } from '@app/store/index.js';
|
||||
import {
|
||||
AccessUrl,
|
||||
DynamicRemoteAccessType,
|
||||
URL_TYPE,
|
||||
} from '@app/unraid-api/graph/resolvers/connect/connect.model.js';
|
||||
|
||||
export class StaticRemoteAccess implements GenericRemoteAccess {
|
||||
public getRemoteAccessUrl({ getState }: { getState: () => RootState }): AccessUrl | null {
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import type { AccessUrl } from '@app/graphql/generated/api/types.js';
|
||||
import { remoteAccessLogger } from '@app/core/log.js';
|
||||
import { DynamicRemoteAccessType, URL_TYPE } from '@app/graphql/generated/api/types.js';
|
||||
import { getServerIps } from '@app/graphql/resolvers/subscription/network.js';
|
||||
import { type GenericRemoteAccess } from '@app/remoteAccess/handlers/remote-access-interface.js';
|
||||
import { setWanAccessAndReloadNginx } from '@app/store/actions/set-wan-access-with-reload.js';
|
||||
import { type AppDispatch, type RootState } from '@app/store/index.js';
|
||||
import { disableUpnp, enableUpnp } from '@app/store/modules/upnp.js';
|
||||
import {
|
||||
AccessUrl,
|
||||
DynamicRemoteAccessType,
|
||||
URL_TYPE,
|
||||
} from '@app/unraid-api/graph/resolvers/connect/connect.model.js';
|
||||
|
||||
export class UpnpRemoteAccess implements GenericRemoteAccess {
|
||||
async stopRemoteAccess({ dispatch }: { getState: () => RootState; dispatch: AppDispatch }) {
|
||||
@@ -16,7 +19,7 @@ export class UpnpRemoteAccess implements GenericRemoteAccess {
|
||||
|
||||
public getRemoteAccessUrl({ getState }: { getState: () => RootState }): AccessUrl | null {
|
||||
const urlsForServer = getServerIps(getState());
|
||||
const url = urlsForServer.urls.find((url) => url.type === URL_TYPE.WAN);
|
||||
const url = urlsForServer.urls.find((url) => url.type === URL_TYPE.WAN) ?? null;
|
||||
|
||||
return url ?? null;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import type { AccessUrl } from '@app/graphql/generated/api/types.js';
|
||||
import type { AppDispatch, RootState } from '@app/store/index.js';
|
||||
import { remoteAccessLogger } from '@app/core/log.js';
|
||||
import { UnraidLocalNotifier } from '@app/core/notifiers/unraid-local.js';
|
||||
import { DynamicRemoteAccessType } from '@app/graphql/generated/api/types.js';
|
||||
import { type IRemoteAccessController } from '@app/remoteAccess/handlers/remote-access-interface.js';
|
||||
import { StaticRemoteAccess } from '@app/remoteAccess/handlers/static-remote-access.js';
|
||||
import { UpnpRemoteAccess } from '@app/remoteAccess/handlers/upnp-remote-access.js';
|
||||
@@ -13,6 +11,10 @@ import {
|
||||
setDynamicRemoteAccessError,
|
||||
setRemoteAccessRunningType,
|
||||
} from '@app/store/modules/dynamic-remote-access.js';
|
||||
import {
|
||||
AccessUrl,
|
||||
DynamicRemoteAccessType,
|
||||
} from '@app/unraid-api/graph/resolvers/connect/connect.model.js';
|
||||
|
||||
export class RemoteAccessController implements IRemoteAccessController {
|
||||
static _instance: RemoteAccessController | null = null;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||
|
||||
import type { RemoteGraphQLEventFragmentFragment } from '@app/graphql/generated/client/graphql.js';
|
||||
import type { RemoteGraphQlEventFragmentFragment } from '@app/graphql/generated/client/graphql.js';
|
||||
import { remoteQueryLogger } from '@app/core/log.js';
|
||||
import { getApiApolloClient } from '@app/graphql/client/api/get-api-client.js';
|
||||
import { RemoteGraphQLEventType } from '@app/graphql/generated/client/graphql.js';
|
||||
import { RemoteGraphQlEventType } from '@app/graphql/generated/client/graphql.js';
|
||||
import { SEND_REMOTE_QUERY_RESPONSE } from '@app/graphql/mothership/mutations.js';
|
||||
import { parseGraphQLQuery } from '@app/graphql/resolvers/subscription/remote-graphql/remote-graphql-helpers.js';
|
||||
import { GraphQLClient } from '@app/mothership/graphql-client.js';
|
||||
@@ -13,7 +13,7 @@ import { type SubscriptionWithSha256 } from '@app/store/types.js';
|
||||
|
||||
export const addRemoteSubscription = createAsyncThunk<
|
||||
SubscriptionWithSha256,
|
||||
RemoteGraphQLEventFragmentFragment['remoteGraphQLEventData'],
|
||||
RemoteGraphQlEventFragmentFragment['remoteGraphQLEventData'],
|
||||
{ state: RootState; dispatch: AppDispatch }
|
||||
>('remoteGraphQL/addRemoteSubscription', async (data, { getState }) => {
|
||||
if (hasRemoteSubscription(data.sha256, getState())) {
|
||||
@@ -48,7 +48,7 @@ export const addRemoteSubscription = createAsyncThunk<
|
||||
input: {
|
||||
sha256: data.sha256,
|
||||
body: JSON.stringify({ data: val.data }),
|
||||
type: RemoteGraphQLEventType.REMOTE_SUBSCRIPTION_EVENT,
|
||||
type: RemoteGraphQlEventType.REMOTE_SUBSCRIPTION_EVENT,
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -63,7 +63,7 @@ export const addRemoteSubscription = createAsyncThunk<
|
||||
input: {
|
||||
sha256: data.sha256,
|
||||
body: JSON.stringify({ errors: errorValue }),
|
||||
type: RemoteGraphQLEventType.REMOTE_SUBSCRIPTION_EVENT,
|
||||
type: RemoteGraphQlEventType.REMOTE_SUBSCRIPTION_EVENT,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||
|
||||
import type { RemoteGraphQLEventFragmentFragment } from '@app/graphql/generated/client/graphql.js';
|
||||
import type { RemoteGraphQlEventFragmentFragment } from '@app/graphql/generated/client/graphql.js';
|
||||
import { remoteQueryLogger } from '@app/core/log.js';
|
||||
import { RemoteGraphQLEventType } from '@app/graphql/generated/client/graphql.js';
|
||||
import { RemoteGraphQlEventType } from '@app/graphql/generated/client/graphql.js';
|
||||
import { executeRemoteGraphQLQuery } from '@app/graphql/resolvers/subscription/remote-graphql/remote-query.js';
|
||||
import { createRemoteSubscription } from '@app/graphql/resolvers/subscription/remote-graphql/remote-subscription.js';
|
||||
import { type AppDispatch, type RootState } from '@app/store/index.js';
|
||||
@@ -10,20 +10,20 @@ import { renewRemoteSubscription } from '@app/store/modules/remote-graphql.js';
|
||||
|
||||
export const handleRemoteGraphQLEvent = createAsyncThunk<
|
||||
void,
|
||||
RemoteGraphQLEventFragmentFragment,
|
||||
RemoteGraphQlEventFragmentFragment,
|
||||
{ state: RootState; dispatch: AppDispatch }
|
||||
>('dynamicRemoteAccess/handleRemoteAccessEvent', async (event, { dispatch }) => {
|
||||
const data = event.remoteGraphQLEventData;
|
||||
switch (data.type) {
|
||||
case RemoteGraphQLEventType.REMOTE_MUTATION_EVENT:
|
||||
case RemoteGraphQlEventType.REMOTE_MUTATION_EVENT:
|
||||
break;
|
||||
case RemoteGraphQLEventType.REMOTE_QUERY_EVENT:
|
||||
case RemoteGraphQlEventType.REMOTE_QUERY_EVENT:
|
||||
remoteQueryLogger.debug('Responding to remote query event');
|
||||
return await executeRemoteGraphQLQuery(event.remoteGraphQLEventData);
|
||||
case RemoteGraphQLEventType.REMOTE_SUBSCRIPTION_EVENT:
|
||||
case RemoteGraphQlEventType.REMOTE_SUBSCRIPTION_EVENT:
|
||||
remoteQueryLogger.debug('Responding to remote subscription event');
|
||||
return await createRemoteSubscription(data);
|
||||
case RemoteGraphQLEventType.REMOTE_SUBSCRIPTION_EVENT_PING:
|
||||
case RemoteGraphQlEventType.REMOTE_SUBSCRIPTION_EVENT_PING:
|
||||
await dispatch(renewRemoteSubscription({ sha256: data.sha256 }));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createAction } from '@reduxjs/toolkit';
|
||||
|
||||
import { type MinigraphStatus } from '@app/graphql/generated/api/types.js';
|
||||
import { MinigraphStatus } from '@app/unraid-api/graph/resolvers/cloud/cloud.model.js';
|
||||
|
||||
export const setGraphqlConnectionStatus = createAction<{
|
||||
status: MinigraphStatus;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||
|
||||
import type { SetupRemoteAccessInput } from '@app/graphql/generated/api/types.js';
|
||||
import {
|
||||
DynamicRemoteAccessType,
|
||||
WAN_ACCESS_TYPE,
|
||||
WAN_FORWARD_TYPE,
|
||||
} from '@app/graphql/generated/api/types.js';
|
||||
import { type AppDispatch, type RootState } from '@app/store/index.js';
|
||||
import { type MyServersConfig } from '@app/types/my-servers-config.js';
|
||||
import {
|
||||
DynamicRemoteAccessType,
|
||||
SetupRemoteAccessInput,
|
||||
WAN_ACCESS_TYPE,
|
||||
WAN_FORWARD_TYPE,
|
||||
} from '@app/unraid-api/graph/resolvers/connect/connect.model.js';
|
||||
|
||||
const getDynamicRemoteAccessType = (
|
||||
accessType: WAN_ACCESS_TYPE,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { logDestination, logger } from '@app/core/log.js';
|
||||
import { DynamicRemoteAccessType, MinigraphStatus } from '@app/graphql/generated/api/types.js';
|
||||
import { setGraphqlConnectionStatus } from '@app/store/actions/set-minigraph-status.js';
|
||||
import { store } from '@app/store/index.js';
|
||||
import { stopListeners } from '@app/store/listeners/stop-listeners.js';
|
||||
import { setWanAccess } from '@app/store/modules/config.js';
|
||||
import { writeConfigSync } from '@app/store/sync/config-disk-sync.js';
|
||||
import { MinigraphStatus } from '@app/unraid-api/graph/resolvers/cloud/cloud.model.js';
|
||||
import { DynamicRemoteAccessType } from '@app/unraid-api/graph/resolvers/connect/connect.model.js';
|
||||
|
||||
export const shutdownApiEvent = () => {
|
||||
logger.debug('Running shutdown');
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { DNSCheck } from '@app/store/types.js';
|
||||
import { type CloudResponse } from '@app/graphql/generated/api/types.js';
|
||||
import { getters, store } from '@app/store/index.js';
|
||||
import { CacheKeys } from '@app/store/types.js';
|
||||
import { type CloudResponse } from '@app/unraid-api/graph/resolvers/cloud/cloud.model.js';
|
||||
|
||||
export const getCloudCache = (): CloudResponse | undefined => {
|
||||
const { nodeCache } = getters.cache();
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { isAnyOf } from '@reduxjs/toolkit';
|
||||
|
||||
import { remoteAccessLogger } from '@app/core/log.js';
|
||||
import { DynamicRemoteAccessType } from '@app/graphql/generated/api/types.js';
|
||||
import { RemoteAccessController } from '@app/remoteAccess/remote-access-controller.js';
|
||||
import { type RootState } from '@app/store/index.js';
|
||||
import { startAppListening } from '@app/store/listeners/listener-middleware.js';
|
||||
import { loadConfigFile } from '@app/store/modules/config.js';
|
||||
import { FileLoadStatus } from '@app/store/types.js';
|
||||
import { DynamicRemoteAccessType } from '@app/unraid-api/graph/resolvers/connect/connect.model.js';
|
||||
|
||||
const shouldDynamicRemoteAccessBeEnabled = (state: RootState | null): boolean => {
|
||||
if (
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { isEqual } from 'lodash-es';
|
||||
|
||||
import { minigraphLogger } from '@app/core/log.js';
|
||||
import { MinigraphStatus } from '@app/graphql/generated/api/types.js';
|
||||
import { setupNewMothershipSubscription } from '@app/mothership/subscribe-to-mothership.js';
|
||||
import { getMothershipConnectionParams } from '@app/mothership/utils/get-mothership-websocket-headers.js';
|
||||
import { setGraphqlConnectionStatus } from '@app/store/actions/set-minigraph-status.js';
|
||||
import { startAppListening } from '@app/store/listeners/listener-middleware.js';
|
||||
import { MinigraphStatus } from '@app/unraid-api/graph/resolvers/cloud/cloud.model.js';
|
||||
|
||||
export const enableMothershipJobsListener = () =>
|
||||
startAppListening({
|
||||
|
||||
@@ -4,8 +4,8 @@ import NodeCache from 'node-cache';
|
||||
|
||||
import type { DNSCheck } from '@app/store/types.js';
|
||||
import { ONE_HOUR_SECS } from '@app/consts.js';
|
||||
import { type CloudResponse } from '@app/graphql/generated/api/types.js';
|
||||
import { CacheKeys } from '@app/store/types.js';
|
||||
import { CloudResponse } from '@app/unraid-api/graph/resolvers/cloud/cloud.model.js';
|
||||
|
||||
const initialState: {
|
||||
nodeCache: NodeCache;
|
||||
|
||||
@@ -6,19 +6,20 @@ import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import { createAsyncThunk, createSlice, isAnyOf } from '@reduxjs/toolkit';
|
||||
import { isEqual, merge } from 'lodash-es';
|
||||
|
||||
import type { Owner } from '@app/graphql/generated/api/types.js';
|
||||
import { logger } from '@app/core/log.js';
|
||||
import { getWriteableConfig } from '@app/core/utils/files/config-file-normalizer.js';
|
||||
import { safelySerializeObjectToIni } from '@app/core/utils/files/safe-ini-serializer.js';
|
||||
import { parseConfig } from '@app/core/utils/misc/parse-config.js';
|
||||
import { NODE_ENV } from '@app/environment.js';
|
||||
import { DynamicRemoteAccessType, MinigraphStatus } from '@app/graphql/generated/api/types.js';
|
||||
import { setGraphqlConnectionStatus } from '@app/store/actions/set-minigraph-status.js';
|
||||
import { setupRemoteAccessThunk } from '@app/store/actions/setup-remote-access.js';
|
||||
import { type RootState } from '@app/store/index.js';
|
||||
import { FileLoadStatus } from '@app/store/types.js';
|
||||
import { RecursivePartial } from '@app/types/index.js';
|
||||
import { type MyServersConfig, type MyServersConfigMemory } from '@app/types/my-servers-config.js';
|
||||
import { MinigraphStatus } from '@app/unraid-api/graph/resolvers/cloud/cloud.model.js';
|
||||
import { DynamicRemoteAccessType } from '@app/unraid-api/graph/resolvers/connect/connect.model.js';
|
||||
import { Owner } from '@app/unraid-api/graph/resolvers/owner/owner.model.js';
|
||||
|
||||
export type SliceState = {
|
||||
status: FileLoadStatus;
|
||||
@@ -68,6 +69,7 @@ export const loginUser = createAsyncThunk<
|
||||
const owner: Owner = {
|
||||
username: userInfo.username,
|
||||
avatar: userInfo.avatar,
|
||||
url: '',
|
||||
};
|
||||
await pubsub.publish(PUBSUB_CHANNEL.OWNER, { owner });
|
||||
return userInfo;
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
import type { AccessUrlInput } from '@app/graphql/generated/api/types.js';
|
||||
import { remoteAccessLogger } from '@app/core/log.js';
|
||||
import { DynamicRemoteAccessType, URL_TYPE } from '@app/graphql/generated/api/types.js';
|
||||
import {
|
||||
AccessUrlInput,
|
||||
DynamicRemoteAccessType,
|
||||
URL_TYPE,
|
||||
} from '@app/unraid-api/graph/resolvers/connect/connect.model.js';
|
||||
|
||||
interface DynamicRemoteAccessState {
|
||||
runningType: DynamicRemoteAccessType; // Is Dynamic Remote Access actively running - shows type of access currently running
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user