fix: servers endpoint not updating when mothership updates

This commit is contained in:
Alexis Tyler
2020-10-02 20:30:00 +09:30
parent f5e8d7d8af
commit d6bf6951c6
6 changed files with 95 additions and 13 deletions
+11 -6
View File
@@ -4,6 +4,7 @@
*/
import { pluginManager, pubsub, utils, bus, errors, states, modules, apiManager, log } from '@unraid/core';
import * as Sentry from '@sentry/node';
import dee from '@gridplus/docker-events';
import { setIntervalAsync } from 'set-interval-async/dynamic';
import GraphQLJSON from 'graphql-type-json';
@@ -101,7 +102,7 @@ const getServers = async (): Promise<Server[]> => {
// For now use the my_servers key
// Later we should return the correct one for the current user with the correct scope, etc.
const apikey = apiManager.getValidKeys().find(key => key.name === 'my_servers')?.key.toString();
const apiKey = apiManager.getValidKeys().find(key => key.name === 'my_servers')?.key.toString();
// No cached servers found
if (!cachedServers) {
@@ -113,19 +114,23 @@ const getServers = async (): Promise<Server[]> => {
'Accept': 'application/json',
},
body: JSON.stringify({
query: 'query($apikey: String!) { servers @auth(apiKey: $apikey) { owner { username url avatar } guid apikey name status wanip lanip localurl remoteurl } }',
query: 'query($apiKey: String!) { servers @auth(apiKey: $apiKey) { owner { username url avatar } guid apikey name status wanip lanip localurl remoteurl } }',
variables: {
apikey
apiKey
}
})
})
.then(r => r.json())
.then(({ data }) => data.servers as Promise<CachedServer[]>);
.then(({ data }) => data.servers as Promise<CachedServer[]>)
.catch(error => {
Sentry.captureException(error);
return [];
});
log.debug('Using upstream for /servers endpoint');
// No servers found
if (servers.length === 0) {
if (!servers || servers.length === 0) {
return [];
}
@@ -153,7 +158,7 @@ const getServers = async (): Promise<Server[]> => {
avatar: ''
},
guid,
apikey,
apikey: apiKey,
name,
status: 'online',
wanip,
+13 -3
View File
@@ -1,8 +1,10 @@
import fs from 'fs';
import WebSocket from 'ws';
import * as Sentry from '@sentry/node';
import { utils, paths, states, log } from '@unraid/core';
import { MOTHERSHIP_RELAY_WS_LINK, INTERNAL_WS_LINK, ONE_MINUTE } from './consts';
import { DynamixConfig } from '@unraid/core/dist/lib/types';
import { MOTHERSHIP_RELAY_WS_LINK, INTERNAL_WS_LINK, ONE_MINUTE } from '../consts';
import { subscribeToServersEndpoint } from './subscribe-to-servers-endpoint';
const { loadState } = utils;
const { varState } = states;
@@ -79,6 +81,7 @@ export const connectToMothership = async (wsServer: WebSocket.Server, currentRet
const lanIp = states.networkState.data.find(network => network.ipaddr[0]).ipaddr[0] || '';
const machineId = `${await utils.getMachineId()}`;
let localGraphqlApi: WebSocket;
let mothershipServersEndpoint;
// Connect to mothership's relay endpoint
// Keep reference outside this scope so we can disconnect later
@@ -109,6 +112,7 @@ export const connectToMothership = async (wsServer: WebSocket.Server, currentRet
// Errors
localGraphqlApi.on('error', error => {
Sentry.captureException(error);
log.error('ws:local-relay', 'error', error);
});
@@ -127,7 +131,7 @@ export const connectToMothership = async (wsServer: WebSocket.Server, currentRet
payload: {
'x-api-key': apiKey
}
}));
}));
});
// Relay message back to mothership
@@ -141,6 +145,9 @@ export const connectToMothership = async (wsServer: WebSocket.Server, currentRet
}
}
});
// Connect to mothership's "servers" endpoint
});
// Relay is closed
@@ -158,6 +165,9 @@ export const connectToMothership = async (wsServer: WebSocket.Server, currentRet
// Clear all listeners before running this again
relay?.removeAllListeners();
// Stop subscriptions with mothership
mothershipServersEndpoint.unsubscribe();
// We likely closed this
// This is usually because the API key is updated
if (code === 4200) {
@@ -171,7 +181,7 @@ export const connectToMothership = async (wsServer: WebSocket.Server, currentRet
log.debug('Invalid API key, waiting for new key...');
return;
}
}
}
// Reconnect
setTimeout(async () => {
@@ -0,0 +1,46 @@
import * as Sentry from '@sentry/node';
import { pubsub } from '@unraid/core';
import { SubscriptionClient } from 'graphql-subscriptions-client';
import { MOTHERSHIP_GRAPHQL_LINK } from '../consts';
import { userCache, CachedServers } from '../cache';
const client = new SubscriptionClient(MOTHERSHIP_GRAPHQL_LINK, {
reconnect: true,
lazy: true, // only connect when there is a query
connectionCallback: (error) => {
if (error) {
Sentry.captureException(error);
}
}
});
export const subscribeToServersEndpoint = (apiKey: string) => {
return client.request({
query: `subscription servers ($apiKey: String!) {
servers @auth(apiKey: $apiKey)
}`,
variables: {
apiKey
}
})
.subscribe({
next({ data, errors }) {
if (errors) {
// Send all errors to Sentry
errors.forEach((error: Error) => {
Sentry.captureException(error);
});
}
if (data) {
const obj = {
servers: data.servers
};
// Update internal cache
userCache.set<CachedServers>('mine', obj);
// Update subscribers
pubsub.publish('servers', obj);
}
}
});
};
+7 -4
View File
@@ -1,14 +1,16 @@
/* eslint-disable camelcase */
const path = require('path');
const SENTRY_DSN = 'https://335a7a44d1a048648a585fc4fa053d65@o427961.ingest.sentry.io/5439629';
const staging = {
MOTHERSHIP_RELAY_WS_LINK: 'wss://staging.mothership.unraid.net/relay',
SENTRY_DSN: 'https://335a7a44d1a048648a585fc4fa053d65@o427961.ingest.sentry.io/5439629'
SENTRY_DSN
};
const production = {
NODE_ENV: 'production',
PORT: '/var/run/graphql-api.sock'
}
};
const envs = {
env_development: {
@@ -18,7 +20,8 @@ const envs = {
PATHS_STATES: path.resolve(__dirname, './dev/states'),
PATHS_DYNAMIX_DATA: '/tmp/dynamix/',
PATHS_DYNAMIX_CONFIG: path.resolve(__dirname, './dev/dynamix.cfg'),
DEBUG: true
DEBUG: true,
SENTRY_DSN
},
'env_safe-mode': {
NODE_ENV: 'safe-mode'
@@ -43,7 +46,7 @@ module.exports = {
wait_ready: true,
listen_timeout: 3000,
exp_backoff_restart_delay: 100,
max_memory_restart: '200M',
max_memory_restart: '150M',
env: {
PROCESS_TITLE: 'graphql-api'
},
+17
View File
@@ -9741,6 +9741,23 @@
"iterall": "^1.2.1"
}
},
"graphql-subscriptions-client": {
"version": "github:OmgImAlexis/graphql-subscriptions-client#b5c6ad721da1c903612a0f942681803644b712fa",
"from": "github:OmgImAlexis/graphql-subscriptions-client",
"requires": {
"backo2": "^1.0.2",
"eventemitter3": "^3.1.2",
"symbol-observable": "^1.2.0",
"ws": "^7.3.1"
},
"dependencies": {
"ws": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz",
"integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA=="
}
}
},
"graphql-tag": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.11.0.tgz",
+1
View File
@@ -44,6 +44,7 @@
"express": "^4.17.1",
"graphql": "^15.3.0",
"graphql-directive": "^0.2.1",
"graphql-subscriptions-client": "github:OmgImAlexis/graphql-subscriptions-client",
"graphql-tag": "^2.11.0",
"graphql-type-json": "^0.3.2",
"graphql-type-long": "^0.1.1",