mirror of
https://github.com/unraid/api.git
synced 2026-01-14 20:50:01 -06:00
178 lines
4.6 KiB
TypeScript
178 lines
4.6 KiB
TypeScript
/*!
|
|
* Copyright 2019-2020 Lime Technology Inc. All rights reserved.
|
|
* Written by: Alexis Tyler
|
|
*/
|
|
|
|
import os from 'os';
|
|
import am from 'am';
|
|
import * as Sentry from '@sentry/node';
|
|
import exitHook from 'async-exit-hook';
|
|
import getServerAddress from 'get-server-address';
|
|
import { core, states, coreLogger, log, apiManager } from './core';
|
|
import { server } from './server';
|
|
import { InternalGraphql, MothershipSocket } from './mothership';
|
|
import { sockets } from './sockets';
|
|
import { sleep } from './core/utils';
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
const { version } = require('../package.json') as { version: string };
|
|
|
|
// Send errors to server if enabled
|
|
Sentry.init({
|
|
dsn: process.env.SENTRY_DSN,
|
|
tracesSampleRate: 1.0,
|
|
release: `unraid-api@${version}`,
|
|
environment: process.env.ENVIRONMENT ?? 'unknown',
|
|
serverName: os.hostname(),
|
|
enabled: Boolean(process.env.SENTRY_DSN)
|
|
});
|
|
|
|
// Set user's ID to their flashGuid
|
|
Sentry.setUser({
|
|
id: states.varState.data.flashGuid
|
|
});
|
|
|
|
const waitForInternalGraphqlToBeReady = async () => {
|
|
if (server.server.address() !== null) {
|
|
return;
|
|
}
|
|
|
|
await sleep(2000);
|
|
return waitForInternalGraphqlToBeReady();
|
|
};
|
|
|
|
// Boot app
|
|
am(async () => {
|
|
let lastknownApiKey: string;
|
|
|
|
// Load core
|
|
await core.load();
|
|
|
|
// Try and load the HTTP server
|
|
coreLogger.debug('Starting HTTP server');
|
|
|
|
// Log only if the server actually binds to the port
|
|
server.server.on('listening', () => {
|
|
coreLogger.info('Server is up! %s', getServerAddress(server.server));
|
|
});
|
|
|
|
// Trying to start server
|
|
await server.start().catch(error => {
|
|
log.error(error);
|
|
|
|
// On process exit
|
|
exitHook(async () => {
|
|
coreLogger.debug('Stopping HTTP server');
|
|
|
|
// Stop the server
|
|
server.stop();
|
|
});
|
|
});
|
|
|
|
// It has it's first keys loaded
|
|
apiManager.on('ready', async () => {
|
|
try {
|
|
// If the internal graphql server has no address it's likely still
|
|
// starting up so let's wait so we don't hit a 1006 error
|
|
await waitForInternalGraphqlToBeReady();
|
|
|
|
const apiKey = apiManager.getKey('my_servers');
|
|
|
|
// We're ready but they're not logged into myservers yet
|
|
if (!apiKey) {
|
|
return;
|
|
}
|
|
|
|
// Make note of API key
|
|
lastknownApiKey = apiKey.key;
|
|
|
|
// Create internal graphql socket
|
|
coreLogger.debug('Creating internal graphql socket');
|
|
const internalGraphqlSocket = new InternalGraphql({ lazy: true });
|
|
sockets.set('internalGraphql', internalGraphqlSocket);
|
|
|
|
// Create relay socket
|
|
coreLogger.debug('Creating relay socket');
|
|
const relaySocket = new MothershipSocket({ lazy: true });
|
|
sockets.set('relay', relaySocket);
|
|
|
|
// Connect sockets
|
|
await Promise.all([
|
|
internalGraphqlSocket.connect(),
|
|
relaySocket.connect()
|
|
]);
|
|
} catch (error: unknown) {
|
|
coreLogger.error('Failed creating sockets on "ready" event with error %s.', (error as Error).message);
|
|
}
|
|
});
|
|
|
|
// If key is removed then disconnect our sockets
|
|
apiManager.on('expire', async name => {
|
|
try {
|
|
// Bail if this isn't our key
|
|
if (name !== 'my_servers') {
|
|
return;
|
|
}
|
|
|
|
// Disconnect relay
|
|
apiManagerLogger.debug('Disconnecting relay');
|
|
await sockets.get('relay')?.disconnect();
|
|
|
|
// Disconnect internal graphql
|
|
apiManagerLogger.debug('Disconnecting internalGraphql');
|
|
await sockets.get('internalGraphql')?.disconnect();
|
|
} catch (error: unknown) {
|
|
apiManagerLogger.error('Failed updating sockets on "expire" event with error %s.', error);
|
|
}
|
|
});
|
|
|
|
// If the key changes try to (re)connect to Mothership
|
|
// The internal graphql check needs to be done
|
|
// first so it'll be up before relay connects
|
|
apiManager.on('replace', async (name, newApiKey) => {
|
|
try {
|
|
// Bail if this isn't our key
|
|
if (name !== 'my_servers') {
|
|
return;
|
|
}
|
|
|
|
// Ignore this key if it's the same as our current key.
|
|
if (newApiKey === lastknownApiKey) {
|
|
apiManagerLogger.debug('API key has\'t changed');
|
|
return;
|
|
}
|
|
|
|
// Make note of API key
|
|
lastknownApiKey = newApiKey;
|
|
|
|
// Let's reconnect all sockets
|
|
await sockets.get('relay')?.reconnect();
|
|
await sockets.get('internalGraphql')?.reconnect();
|
|
} catch (error: unknown) {
|
|
apiManagerLogger.error('Failed updating sockets on apiKey "replace" event with error %s.', error);
|
|
}
|
|
});
|
|
|
|
// Load nchan
|
|
core.loadNchan().catch(error => {
|
|
log.error(error);
|
|
});
|
|
}, async (error: NodeJS.ErrnoException) => {
|
|
// Send error to server for debugging
|
|
Sentry.captureException(error);
|
|
|
|
// Stop server
|
|
server.stop(async () => {
|
|
/**
|
|
* Flush messages to server before stopping.
|
|
*
|
|
* This may mean waiting up to 5s
|
|
* before the server actually stops.
|
|
*/
|
|
await Sentry.flush(5000);
|
|
|
|
// Kill application
|
|
process.exit(1);
|
|
});
|
|
});
|