diff --git a/plugins/workbench-resources/src/components/ServerManagerGeneral.svelte b/plugins/workbench-resources/src/components/ServerManagerGeneral.svelte index a0ca57db40..9ab69f43f2 100644 --- a/plugins/workbench-resources/src/components/ServerManagerGeneral.svelte +++ b/plugins/workbench-resources/src/components/ServerManagerGeneral.svelte @@ -3,7 +3,7 @@ import login from '@hcengineering/login' import { getEmbeddedLabel, getMetadata } from '@hcengineering/platform' import presentation, { getClient, isAdminUser } from '@hcengineering/presentation' - import { Button, IconArrowRight, fetchMetadataLocalStorage } from '@hcengineering/ui' + import { Button, IconArrowLeft, IconArrowRight, fetchMetadataLocalStorage } from '@hcengineering/ui' import EditBox from '@hcengineering/ui/src/components/EditBox.svelte' const _endpoint: string = fetchMetadataLocalStorage(login.metadata.LoginEndpoint) ?? '' @@ -107,9 +107,12 @@ icon={IconArrowRight} label={getEmbeddedLabel('Set maintenance warning')} on:click={() => { - void fetch(endpoint + `/api/v1/manage?token=${token}&operation=maintenance&timeout=${warningTimeout}`, { - method: 'PUT' - }) + const endpoint = getMetadata(login.metadata.AccountsUrl) ?? '' + if (endpoint !== '') { + void fetch(endpoint + `/api/v1/manage?token=${token}&operation=maintenance&timeout=${warningTimeout}`, { + method: 'PUT' + }) + } }} /> <div class="flex-col p-1"> @@ -117,6 +120,18 @@ <EditBox kind={'underline'} format={'number'} bind:value={warningTimeout} /> min </div> </div> + <Button + icon={IconArrowLeft} + label={getEmbeddedLabel('Clear warning')} + on:click={() => { + const endpoint = getMetadata(login.metadata.AccountsUrl) ?? '' + if (endpoint !== '') { + void fetch(endpoint + `/api/v1/manage?token=${token}&operation=maintenance&timeout=-1`, { + method: 'PUT' + }) + } + }} + /> </div> <div class="flex-col p-1"> <div class="flex-row-center p-1"> diff --git a/server/account-service/src/index.ts b/server/account-service/src/index.ts index 40220d4ab8..5a6b229219 100644 --- a/server/account-service/src/index.ts +++ b/server/account-service/src/index.ts @@ -4,9 +4,11 @@ import account, { ACCOUNT_DB, + EndpointKind, UpgradeWorker, accountId, cleanInProgressWorkspaces, + getAllTransactors, getMethods } from '@hcengineering/account' import accountEn from '@hcengineering/account/lang/en.json' @@ -166,6 +168,44 @@ export function serveAccount ( } }) + router.put('/api/v1/manage', async (req, res) => { + try { + const token = req.query.token as string + const payload = decodeToken(token) + if (payload.extra?.admin !== 'true') { + req.res.writeHead(404, {}) + req.res.end() + return + } + + const operation = req.query.operation + + switch (operation) { + case 'maintenance': { + const timeMinutes = parseInt((req.query.timeout as string) ?? '5') + const transactors = getAllTransactors(EndpointKind.Internal) + for (const tr of transactors) { + const serverEndpoint = tr.replaceAll('wss://', 'https://').replace('ws://', 'http://') + await fetch(serverEndpoint + `/api/v1/manage?token=${token}&operation=maintenance&timeout=${timeMinutes}`, { + method: 'PUT' + }) + } + + req.res.writeHead(200) + req.res.end() + return + } + } + + req.res.writeHead(404, {}) + req.res.end() + } catch (err: any) { + Analytics.handleError(err) + req.res.writeHead(404, {}) + req.res.end() + } + }) + router.post('rpc', '/', async (ctx) => { const token = extractToken(ctx.request.headers) diff --git a/server/account/src/operations.ts b/server/account/src/operations.ts index 21da626054..7dc9540e02 100644 --- a/server/account/src/operations.ts +++ b/server/account/src/operations.ts @@ -148,6 +148,28 @@ const getEndpoint = (ctx: MeasureContext, workspaceInfo: Workspace, kind: Endpoi return transactors[Math.abs(hash % transactors.length)] } +export function getAllTransactors (kind: EndpointKind): string[] { + const transactorsUrl = getMetadata(accountPlugin.metadata.Transactors) + if (transactorsUrl === undefined) { + throw new Error('Please provide transactor endpoint url') + } + const endpoints = transactorsUrl + .split(',') + .map((it) => it.trim()) + .filter((it) => it.length > 0) + + if (endpoints.length === 0) { + throw new Error('Please provide transactor endpoint url') + } + + const toTransactor = (line: string): { internalUrl: string, group: string, externalUrl: string } => { + const [internalUrl, externalUrl, group] = line.split(';') + return { internalUrl, group: group ?? '', externalUrl: externalUrl ?? internalUrl } + } + + return endpoints.map(toTransactor).map((it) => (kind === EndpointKind.External ? it.externalUrl : it.internalUrl)) +} + /** * @public */