Compare commits

...

41 Commits

Author SHA1 Message Date
Alexis
7b337dc286 chore(release): 2.27.1 2021-10-12 09:40:54 +10:30
Alexis
2cb783f597 chore: add log on no-daemon 2021-10-12 09:40:49 +10:30
Alexis
8bf89236cd fix: move config from vars.configValid/configError to config.valid/error 2021-10-07 14:39:56 +10:30
Alexis
9b3266a812 fix: when getting an unknown status code wait 60s before reconnecting instead of 0s 2021-10-07 14:08:53 +10:30
Alexis
76115dee0b chore: rename unraid-supervisor -> unsupervisor 2021-10-04 13:46:06 +10:30
Alexis
f6ea20a7c0 fix: add no-daemon to args list 2021-10-04 13:28:40 +10:30
Alexis
120fdbc33f fix: allow skipping daemonization 2021-10-04 13:22:19 +10:30
Alexis
31e1dd121a chore(release): 2.27.0 2021-09-30 16:37:44 +09:30
Alexis
a192605d5c fix: kill all old processes before starting 2021-09-30 16:37:37 +09:30
Alexis
803f7cb9e8 chore: pass env and args through from supervisor 2021-09-30 16:32:35 +09:30
Alexis
d227905999 chore: cleanup supervisor 2021-09-30 16:22:56 +09:30
Alexis
9b95cfc543 fix: ensure we always kill orphaned unraid-api processes 2021-09-30 12:53:58 +09:30
Alexis
aad1595105 chore: ship whole dist in source 2021-09-30 12:30:31 +09:30
Alexis
66c4ad0e03 chore: cleanup nexe build for supervisor 2021-09-30 09:37:42 +09:30
Alexis
9012891d26 fix: ensure nexe uses correct entry 2021-09-30 07:36:06 +09:30
Alexis
c162b3d3b9 fix: include .graphql files in bundle 2021-09-30 07:22:33 +09:30
Alexis
2bbfc6de62 chore: fix typo 2021-09-30 06:44:09 +09:30
Alexis
0d4cd20ba3 chore: ensure binary runs all steps 2021-09-30 06:43:53 +09:30
Alexis
0798f9a659 chore: include main field in package.json 2021-09-29 20:17:22 +09:30
Alexis
471c554a04 chore: allow both to include node_modules 2021-09-29 20:09:51 +09:30
Alexis
f21d54b533 chore: ensure package.json is included in build 2021-09-29 19:57:21 +09:30
Alexis
c092890544 chore: try and slim build 2021-09-29 19:22:12 +09:30
Alexis
5077563acc chore: ensure nexe directory exists before downloading node to it 2021-09-29 16:57:34 +09:30
Alexis
2de938acd2 chore: use absolute paths for wget? 2021-09-29 16:37:09 +09:30
Alexis
e9e0ebb63d chore: ensure wget uses -O flag 2021-09-29 16:26:54 +09:30
Alexis
63d7cafdc4 chore: split nexe download and upx into seperate jobs 2021-09-29 16:17:51 +09:30
Alexis
5b29bbbf69 chore: download nexe before trying to run upx on it 2021-09-29 15:55:44 +09:30
Alexis
1592f45da4 fix: modclean being run twice 2021-09-29 15:47:16 +09:30
Alexis
c41f708b94 fix: binary building 2021-09-29 15:41:09 +09:30
Alexis
60b4665dcc chore: add upx and modclean to github actions workflow 2021-09-29 15:38:11 +09:30
Alexis
5a46501334 chore: move commit hash injection to before source pack 2021-09-29 15:31:15 +09:30
Alexis
76389ea8af chore: slim supervisor binary 2021-09-29 15:30:54 +09:30
Alexis
5ed2d619f6 fix: use new bin directory for OS 2021-09-29 15:15:20 +09:30
Alexis
0984477cc6 chore: include new bin files in package 2021-09-29 15:14:45 +09:30
Alexis
c061aeb9f2 fix: ensure bin directory exists 2021-09-29 15:13:49 +09:30
Alexis
467f74cd52 chore: fix typo 2021-09-29 15:12:53 +09:30
Alexis
526efe6cae fix: ensure we get the correct path 2021-09-29 15:06:58 +09:30
Alexis
42051849d7 feat: add supervisor 2021-09-29 14:49:42 +09:30
Alexis
7d24bd24c5 fix: code->token and increase 2FA length to 64 chars 2021-09-24 04:13:20 +09:30
Alexis
5d40bd624f feat: add 2fa support 2021-09-23 15:32:16 +09:30
Alexis
bcdb044cc9 fix: allow TRAIL to be a registrationType 2021-09-23 09:23:55 +09:30
24 changed files with 664 additions and 402 deletions

View File

@@ -133,6 +133,14 @@ jobs:
- name: Clean node_modules
run: npm run clean && curl -sf https://gobinaries.com/tj/node-prune | sh && node-prune
- name: Add commit hash to version
if: startsWith(github.ref, 'refs/tags/v') == false
run: |
VERSION=$(jq -r .version package.json) && \
SHORT_HASH=$(echo $GITHUB_SHA | cut -c 1-7) && \
jq --arg version $VERSION-$SHORT_HASH '.version = $version' package.json > out.json && mv out.json package.json && \
echo '✔ Version field updated'
- name: Pack source
run: npm pack
@@ -172,6 +180,18 @@ jobs:
with:
name: unraid-api-source
- name: Pre-download node for nexe
shell: bash
run: mkdir -p /home/runner/.nexe && wget -O /home/runner/.nexe/linux-x64-14.15.3 -c https://github.com/nexe/nexe/releases/download/v3.3.3/linux-x64-14.15.3 && chmod +x /home/runner/.nexe/linux-x64-14.15.3
- name: Install upx
shell: bash
run: sudo apt-get install upx
- name: Reduce size of nexe node
shell: bash
run: upx /home/runner/.nexe/linux-x64-14.15.3
- name: Unpack unraid-api source tgz
shell: bash
run: tar xvzf ./unraid-api-*.tgz --strip 1 && rm ./unraid-api-*.tgz
@@ -179,14 +199,6 @@ jobs:
- name: Install nexe
run: npm i -g nexe
- name: Add commit hash to version
if: startsWith(github.ref, 'refs/tags/v') == false
run: |
VERSION=$(jq -r .version package.json) && \
SHORT_HASH=$(echo $GITHUB_SHA | cut -c 1-7) && \
jq --arg version $VERSION-$SHORT_HASH '.version = $version' package.json > out.json && mv out.json package.json && \
echo '✔ Version field updated'
- name: Build binary
run: npm run build-binary

3
.gitignore vendored
View File

@@ -69,3 +69,6 @@ typescript
# Github actions
RELEASE_NOTES.md
# Binary files
bin

View File

@@ -2,6 +2,39 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
### [2.27.1](https://github.com/unraid/api/compare/v2.27.0...v2.27.1) (2021-10-11)
### Bug Fixes
* add no-daemon to args list ([f6ea20a](https://github.com/unraid/api/commit/f6ea20a7c0e1d6c622b1107c7855889d5da350f6))
* allow skipping daemonization ([120fdbc](https://github.com/unraid/api/commit/120fdbc33f17784524fc92e995f50ae5ecb81824))
* move config from vars.configValid/configError to config.valid/error ([8bf8923](https://github.com/unraid/api/commit/8bf89236cdd163959f551d0bdaf9a15fd93cc2ca))
* when getting an unknown status code wait 60s before reconnecting instead of 0s ([9b3266a](https://github.com/unraid/api/commit/9b3266a81259bf7928831b5beb424350a29a78e2))
## [2.27.0](https://github.com/unraid/api/compare/v2.26.14...v2.27.0) (2021-09-30)
### Features
* add 2fa support ([5d40bd6](https://github.com/unraid/api/commit/5d40bd624f1b3df4b1d6c0ae712cf31e740f089d))
* add supervisor ([4205184](https://github.com/unraid/api/commit/42051849d7985326cbb33157c9afdb2c94cc7d27))
### Bug Fixes
* allow TRAIL to be a registrationType ([bcdb044](https://github.com/unraid/api/commit/bcdb044cc9b8c6eb5d1a93fc72ba002fbe5477a5))
* binary building ([c41f708](https://github.com/unraid/api/commit/c41f708b94df51d51946c3378c0d5b3b958b6fe4))
* code->token and increase 2FA length to 64 chars ([7d24bd2](https://github.com/unraid/api/commit/7d24bd24c505da8a3e3ebe82d3e7933758b028c5))
* ensure bin directory exists ([c061aeb](https://github.com/unraid/api/commit/c061aeb9f26828468baa03705599133718dc04d7))
* ensure nexe uses correct entry ([9012891](https://github.com/unraid/api/commit/9012891d262dfbe4867b1baefb7fcf13b43d37bb))
* ensure we always kill orphaned unraid-api processes ([9b95cfc](https://github.com/unraid/api/commit/9b95cfc543c516a2664cc10d8878d10bcff31e31))
* ensure we get the correct path ([526efe6](https://github.com/unraid/api/commit/526efe6caefaa1ba4e7d421ac8b30325f3582e1f))
* include .graphql files in bundle ([c162b3d](https://github.com/unraid/api/commit/c162b3d3b994c82aa917dc649cc5ba338f6e8520))
* kill all old processes before starting ([a192605](https://github.com/unraid/api/commit/a192605d5ca1c0b2b001265f999bc6bcea293561))
* modclean being run twice ([1592f45](https://github.com/unraid/api/commit/1592f45da43ceaaab1a819ad1eb7512d68fde1b5))
* use new bin directory for OS ([5ed2d61](https://github.com/unraid/api/commit/5ed2d619f68ec47191476d9c4095133aef544904))
### [2.26.14](https://github.com/unraid/api/compare/v2.26.13...v2.26.14) (2021-09-22)

View File

@@ -25,6 +25,7 @@ interface Flags {
command?: string;
help?: boolean;
debug?: boolean;
'no-daemon'?: boolean;
port?: string;
'log-level'?: string;
'log-transport'?: string;
@@ -35,7 +36,8 @@ interface Flags {
const args: ArgumentConfig<Flags> = {
command: { type: String, defaultOption: true, optional: true },
help: { type: Boolean, optional: true, alias: 'h', description: 'Prints this usage guide.' },
debug: { type: Boolean, optional: true, alias: 'd', description: 'Enabled debug mode.' },
debug: { type: Boolean, optional: true, alias: 'd', description: 'Enable debug mode.' },
'no-daemon': { type: Boolean, optional: true, description: 'Prevent process being daemonized.' },
port: { type: String, optional: true, alias: 'p', description: 'Set the graphql port.' },
environment: { type: String, typeLabel: '{underline production/staging/development}', optional: true, description: 'Set the working environment.' },
'log-level': { type: (level?: string) => {
@@ -136,6 +138,12 @@ const commands = {
const indexPath = './index.js';
require(indexPath);
// Skip daemonizing
if (mainOptions['no-daemon']) {
console.info('Skipping daemonizing process.');
return;
}
if (!mainOptions.debug) {
if ('_DAEMONIZE_PROCESS' in process.env) {
// In the child, clean up the tracking environment variable

View File

@@ -8,6 +8,7 @@ export const admin = {
{ resource: 'apikey', action: 'read:any', attributes: '*' },
{ resource: 'array', action: 'read:any', attributes: '*' },
{ resource: 'cpu', action: 'read:any', attributes: '*' },
{ resource: 'config', action: 'read:any', attributes: '*' },
{ resource: 'crash-reporting-enabled', action: 'read:any', attributes: '*' },
{ resource: 'device', action: 'read:any', attributes: '*' },
{ resource: 'device/unassigned', action: 'read:any', attributes: '*' },
@@ -57,6 +58,7 @@ export const upc = {
extends: 'guest',
permissions: [
{ resource: 'apikey', action: 'read:own', attributes: '*' },
{ resource: 'config', action: 'read:any', attributes: '*' },
{ resource: 'permission', action: 'read:any', attributes: '*' },
{ resource: 'registration', action: 'read:any', attributes: '*' },
{ resource: 'owner', action: 'read:any', attributes: '*' },
@@ -74,6 +76,7 @@ export const my_servers = {
extends: 'guest',
permissions: [
{ resource: 'array', action: 'read:any', attributes: '*' },
{ resource: 'config', action: 'read:any', attributes: '*' },
{ resource: 'display', action: 'read:any', attributes: '*' },
{ resource: 'docker/container', action: 'read:any', attributes: '*' },
{ resource: 'docker/network', action: 'read:any', attributes: '*' },

View File

@@ -5,7 +5,7 @@
import { ensurePermission } from '../../utils';
import { CoreContext, CoreResult } from '../../types';
import packageJson from '../../../../package.json';
import { version } from '../../../../package.json';
interface Result extends CoreResult {
json: {
@@ -45,7 +45,7 @@ export const getUnraidApiService = async (context: CoreContext): Promise<Result>
timestamp: uptimeTimestamp,
seconds: uptimeSeconds
},
version: packageJson.version
version
};
return {

View File

@@ -5,13 +5,13 @@
import path from 'path';
import { paths } from '../paths';
import { ConfigErrorState, Var } from '../types/states';
import { Var } from '../types/states';
import { IniStringBooleanOrAuto, IniStringBoolean } from '../types/ini';
import { State } from './state';
import { toNumber } from '../utils/casting';
import { parseConfig } from '../utils/misc';
const iniBooleanToJsBoolean = (value: IniStringBoolean | string, defaultValue?: any) => {
export const iniBooleanToJsBoolean = (value: IniStringBoolean | string, defaultValue?: any) => {
if (value === 'no') {
return false;
}
@@ -27,7 +27,7 @@ const iniBooleanToJsBoolean = (value: IniStringBoolean | string, defaultValue?:
throw new Error(`Value "${value}" is not no/yes.`);
};
const iniBooleanOrAutoToJsBoolean = (value: IniStringBooleanOrAuto | string) => {
export const iniBooleanOrAutoToJsBoolean = (value: IniStringBooleanOrAuto | string) => {
try {
// Either it'll return true/false or throw
return iniBooleanToJsBoolean((value as IniStringBoolean));
@@ -95,6 +95,7 @@ interface VarIni {
cacheSbNumDisks: string;
comment: string;
configValid: string;
configState: string;
csrfToken: string;
defaultFormat: string;
defaultFsType: string;
@@ -233,12 +234,7 @@ const parse = (state: VarIni): Var => {
cacheNumDevices: toNumber(state.cacheNumDevices),
cacheSbNumDisks: toNumber(state.cacheSbNumDisks),
configValid,
configError: configValid === false ? (({
error: 'UNKNOWN_ERROR',
invalid: 'INVALID',
nokeyserver: 'NO_KEY_SERVER',
withdrawn: 'WITHDRAWN'
}[state.configValid] ?? 'UNKNOWN_ERROR') as ConfigErrorState) : undefined,
configState: state.configValid,
deviceCount: toNumber(state.deviceCount),
fsCopyPrcnt: toNumber(state.fsCopyPrcnt),
fsNumMounted: toNumber(state.fsNumMounted),

View File

@@ -3,17 +3,9 @@
* Written by: Alexis Tyler
*/
// @todo: remove string and add rest of the options
type FsType = 'xfs' | string;
type RegistrationCheck = 'Error' | 'Valid';
type RegistrationType = 'INVALID' | 'TRIAL' | 'BASIC' | 'PLUS' | 'PRO' | string;
export type ConfigErrorState =
'UNKNOWN_ERROR' |
'INVALID' |
'NO_KEY_SERVER' |
'WITHDRAWN';
/**
* Global vars
*/
@@ -25,8 +17,8 @@ export interface Var {
comment: string;
/** Is the array's config valid. */
configValid: boolean;
/** If the array's config isn't valid this is the reason. */
configError?: ConfigErrorState;
/** @internal used to hold the value for config.error */
configState: string;
/** Current CSRF token for HTTP requests with emhttpd. */
csrfToken: string;
defaultFormat: string;

View File

@@ -19,7 +19,7 @@ import * as resolvers from './resolvers';
import { wsHasConnected, wsHasDisconnected } from '../ws';
import { MOTHERSHIP_RELAY_WS_LINK } from '../consts';
import { isNodeError } from '../core/utils';
import { User } from '../core/types';
import { User, Var } from '../core/types';
const internalServiceUser: User = { id: '-1', description: 'Internal service account', name: 'internal', role: 'admin', password: false };
@@ -354,29 +354,44 @@ bus.on('slots', async () => {
});
});
let hostname: string;
const localCache: {
hostname?: string;
} = {};
// Update info/hostname when hostname changes
bus.on('var', async data => {
bus.on('var', async (data: { var: { node: Var } }) => {
// Publish var changes
await pubsub.publish('vars', {
vars: data.var.node
});
// Hostname changed
if (hostname !== data.var.node.name) {
if (localCache.hostname !== data.var.node.name) {
// Update cache
hostname = data.var.node.name;
localCache.hostname = data.var.node.name;
// Publish new hostname
await pubsub.publish('info', {
info: {
os: {
hostname
hostname: localCache.hostname
}
}
});
}
// Config
await pubsub.publish('config', {
config: {
valid: data.var.node.configValid,
error: data.var.node.configValid ? undefined : ({
error: 'UNKNOWN_ERROR',
invalid: 'INVALID',
nokeyserver: 'NO_KEY_SERVER',
withdrawn: 'WITHDRAWN'
}[data.var.node.configState] ?? 'UNKNOWN_ERROR')
}
});
});
// On Docker event update info with { apps: { installed, started } }

View File

@@ -0,0 +1,26 @@
/*!
* Copyright 2021 Lime Technology Inc. All rights reserved.
* Written by: Alexis Tyler
*/
import { varState } from '../../../core/states';
import { ensurePermission } from '../../../core/utils';
import { Context } from '../../schema/utils';
export default async (_: unknown, __: unknown, context: Context) => {
ensurePermission(context.user, {
resource: 'config',
action: 'read',
possession: 'any'
});
return {
valid: varState.data.configValid,
error: varState.data.configValid ? undefined : ({
error: 'UNKNOWN_ERROR',
invalid: 'INVALID',
nokeyserver: 'NO_KEY_SERVER',
withdrawn: 'WITHDRAWN'
}[varState.data.configState] ?? 'UNKNOWN_ERROR')
};
};

View File

@@ -2,6 +2,7 @@
* Copyright 2019-2020 Lime Technology Inc. All rights reserved.
* Written by: Alexis Tyler
*/
import config from './config';
import crashReportingEnabled from './crash-reporting-enabled';
import display from './display';
import disks from './disks';
@@ -15,6 +16,7 @@ import servers from './servers';
import vms from './vms';
export const Query = {
config,
crashReportingEnabled,
disks,
display,

View File

@@ -10,6 +10,7 @@ const files = [
'./dist/types/graphql/schema/types/apikeys/apikey.graphql',
'./dist/types/graphql/schema/types/array/array.graphql',
'./dist/types/graphql/schema/types/array/parity.graphql',
'./dist/types/graphql/schema/types/config/config.graphql',
'./dist/types/graphql/schema/types/crash-reporting-enabled/crash-reporting-enabled.graphql',
'./dist/types/graphql/schema/types/devices/device.graphql',
'./dist/types/graphql/schema/types/disks/disk.graphql',

View File

@@ -0,0 +1,19 @@
enum ConfigErrorState {
UNKNOWN_ERROR
INVALID
NO_KEY_SERVER
WITHDRAWN
}
type Config {
valid: boolean;
error: ConfigErrorState;
}
type Query {
config: Config!
}
type Subscription {
config: Config!
}

View File

@@ -6,13 +6,6 @@ type Subscription {
vars: Vars!
}
enum ConfigErrorState {
UNKNOWN_ERROR
INVALID
NO_KEY_SERVER
WITHDRAWN
}
type Vars {
"""Unraid version"""
version: String
@@ -109,8 +102,6 @@ type Vars {
shareAvahiAfpModel: String
safeMode: Boolean
startMode: String
configValid: Boolean
configError: ConfigErrorState
joinStatus: String
deviceCount: Int
flashGuid: String
@@ -182,14 +173,16 @@ enum mdState {
}
enum registrationType {
"""Invalid"""
INVALID
"""Trial"""
TRIAL
"""Basic"""
BASIC
"""Plus"""
PLUS
"""Pro"""
PRO
"""Invalid"""
INVALID
}
enum registrationState {

View File

@@ -13,9 +13,7 @@ import { server } from './server';
import { mothership } from './mothership/subscribe-to-servers';
import { startInternal, sockets } from './mothership';
import { sleep } from './core/utils';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { version } = require('../package.json') as { version: string };
import { version } from '../package.json';
// Send errors to server if enabled
Sentry.init({

View File

@@ -5,7 +5,7 @@ import { INTERNAL_WS_LINK, MOTHERSHIP_RELAY_WS_LINK } from '../consts';
import { apiManager } from '../core/api-manager';
import { log } from '../core/log';
import { varState } from '../core/states/var';
import packageJson from '../../package.json';
import { version } from '../../package.json';
import { paths } from '../core/paths';
import { loadState } from '../core/utils/misc/load-state';
import { subscribeToServers } from './subscribe-to-servers';
@@ -16,17 +16,9 @@ export const sockets = {
internal: null as GracefulWebSocket | null,
relay: null as GracefulWebSocket | null
};
let isLocalConnecting = false;
let isRelayConnecting = false;
export const startInternal = (apiKey: string) => {
// Another process has already kicked this off
if (isLocalConnecting) {
return;
}
// Start the connection
isLocalConnecting = true;
log.debug('⌨️ INTERNAL:CONNECTING');
sockets.internal = new GracefulWebSocket(INTERNAL_WS_LINK, ['graphql-ws'], {
headers: {
@@ -36,7 +28,6 @@ export const startInternal = (apiKey: string) => {
sockets.internal.on('connected', () => {
log.debug('⌨️ INTERNAL:CONNECTED');
isLocalConnecting = false;
sockets.internal?.send(JSON.stringify({
type: 'connection_init',
payload: {
@@ -67,11 +58,9 @@ export const startInternal = (apiKey: string) => {
sockets.internal.on('disconnected', () => {
log.debug('⌨️ INTERNAL:DISCONNECTED');
isLocalConnecting = false;
});
sockets.internal.on('killed', () => {
isLocalConnecting = false;
log.debug('☁️ INTERNAL:KILLED');
});
@@ -105,7 +94,7 @@ const getRelayHeaders = () => {
'x-api-key': apiKey,
'x-flash-guid': varState.data?.flashGuid ?? '',
'x-server-name': serverName,
'x-unraid-api-version': packageJson.version
'x-unraid-api-version': version
};
};
@@ -119,13 +108,6 @@ const serializer = new IniSerializer({
});
export const startRelay = () => {
// Another process has already kicked this off
if (isRelayConnecting) {
return;
}
// Start the connection
isRelayConnecting = true;
log.debug('☁️ RELAY:CONNECTING');
sockets.relay = new GracefulWebSocket(MOTHERSHIP_RELAY_WS_LINK, ['graphql-ws'], {
headers: getRelayHeaders()
@@ -133,19 +115,16 @@ export const startRelay = () => {
// Connection-state related events
sockets.relay.on('connected', () => {
isRelayConnecting = false;
log.debug('☁️ RELAY:CONNECTED');
});
sockets.relay.on('disconnected', () => {
log.debug('☁️ RELAY:DISCONNECTED');
isRelayConnecting = false;
sockets.internal?.close();
sockets.internal?.start();
});
sockets.relay.on('killed', () => {
isRelayConnecting = false;
log.debug('☁️ RELAY:KILLED');
});
@@ -198,7 +177,6 @@ export const startRelay = () => {
// Retry in 30s
setTimeout(() => {
// Restart relay connection
isRelayConnecting = true;
log.debug(`☁️ RELAY:${message ?? 'API_KEY_IN_USE'}:RECONNECTING:NOW`);
sockets.relay?.start();
}, 30_000);
@@ -211,7 +189,6 @@ export const startRelay = () => {
// Retry in 60s
setTimeout(() => {
// Restart relay connection
isRelayConnecting = true;
log.debug(`☁️ RELAY:${message ?? 'INTERNAL_SERVER_ERROR'}:RECONNECTING:NOW`);
sockets.relay?.start();
}, 60_000);
@@ -224,7 +201,6 @@ export const startRelay = () => {
// Retry in 60s
setTimeout(() => {
// Restart relay connection
isRelayConnecting = true;
log.debug(`☁️ RELAY:${message ?? 'GATEWAY_DOWN'}:RECONNECTING:NOW`);
sockets.relay?.start();
}, 60_000);
@@ -232,10 +208,15 @@ export const startRelay = () => {
break;
default:
// Restart relay connection
isRelayConnecting = true;
log.debug(`☁️ RELAY:${code}:${message}:RECONNECTING:NOW`);
sockets.relay?.start();
log.debug(`☁️ RELAY:${code}:${message ?? 'UNKNOWN_ERROR'}:RECONNECTING:60_000`);
// Retry in 60s
setTimeout(() => {
// Restart relay connection
log.debug(`☁️ RELAY:${message ?? 'UNKNOWN_ERROR'}:RECONNECTING:NOW`);
sockets.relay?.start();
}, 60_000);
break;
}
});

View File

@@ -5,6 +5,7 @@
import fs from 'fs';
import net from 'net';
import crypto from 'crypto';
import path from 'path';
import execa from 'execa';
import cors from 'cors';
@@ -18,7 +19,7 @@ import { ApolloServer } from 'apollo-server-express';
import { log, config, paths, pubsub, coreLogger } from './core';
import { getEndpoints, globalErrorHandler, exitApp, cleanStdout, sleep, loadState, attemptReadFileSync, attemptJSONParse } from './core/utils';
import { graphql } from './graphql';
import packageJson from '../package.json';
import { version } from '../package.json';
import display from './graphql/resolvers/query/display';
import { networkState, varState } from './core/states';
@@ -140,7 +141,7 @@ app.use(cors({
return;
}
log.debug(`📒 Checking "${origin.toLowerCase()}" for CORS access.`);
log.trace(`📒 Checking "${origin.toLowerCase()}" for CORS access.`);
// Only allow known origins
if (!allowedOrigins.includes(origin.toLowerCase())) {
@@ -149,7 +150,7 @@ app.use(cors({
return;
}
log.debug('✔️ Origin check passed, granting CORS!');
log.trace('✔️ Origin check passed, granting CORS!');
callback(null, true);
}
}));
@@ -159,7 +160,7 @@ app.use(async (_req, res, next) => {
// Only get the machine ID on first request
// We do this to avoid using async in the main server function
if (!app.get('x-unraid-api-version')) {
app.set('x-unraid-api-version', packageJson.version);
app.set('x-unraid-api-version', version);
}
// Update header with unraid API version
@@ -194,6 +195,26 @@ app.get('/', (_, res) => {
return res.send(getEndpoints(app));
});
const generateTwoFactorToken = () => crypto.randomBytes(256).toString('hex').substring(0, 64);
// Either use the env passed in or generate a fresh one on app load
let twoFactorToken = process.env.TWO_FACTOR_TOKEN ?? generateTwoFactorToken();
app.post('/verify', (req, res) => {
// Check token matches existing one
if (req.query.token === twoFactorToken) {
// Generate a new token
twoFactorToken = generateTwoFactorToken();
// Allow the user to pass
res.sendStatus(204);
return;
}
// User failed verification
res.status(401);
res.send('Invalid 2FA token.');
});
// Handle errors by logging them and returning a 500.
app.use((error, _, res, __) => {
// Don't log CORS errors

222
app/supervisor.ts Normal file
View File

@@ -0,0 +1,222 @@
import { join as joinPath, resolve as resolveToAbsolutePath } from 'path';
import { createWriteStream, mkdirSync, existsSync, lstatSync } from 'fs';
import { spawn as spawnProcess, ChildProcess } from 'child_process';
import locatePath from 'locate-path';
import psList from 'ps-list';
import { cyan, green, red } from 'nanocolors';
import intervalToHuman from 'interval-to-human';
import killProcess from 'fkill';
import cleanStack from 'clean-stack';
const createLogger = (namespace: string) => {
const ns = namespace.toUpperCase();
return {
info(message: string, ...args: any[]) {
console.info(`${cyan(`[${ns}]`)} ${message}`, ...args);
},
debug(message: string, ...args: any[]) {
if (!isDebug) {
return;
}
console.debug(`${green(`[${ns}]`)} ${message}`, ...args);
},
error(message: string, ...args: any[]) {
console.error(`${red(`[${ns}][ERROR]`)} ${message}`, ...args);
},
print(message: string, ...args: any[]) {
console.log(message, ...args);
}
};
};
const isDebug = process.env.DEBUG !== undefined;
const appName = 'unraid-api';
const logsPath = process.env.API_LOGS_PATH ?? '/var/log/unraid-api/';
const maxRestarts = 100;
const logger = createLogger('supervisor');
const isProcessRunning = async (name: string) => {
const list = await psList();
const runningProcesses = list
// Don't return the process that ran this
.filter(_process => _process.pid !== process.pid)
.filter(process => {
return name.startsWith(process.name) || process.name.endsWith(name) || (process.name === 'node' && process.cmd?.split(' ')[1]?.endsWith(name));
});
return {
isRunning: runningProcesses.length !== 0,
pids: runningProcesses.map(process => process.pid)
};
};
const sleep = async (ms: number) => new Promise<void>(resolve => {
setTimeout(() => {
resolve();
}, ms);
});
// Save the child process outside of startApi
// This is to allow us to kill it on exit
// and/or if the timeout happens when starting it
let apiProcess: ChildProcess;
const killOldProcesses = async (name: string, processName: string) => {
const { isRunning, pids } = await isProcessRunning(processName);
if (isRunning && pids.length !== 0) {
logger.debug('Killing old %s process with pids %s', name, pids);
await killProcess(pids);
// Wait 1s for the old process to die
await sleep(1_000);
// Should be killed by now
const isRunning = await isProcessRunning(processName);
// If this is still somehow running then we need to bail.
// This should only happen if you're running supervisor as a
// user with less privileges then the one who started
// the unraid-api process we're trying to kill.
if (isRunning) {
process.exitCode = 1;
}
}
};
const startApi = async (restarts = 0, shouldRestart = true) => {
// Get an absolute path to the API binary
const apiPath = resolveToAbsolutePath(process.env.UNRAID_API_BINARY_LOCATION ?? await locatePath([
// Local dev
resolveToAbsolutePath('./bin/unraid-api'),
// Unraid OS
'/usr/local/bin/unraid-api/bin/unraid-api'
]) ?? '') ?? undefined;
// If the unraid-api we found isn't a file then bail
if (!lstatSync(apiPath).isFile()) {
throw new Error('unraid-api binary couldn\'t be located.');
}
// Ensure the directories exist for the log files
if (!existsSync(logsPath)) {
logger.debug('Creating log directory %s', logsPath);
mkdirSync(logsPath, { recursive: true });
}
// Either the new process will spawn
// or it'll timeout/disconnect/exit
logger.debug('Starting %s', appName);
await Promise.race([
new Promise<void>((resolve, reject) => {
logger.debug('Spawning %s from %s', appName, apiPath);
// Fork the child process
const args = process.argv.slice(2);
apiProcess = spawnProcess(apiPath, args, {
stdio: 'pipe',
env: {
...process.env,
DEBUG: undefined
}
});
// Create stdout and stderr log files
const logConsoleStream = createWriteStream(joinPath(logsPath, `${appName}.stdout.log`), { flags: 'a' });
const logErrorStream = createWriteStream(joinPath(logsPath, `${appName}.stderr.log`), { flags: 'a' });
// Redirect stdout and stderr to log files
apiProcess.stdout?.pipe(logConsoleStream);
apiProcess.stderr?.pipe(logErrorStream);
// App has started
apiProcess.stdout?.once('data', () => {
logger.debug('%s has started', appName);
resolve();
});
// App has thrown an error
apiProcess.stderr?.once('data', (data: Buffer) => {
logger.debug('%s threw an error %s', appName, data);
reject(new Error(data.toString()));
});
// App has exited
apiProcess.once('exit', async code => {
const exitCode = code ?? 0;
logger.debug('%s exited with code %s', appName, exitCode);
// Increase timeout by 30s for every restart
const initialTimeoutLength = restarts === 0 ? 30_000 : 30_000 * restarts;
// Reset the timeout to 30s if it gets above 5 minutes
const timeoutLength = initialTimeoutLength >= (60_000 * 5) ? 30_000 : initialTimeoutLength;
// Wait for timer before restarting
logger.info('Restarting %s in %s %s/%s', appName, intervalToHuman(timeoutLength), restarts + 1, maxRestarts);
await sleep(timeoutLength);
// Restart the app
if (shouldRestart && restarts < maxRestarts) {
logger.info('Restarting %s now %s/%s', appName, restarts + 1, maxRestarts);
await startApi(restarts + 1).catch(error => {
logger.error('Failed restarting %s with %s', appName, error);
});
}
});
logger.debug('Waiting for %s to start', appName);
}),
new Promise<void>((_resolve, reject) => {
// Increase timeout by 30s for every restart
const initialTimeoutLength = restarts === 0 ? 30_000 : 30_000 * restarts;
// Reset the timeout to 30s if it gets above 5 minutes
const timeoutLength = initialTimeoutLength >= (60_000 * 5) ? 30_000 : initialTimeoutLength;
setTimeout(() => {
reject(new Error(`Timed-out starting \`${appName}\`.`));
}, timeoutLength);
})
]).catch((error: unknown) => {
logger.error('Failed spawning %s with %s', appName, error);
apiProcess?.kill();
});
};
// On exit
const bindExitHook = async () => {
let isExiting = false;
const onExit = (shouldBail: boolean, signal: number) => {
if (isExiting) {
return;
}
// Kill api if it's still running
if (apiProcess) {
logger.debug('Killing %s process with pid %s', appName, apiProcess.pid);
apiProcess.kill('SIGTERM');
}
isExiting = true;
const exitCode = 128 + signal;
logger.debug('Supervisor exited with code %s', exitCode);
if (shouldBail) {
process.exit(exitCode);
}
};
process.once('exit', onExit);
process.once('SIGINT', onExit.bind(undefined, true, 2));
process.once('SIGTERM', onExit.bind(undefined, true, 15));
};
const startSupervisor = async () => {
logger.debug('Starting supervisor');
await bindExitHook();
await killOldProcesses('supervisor', 'unsupervisor');
await killOldProcesses('unraid-api', 'unraid-api');
await startApi();
};
// Start supervisor
startSupervisor().catch(error => {
logger.error('Failed starting %s with %s', appName, isDebug ? cleanStack(error) : error.message);
});

View File

@@ -1 +0,0 @@
declare module '*.json';

View File

@@ -2,14 +2,14 @@ import fetch from 'cross-fetch';
import * as Sentry from '@sentry/node';
import { MOTHERSHIP_GRAPHQL_LINK } from './consts';
import { CachedServer } from './cache';
import packageJson from '../package.json';
import { version } from '../package.json';
export const getServers = async (apiKey: string) => fetch(MOTHERSHIP_GRAPHQL_LINK, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
'x-unraid-api-version': packageJson.version
'x-unraid-api-version': version
},
body: JSON.stringify({
query: 'query($apiKey: String!) { servers @auth(apiKey: $apiKey) { owner { username url avatar } guid apikey name status wanip lanip localurl remoteurl } }',

View File

@@ -8,19 +8,19 @@ const subscriptions: Record<string, subscription> = {};
/**
* Return current ws connection count.
*/
export const getWsConectionCount = () => {
export const getWsConnectionCount = () => {
return Object.values(subscriptions).filter(subscription => subscription.total >= 1).length;
};
/**
* Return current ws connection count in channel.
*/
export const getWsConectionCountInChannel = (channel: string) => {
export const getWsConnectionCountInChannel = (channel: string) => {
return Object.values(subscriptions).filter(subscription => subscription.channels.includes(channel)).length;
};
export const hasSubscribedToChannel = (id: string, channel: string) => {
// Setup inital object
// Setup initial object
if (subscriptions[id] === undefined) {
subscriptions[id] = {
total: 1,
@@ -34,7 +34,7 @@ export const hasSubscribedToChannel = (id: string, channel: string) => {
};
export const hasUnsubscribedFromChannel = (id: string, channel: string) => {
// Setup inital object
// Setup initial object
if (subscriptions[id] === undefined) {
subscriptions[id] = {
total: 0,

430
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "@unraid/api",
"version": "2.26.14",
"version": "2.27.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -1447,14 +1447,6 @@
"@types/node": "*"
}
},
"@types/fs-extra": {
"version": "9.0.12",
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.12.tgz",
"integrity": "sha512-I+bsBr67CurCGnSenZZ7v94gd3tc3+Aj2taxMT4yu4ABLuOgOjeFxX3dokG24ztSRg5tnT00sL8BszO7gSMoIw==",
"requires": {
"@types/node": "*"
}
},
"@types/http-assert": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.1.tgz",
@@ -1543,32 +1535,17 @@
"integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==",
"dev": true
},
"@types/ms": {
"version": "0.7.31",
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz",
"integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==",
"dev": true
},
"@types/node": {
"version": "16.9.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz",
"integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g=="
},
"@types/node-fetch": {
"version": "2.5.12",
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.12.tgz",
"integrity": "sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw==",
"requires": {
"@types/node": "*",
"form-data": "^3.0.0"
},
"dependencies": {
"form-data": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
"integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
}
}
},
"@types/normalize-package-data": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
@@ -1991,6 +1968,13 @@
"requires": {
"clean-stack": "^2.0.0",
"indent-string": "^4.0.0"
},
"dependencies": {
"clean-stack": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
"integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="
}
}
},
"ain2": {
@@ -3688,9 +3672,21 @@
}
},
"clean-stack": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
"integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-4.1.0.tgz",
"integrity": "sha512-dxXQYI7mfQVcaF12s6sjNFoZ6ZPDQuBBLp3QJ5156k9EvUFClUoZ11fo8HnLQO241DDVntHEug8MOuFO5PSfRg==",
"dev": true,
"requires": {
"escape-string-regexp": "5.0.0"
},
"dependencies": {
"escape-string-regexp": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
"integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
"dev": true
}
}
},
"clean-yaml-object": {
"version": "0.1.0",
@@ -3841,7 +3837,8 @@
"command-exists": {
"version": "1.2.9",
"resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz",
"integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w=="
"integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==",
"dev": true
},
"command-line-args": {
"version": "5.1.1",
@@ -5216,7 +5213,8 @@
"crypto-random-string": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
"integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA=="
"integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==",
"dev": true
},
"cssfilter": {
"version": "0.0.10",
@@ -5392,7 +5390,8 @@
"dedent-tabs": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/dedent-tabs/-/dedent-tabs-0.9.0.tgz",
"integrity": "sha512-XpJr3b3pX6XHK0MDE9/hF2AxAIqzeBhBWqQ22PlzEZFqR31N8x44+e/iML76cwVvhjVqk1BqZeTBS9BrccMJlA=="
"integrity": "sha512-XpJr3b3pX6XHK0MDE9/hF2AxAIqzeBhBWqQ22PlzEZFqR31N8x44+e/iML76cwVvhjVqk1BqZeTBS9BrccMJlA==",
"dev": true
},
"deep-equal": {
"version": "1.1.1",
@@ -5513,6 +5512,7 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz",
"integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==",
"dev": true,
"requires": {
"globby": "^11.0.1",
"graceful-fs": "^4.2.4",
@@ -5528,6 +5528,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
"integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
"dev": true,
"requires": {
"aggregate-error": "^3.0.0"
}
@@ -5810,7 +5811,8 @@
"duplexer": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
"integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg=="
"integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
"dev": true
},
"duplexer3": {
"version": "0.1.4",
@@ -5984,6 +5986,12 @@
"event-emitter": "~0.3.5"
}
},
"es6-promise": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
"integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=",
"dev": true
},
"es6-set": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz",
@@ -6017,9 +6025,9 @@
}
},
"esbuild": {
"version": "0.12.17",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.17.tgz",
"integrity": "sha512-GshKJyVYUnlSXIZj/NheC2O0Kblh42CS7P1wJyTbbIHevTG4jYMS9NNw8EOd8dDWD0dzydYHS01MpZoUcQXB4g==",
"version": "0.12.29",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.29.tgz",
"integrity": "sha512-w/XuoBCSwepyiZtIRsKsetiLDUVGPVw1E/R3VTFSecIy8UR7Cq3SOtwKHJMFoVqqVG36aGkzh4e8BvpO1Fdc7g==",
"dev": true
},
"escalade": {
@@ -6346,30 +6354,6 @@
"es5-ext": "~0.10.14"
}
},
"event-stream": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz",
"integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=",
"requires": {
"duplexer": "~0.1.1",
"from": "~0",
"map-stream": "~0.1.0",
"pause-stream": "0.0.11",
"split": "0.3",
"stream-combiner": "~0.0.4",
"through": "~2.3.1"
},
"dependencies": {
"split": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz",
"integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=",
"requires": {
"through": "2"
}
}
}
},
"eventemitter3": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz",
@@ -6846,6 +6830,29 @@
"resolve-dir": "^1.0.1"
}
},
"fkill": {
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/fkill/-/fkill-7.2.1.tgz",
"integrity": "sha512-eN9cmsIlRdq06wu3m01OOEgQf5Xh/M7REm0jfZ4eL3V3XisjXzfRq3iyqtKS+FhO6wB36FvWRiRGdeSx5KpLAQ==",
"dev": true,
"requires": {
"aggregate-error": "^3.1.0",
"arrify": "^2.0.1",
"execa": "^5.0.0",
"pid-port": "^0.1.0",
"process-exists": "^4.0.0",
"ps-list": "^7.2.0",
"taskkill": "^3.1.0"
},
"dependencies": {
"arrify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
"integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
"dev": true
}
}
},
"flat-cache": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
@@ -6939,11 +6946,6 @@
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
},
"from": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz",
"integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4="
},
"fromentries": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz",
@@ -8131,6 +8133,12 @@
"kind-of": "^6.0.2"
}
},
"interval-to-human": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/interval-to-human/-/interval-to-human-0.1.1.tgz",
"integrity": "sha1-TF/91qV1oMUvAcqm2oz+sDJoXFA=",
"dev": true
},
"ip": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
@@ -8396,12 +8404,14 @@
"is-path-cwd": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz",
"integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ=="
"integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==",
"dev": true
},
"is-path-inside": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
"integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ=="
"integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
"dev": true
},
"is-plain-obj": {
"version": "1.1.0",
@@ -9268,11 +9278,6 @@
"resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz",
"integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ=="
},
"map-stream": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz",
"integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ="
},
"map-visit": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
@@ -9858,6 +9863,12 @@
"remove-array-items": "^1.0.0"
}
},
"nanocolors": {
"version": "0.2.12",
"resolved": "https://registry.npmjs.org/nanocolors/-/nanocolors-0.2.12.tgz",
"integrity": "sha512-SFNdALvzW+rVlzqexid6epYdt8H9Zol7xDoQarioEFcFN0JHo4CYNztAxmtfgGTVRCmFlEOqqhBpoFGKqSAMug==",
"dev": true
},
"nanomatch": {
"version": "1.2.13",
"resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
@@ -10721,14 +10732,6 @@
"integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==",
"dev": true
},
"pause-stream": {
"version": "0.0.11",
"resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz",
"integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=",
"requires": {
"through": "~2.3"
}
},
"performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
@@ -10739,6 +10742,15 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
"integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw=="
},
"pid-port": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/pid-port/-/pid-port-0.1.1.tgz",
"integrity": "sha512-boqPJtSgZC6KOgXKNPC+/XR3xwVtpOtaLa7JLcdf8jfVe0ZM2TwllBXxxLUO8GQbOLJ4/hEtf2+L1QCKbaoHUg==",
"dev": true,
"requires": {
"execa": "^5.0.0"
}
},
"pidusage": {
"version": "2.0.21",
"resolved": "https://registry.npmjs.org/pidusage/-/pidusage-2.0.21.tgz",
@@ -10750,7 +10762,8 @@
"pify": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz",
"integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA=="
"integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==",
"dev": true
},
"pirates": {
"version": "4.0.1",
@@ -10890,6 +10903,23 @@
"parse-ms": "^2.1.0"
}
},
"process-exists": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/process-exists/-/process-exists-4.1.0.tgz",
"integrity": "sha512-BBJoiorUKoP2AuM5q/yKwIfT1YWRHsaxjW+Ayu9erLhqKOfnXzzVVML0XTYoQZuI1YvcWKmc1dh06DEy4+KzfA==",
"dev": true,
"requires": {
"ps-list": "^6.3.0"
},
"dependencies": {
"ps-list": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/ps-list/-/ps-list-6.3.0.tgz",
"integrity": "sha512-qau0czUSB0fzSlBOQt0bo+I2v6R+xiQdj78e1BR/Qjfl5OHWJ/urXi8+ilw1eHe+5hSeDI1wrwVTgDp2wst4oA==",
"dev": true
}
}
},
"process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
@@ -10928,13 +10958,11 @@
"ipaddr.js": "1.9.1"
}
},
"ps-tree": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz",
"integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==",
"requires": {
"event-stream": "=3.3.4"
}
"ps-list": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/ps-list/-/ps-list-7.2.0.tgz",
"integrity": "sha512-v4Bl6I3f2kJfr5o80ShABNHAokIgY+wFDTQfE+X3zWYgSGQOCBeYptLZUpoOALBqO5EawmDN/tjTldJesd0ujQ==",
"dev": true
},
"psl": {
"version": "1.8.0",
@@ -11562,9 +11590,9 @@
}
},
"rollup": {
"version": "2.55.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.55.1.tgz",
"integrity": "sha512-1P9w5fpb6b4qroePh8vHKGIvPNxwoCQhjJpIqfZGHLKpZ0xcU2/XBmFxFbc9697/6bmHpmFTLk5R1dAQhFSo0g==",
"version": "2.57.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.57.0.tgz",
"integrity": "sha512-bKQIh1rWKofRee6mv8SrF2HdP6pea5QkwBZSMImJysFj39gQuiV8MEPBjXOCpzk3wSYp63M2v2wkWBmFC8O/rg==",
"dev": true,
"requires": {
"fsevents": "~2.3.2"
@@ -12473,14 +12501,6 @@
"resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz",
"integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw=="
},
"stream-combiner": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz",
"integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=",
"requires": {
"duplexer": "~0.1.1"
}
},
"stream-shift": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
@@ -12593,14 +12613,6 @@
"dev": true,
"requires": {
"es6-promise": "^3.0.2"
},
"dependencies": {
"es6-promise": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
"integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=",
"dev": true
}
}
},
"subscriptions-transport-ws": {
@@ -12623,9 +12635,9 @@
}
},
"sucrase": {
"version": "3.20.0",
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.20.0.tgz",
"integrity": "sha512-Rsp+BX7DRuCleJvBAHN7gQ3ddk7U0rJev19XlIBF6dAq9vX4Tr5mHk4E7+ig/I7BM3DLYotCmm20lfBElT2XtQ==",
"version": "3.20.1",
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.20.1.tgz",
"integrity": "sha512-BIG59HaJOxNct9Va6KvT5yzBA/rcMGetzvZyTx0ZdCcspIbpJTPS64zuAfYlJuOj+3WaI5JOdA+F0bJQQi8ZiQ==",
"dev": true,
"requires": {
"commander": "^4.0.0",
@@ -12873,15 +12885,80 @@
"xtend": "^4.0.0"
}
},
"taskkill": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/taskkill/-/taskkill-3.1.0.tgz",
"integrity": "sha512-5KcOFzPvd1nGFVrmB7H4+QAWVjYOf//+QTbOj0GpXbqtqbKGWVczG+rq6VhXAtdtlKLTs16NAmHRyF5vbggQ2w==",
"dev": true,
"requires": {
"arrify": "^2.0.1",
"execa": "^3.3.0"
},
"dependencies": {
"arrify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
"integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
"dev": true
},
"execa": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-3.4.0.tgz",
"integrity": "sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g==",
"dev": true,
"requires": {
"cross-spawn": "^7.0.0",
"get-stream": "^5.0.0",
"human-signals": "^1.1.1",
"is-stream": "^2.0.0",
"merge-stream": "^2.0.0",
"npm-run-path": "^4.0.0",
"onetime": "^5.1.0",
"p-finally": "^2.0.0",
"signal-exit": "^3.0.2",
"strip-final-newline": "^2.0.0"
}
},
"get-stream": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
"integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
"dev": true,
"requires": {
"pump": "^3.0.0"
}
},
"human-signals": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
"integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==",
"dev": true
},
"is-stream": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
"dev": true
},
"p-finally": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz",
"integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==",
"dev": true
}
}
},
"temp-dir": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz",
"integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg=="
"integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==",
"dev": true
},
"tempy": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/tempy/-/tempy-1.0.1.tgz",
"integrity": "sha512-biM9brNqxSc04Ee71hzFbryD11nX7VPhQQY32AdDmjFvodsRFz/3ufeoTZ6uYkRFfGo188tENcASNs3vTdsM0w==",
"dev": true,
"requires": {
"del": "^6.0.0",
"is-stream": "^2.0.0",
@@ -12893,12 +12970,14 @@
"is-stream": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
"dev": true
},
"type-fest": {
"version": "0.16.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz",
"integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg=="
"integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==",
"dev": true
}
}
},
@@ -12955,7 +13034,8 @@
"through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
"dev": true
},
"through2": {
"version": "2.0.5",
@@ -13164,23 +13244,23 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"tsup": {
"version": "4.12.5",
"resolved": "https://registry.npmjs.org/tsup/-/tsup-4.12.5.tgz",
"integrity": "sha512-3f0StcX+trOZvgaY/iU11U8HvvQ4v/LLgoP9OmxtOQVXP8e/Q8FSk69d0bXFb2pHB77CmVxvqiWdwybELQfx1A==",
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/tsup/-/tsup-5.2.1.tgz",
"integrity": "sha512-eQmRfKoHIiTFg38Dh2MARCx3J0+P+wbwB5iSHcXTwsnVR9csGLpln5qqBjdgyAGV1VTbOmyU37veCi2eA+wuqA==",
"dev": true,
"requires": {
"cac": "^6.7.2",
"chalk": "^4.1.0",
"chokidar": "^3.5.1",
"debug": "^4.3.1",
"esbuild": "^0.12.9",
"esbuild": "^0.12.28",
"execa": "^5.0.0",
"globby": "^11.0.3",
"joycon": "^3.0.1",
"postcss-load-config": "^3.0.1",
"resolve-from": "^5.0.0",
"rollup": "^2.45.2",
"sucrase": "^3.18.1",
"rollup": "^2.56.1",
"sucrase": "^3.20.1",
"tree-kill": "^1.2.2"
},
"dependencies": {
@@ -13387,6 +13467,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz",
"integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==",
"dev": true,
"requires": {
"crypto-random-string": "^2.0.0"
}
@@ -13905,107 +13986,6 @@
"tslib": "^1.9.3",
"zen-observable": "^0.8.0"
}
},
"zx": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/zx/-/zx-4.2.0.tgz",
"integrity": "sha512-/4f7FaJecA9I655KXKXIHO3CFNYjAz2uSmTz6v2eNlKdrQKyz4VyF3RjqFuP6nQG+Hd3+NjOvrVNBkv8Ne9d4Q==",
"requires": {
"@types/fs-extra": "^9.0.12",
"@types/minimist": "^1.2.2",
"@types/node": "^16.6",
"@types/node-fetch": "^2.5.12",
"chalk": "^4.1.2",
"fs-extra": "^10.0.0",
"globby": "^12.0.1",
"minimist": "^1.2.5",
"node-fetch": "^2.6.1",
"ps-tree": "^1.2.0",
"which": "^2.0.2"
},
"dependencies": {
"@types/minimist": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz",
"integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ=="
},
"array-union": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz",
"integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw=="
},
"chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"fast-glob": {
"version": "3.2.7",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz",
"integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==",
"requires": {
"@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3",
"glob-parent": "^5.1.2",
"merge2": "^1.3.0",
"micromatch": "^4.0.4"
}
},
"fs-extra": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz",
"integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==",
"requires": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
}
},
"globby": {
"version": "12.0.2",
"resolved": "https://registry.npmjs.org/globby/-/globby-12.0.2.tgz",
"integrity": "sha512-lAsmb/5Lww4r7MM9nCCliDZVIKbZTavrsunAsHLr9oHthrZP1qi7/gAnHOsUs9bLvEt2vKVJhHmxuL7QbDuPdQ==",
"requires": {
"array-union": "^3.0.1",
"dir-glob": "^3.0.1",
"fast-glob": "^3.2.7",
"ignore": "^5.1.8",
"merge2": "^1.4.1",
"slash": "^4.0.0"
}
},
"jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"requires": {
"graceful-fs": "^4.1.6",
"universalify": "^2.0.0"
}
},
"slash": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz",
"integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew=="
},
"universalify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ=="
},
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"requires": {
"isexe": "^2.0.0"
}
}
}
}
}
}

View File

@@ -1,17 +1,18 @@
{
"name": "@unraid/api",
"version": "2.26.14",
"main": "dist/index.js",
"version": "2.27.1",
"repository": "git@github.com:unraid/api.git",
"author": "Alexis Tyler <xo@wvvw.me> (https://wvvw.me/)",
"license": "UNLICENSED",
"scripts": {
"build": "npm run build-app && npm run build-cli && npm run copy-schemas",
"build": "npm run build-app && npm run build-cli && npm run build-supervisor && npm run copy-schemas",
"build-app": "npx tsup ./app/index.ts",
"build-cli": "npx tsup ./app/cli.ts",
"build-binary-step-1": "nexe ./dist/cli.js --make=\"-j$(nproc 2> /dev/null || echo 1)\" -r './dist/**/*' -r './node_modules' && mv ./cli ./unraid-api && echo '✔ Binary built: ./unraid-api'",
"build-binary-step-2": "rm -rf ./node_modules && rm -rf ./dist && echo '✔ Source files deleted'",
"build-binary": "npm run build-binary-step-1 && npm run build-binary-step-2",
"build-supervisor": "npx tsup ./app/supervisor.ts",
"build-binary-step-1": "nexe --input ./dist/cli.js --output ./bin/unraid-api --make=\"-j$(nproc 2> /dev/null || echo 1)\" -r './dist/**/*' -r './node_modules/**/*' && echo '✔ Binary built: ./bin/unraid-api'",
"build-binary-step-2": "nexe --input ./dist/supervisor.js --output ./bin/unsupervisor --make=\"-j$(nproc 2> /dev/null || echo 1)\" && echo '✔ Binary built: ./bin/unsupervisor'",
"build-binary-step-3": "rm -rf ./node_modules && rm -rf ./dist && echo '✔ Source files deleted'",
"build-binary": "npm run build-binary-step-1 && npm run build-binary-step-2 && npm run build-binary-step-3",
"copy-schemas": "cpx app/**/*.graphql dist/types",
"clean": "modclean --no-progress --run --path .",
"commit": "npx git-cz",
@@ -29,10 +30,10 @@
"update-bundle-dependencies": "bundle-dependencies update"
},
"files": [
".env.staging",
".env.production",
"dist",
"unraid-api"
"bin",
".env.staging",
".env.production"
],
"dependencies": {
"@apollo/client": "^3.3.17",
@@ -58,9 +59,7 @@
"clean-cache": "github:OmgImAlexis/clean-cache#master",
"clear-module": "^4.1.1",
"cli-table": "^0.3.4",
"command-exists": "^1.2.9",
"cross-fetch": "^3.1.4",
"dedent-tabs": "^0.9.0",
"deepmerge": "^4.2.2",
"dl-tgz": "^0.7.1",
"dockerode": "^3.3.0",
@@ -93,7 +92,6 @@
"map-obj": "^4.2.1",
"micromongo": "^0.3.1",
"mqtt": "github:OmgImAlexis/MQTT.js#master",
"ms": "^2.1.3",
"multi-ini": "^2.1.2",
"mustache": "^4.2.0",
"nanobus": "^4.5.0",
@@ -108,7 +106,6 @@
"package-json": "^7.0.0",
"path-exists": "^4.0.0",
"pidusage": "^2.0.21",
"pify": "^5.0.0",
"pretty-bytes": "^5.6.0",
"pretty-ms": "^7.0.1",
"redact-secrets": "OmgImAlexis/redact-secrets#master",
@@ -121,7 +118,6 @@
"stoppable": "^1.1.0",
"subscriptions-transport-ws": "^0.9.18",
"systeminformation": "^5.6.20",
"tempy": "^1.0.1",
"ts-command-line-args": "^2.1.0",
"upcast": "^4.0.0",
"uuid": "^8.3.2",
@@ -135,6 +131,7 @@
"@types/cli-table": "^0.3.0",
"@types/dockerode": "^3.2.7",
"@types/lodash.get": "^4.4.6",
"@types/ms": "^0.7.31",
"@types/node": "^16.6.1",
"@types/pify": "^5.0.1",
"@types/semver-regex": "^3.1.0",
@@ -145,20 +142,30 @@
"ava": "^3.15.0",
"ava-env": "^2.0.2",
"bundle-dependencies": "^1.0.2",
"clean-stack": "^4.1.0",
"command-exists": "^1.2.9",
"cpx": "1.5.0",
"cz-conventional-changelog": "3.3.0",
"dedent-tabs": "^0.9.0",
"eslint": "^7.32.0",
"fkill": "^7.2.1",
"interval-to-human": "^0.1.1",
"modclean": "^3.0.0-beta.1",
"ms": "^2.1.3",
"nanocolors": "^0.2.12",
"node-env-run": "^4.0.2",
"nyc": "^15.1.0",
"p-each-series": "^3.0.0",
"p-props": "^5.0.0",
"path-type": "^5.0.0",
"pify": "^5.0.0",
"ps-list": "^7.2.0",
"source-map-support": "0.5.19",
"standard-version": "^9.1.1",
"supertest": "^6.1.3",
"tempy": "^1.0.1",
"ts-node": "10.1.0",
"tsup": "^4.12.5",
"tsup": "^5.2.1",
"typescript": "4.3.5",
"typescript-coverage-report": "^0.6.0"
},

View File

@@ -1,78 +1,29 @@
{
"include": [
"app/**/*",
"test/**/*"
"test/**/*",
"package.json"
],
"exclude": [
"node_modules"
],
"compilerOptions": {
"skipLibCheck": true,
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "es2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
"allowJs": false, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
"outDir": "./dist", /* Redirect output structure to the directory. */
"rootDir": "./app", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
"removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
"noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */
"strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
"noUnusedLocals": false, /* Report errors on unused locals. */
"noUnusedParameters": false, /* Report errors on unused parameters. */
"noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
"noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
"typeRoots": [
"node_modules/@types/",
"./app/types/"
], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Advanced Options */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */,
"resolveJsonModule": false
}
}
"compilerOptions": {
"skipLibCheck": true,
"target": "es2020",
"module": "commonjs",
"allowJs": false,
"outDir": "./dist",
"rootDir": ".",
"removeComments": true,
"strict": true,
"noImplicitAny": false,
"strictNullChecks": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true
}
}