fix: ensure we re-query the servers endpoint on update

This commit is contained in:
Alexis Tyler
2020-10-04 13:37:21 +10:30
parent 4c2c6fa080
commit a9b4e8524d
5 changed files with 98 additions and 75 deletions
+3 -23
View File
@@ -4,17 +4,15 @@
*/
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';
import GraphQLLong from 'graphql-type-long';
import GraphQLUUID from 'graphql-type-uuid';
import fetch from 'cross-fetch';
import { run, publish } from '../../run';
import { userCache, CachedServer, CachedServers } from '../../cache';
import { hasSubscribedToChannel } from '../../ws';
import { MOTHERSHIP_GRAPHQL_LINK } from '../../consts';
import { getServers as getUserServers } from '../../utils'
const { ensurePermission } = utils;
const { usersState, varState, networkState } = states;
@@ -102,30 +100,12 @@ 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) {
// Fetch servers from mothership
const servers = await fetch(MOTHERSHIP_GRAPHQL_LINK, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'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 } }',
variables: {
apiKey
}
})
})
.then(r => r.json())
.then(({ data }) => data.servers as Promise<CachedServer[]>)
.catch(error => {
Sentry.captureException(error);
return [];
});
const servers = await getUserServers(apiKey);
log.debug('Using upstream for /servers endpoint');
+15 -6
View File
@@ -4,7 +4,8 @@ import * as Sentry from '@sentry/node';
import { utils, paths, states, log } from '@unraid/core';
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';
import { subscribeToServer } from './subscribe-to-server';
import { getServers as getUserServers } from '../utils';
const { loadState } = utils;
const { varState } = states;
@@ -81,9 +82,9 @@ 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: {
let mothershipServersEndpoints: {
unsubscribe: () => void;
};
}[];
// Connect to mothership's relay endpoint
// Keep reference outside this scope so we can disconnect later
@@ -148,8 +149,14 @@ export const connectToMothership = async (wsServer: WebSocket.Server, currentRet
}
});
// Connect to mothership's "servers" endpoint
mothershipServersEndpoint = subscribeToServersEndpoint(apiKey);
// Get servers
const servers = await getUserServers(apiKey);
if (servers) {
servers.forEach(server => {
// Subscribe to online for each server
mothershipServersEndpoints.push(subscribeToServer(server.apikey));
});
}
});
// Relay is closed
@@ -168,7 +175,9 @@ export const connectToMothership = async (wsServer: WebSocket.Server, currentRet
relay?.removeAllListeners();
// Stop subscriptions with mothership
mothershipServersEndpoint.unsubscribe();
mothershipServersEndpoints.forEach(endpoint => {
endpoint.unsubscribe();
});
// We likely closed this
// This is usually because the API key is updated
+56
View File
@@ -0,0 +1,56 @@
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';
import { getServers } from '../utils';
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 subscribeToServer = (apiKey: string) => {
// For each server subscribe to it's endpoint
return client.request({
query: `subscription status ($apiKey: String!) {
status @auth(apiKey: $apiKey)
}`,
variables: {
apiKey
}
})
.subscribe({
next({ data, errors }) {
if (errors) {
// Send all errors to Sentry
errors.forEach((error: Error) => {
Sentry.captureException(error);
});
}
// Only update if we actually have data
if (data && data.status) {
const servers = userCache.get<CachedServers>('mine');
const server = servers?.servers.find(server => server.apikey === apiKey);
if (server && server.status && server.status !== data.status) {
getServers(apiKey).then(servers => {
if (servers) {
// Update internal cache
userCache.set<CachedServers>('mine', {
servers
});
// Update subscribers
pubsub.publish('servers', servers);
}
});
}
}
}
});
};
@@ -1,46 +0,0 @@
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);
}
}
});
};
+24
View File
@@ -0,0 +1,24 @@
import * as Sentry from '@sentry/node';
import { MOTHERSHIP_GRAPHQL_LINK } from './consts';
import { CachedServer } from './cache';
export const getServers = (apiKey: string) => fetch(MOTHERSHIP_GRAPHQL_LINK, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'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 } }',
variables: {
apiKey
}
})
})
.then(async response => {
const data = await response.json();
return data.servers as Promise<CachedServer[]>;
})
.catch(error => {
Sentry.captureException(error);
});