mirror of
https://github.com/unraid/api.git
synced 2026-05-06 23:20:30 -05:00
fix: ensure we re-query the servers endpoint on update
This commit is contained in:
@@ -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
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -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);
|
||||
});
|
||||
Reference in New Issue
Block a user