🩹 backend,oo: small fixes, related to instance id (#525)
This commit is contained in:
@@ -960,7 +960,8 @@ export class DocumentsService {
|
|||||||
* @param id DriveFile ID of the document to begin editing
|
* @param id DriveFile ID of the document to begin editing
|
||||||
* @param editorApplicationId Editor/Application/Plugin specific identifier
|
* @param editorApplicationId Editor/Application/Plugin specific identifier
|
||||||
* @param appInstanceId For that `editorApplicationId` a unique identifier
|
* @param appInstanceId For that `editorApplicationId` a unique identifier
|
||||||
* when multiple instances are running. Unused today.
|
* when multiple instances are running. Unused today - would need a mapping
|
||||||
|
* from `appInstanceId` to server host.
|
||||||
* @param context
|
* @param context
|
||||||
* @returns An object in the format `{}` with the unique identifier for the
|
* @returns An object in the format `{}` with the unique identifier for the
|
||||||
* editing session
|
* editing session
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ export const beginEditingSchema = {
|
|||||||
type: "object",
|
type: "object",
|
||||||
properties: {
|
properties: {
|
||||||
editorApplicationId: { type: "string" },
|
editorApplicationId: { type: "string" },
|
||||||
|
appInstanceId: { type: "string" },
|
||||||
},
|
},
|
||||||
required: ["editorApplicationId"],
|
required: ["editorApplicationId"],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export const {
|
|||||||
CREDENTIALS_SECRET,
|
CREDENTIALS_SECRET,
|
||||||
SERVER_PREFIX,
|
SERVER_PREFIX,
|
||||||
SERVER_ORIGIN,
|
SERVER_ORIGIN,
|
||||||
|
INSTANCE_ID,
|
||||||
} = process.env;
|
} = process.env;
|
||||||
|
|
||||||
const secs = 1000,
|
const secs = 1000,
|
||||||
|
|||||||
+55
-6
@@ -1,6 +1,7 @@
|
|||||||
import { NextFunction, Request, Response } from 'express';
|
import { Request, Response } from 'express';
|
||||||
import logger from '@/lib/logger';
|
import logger from '@/lib/logger';
|
||||||
import onlyofficeService, { CommandError, ErrorCode } from '@/services/onlyoffice.service';
|
import onlyofficeService, { Callback, CommandError, ErrorCode } from '@/services/onlyoffice.service';
|
||||||
|
import driveService from '@/services/drive.service';
|
||||||
|
|
||||||
interface RequestQuery {
|
interface RequestQuery {
|
||||||
editing_session_key: string;
|
editing_session_key: string;
|
||||||
@@ -27,12 +28,60 @@ export default class TwakeDriveBackendCallbackController {
|
|||||||
* Get status of an `editing_session_key` from OO, and return a URL to get the latest version,
|
* Get status of an `editing_session_key` from OO, and return a URL to get the latest version,
|
||||||
* or an object with no `url` property, in which case the key is not known as forgotten by OO and should
|
* or an object with no `url` property, in which case the key is not known as forgotten by OO and should
|
||||||
* be considered lost after an admin alert.
|
* be considered lost after an admin alert.
|
||||||
|
*
|
||||||
|
* @returns
|
||||||
|
* - `{ status: 'updated' }`: the key needed updating but is now invalid
|
||||||
|
* - `{ status: 'expired' }`: the key can't be used (it is verified unknown)
|
||||||
|
* - `{ status: 'live' }`: the key is valid and current and should be used again for the same file
|
||||||
|
* - `{ error: number }`: there was an error retreiving the status of the key, http status `!= 200`
|
||||||
*/
|
*/
|
||||||
public async checkSessionStatus(req: Request<RequestQuery>, res: Response): Promise<void> {
|
public async checkSessionStatus(req: Request<RequestQuery>, res: Response): Promise<void> {
|
||||||
//TODO: Find a way to check if the key is live (`info` uses the callback url)
|
//TODO: check there is auth from backend before this is ran
|
||||||
await ignoreMissingKeyErrorButNoneElse(res, async () => {
|
|
||||||
await res.send({ url: await onlyofficeService.getForgotten(req.params.editing_session_key) });
|
// have to get forgotten first, if it's there it's definitive,
|
||||||
});
|
// but if we paralelise it risks calling the callback
|
||||||
|
try {
|
||||||
|
const forgottenURL = await onlyofficeService.getForgotten(req.params.editing_session_key);
|
||||||
|
// Run upload before returning
|
||||||
|
return void res.send({ status: 'updated' });
|
||||||
|
} catch (e) {
|
||||||
|
if (!(e instanceof CommandError && e.errorCode == ErrorCode.KEY_MISSING_OR_DOC_NOT_FOUND)) {
|
||||||
|
logger.error(`getForgotten failed`, e);
|
||||||
|
return void res.status(e instanceof CommandError ? 502 : 500).send({ error: -51 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const info = await onlyofficeService.getInfoAndWaitForCallbackUnsafe(req.params.editing_session_key);
|
||||||
|
if (info.error === ErrorCode.KEY_MISSING_OR_DOC_NOT_FOUND) {
|
||||||
|
// just cancel it
|
||||||
|
return void res.send({ status: 'expired' });
|
||||||
|
}
|
||||||
|
if (info.error !== undefined) {
|
||||||
|
logger.error(`getInfo failed`, { error: info });
|
||||||
|
return void res.status(502).send({ error: -52 });
|
||||||
|
}
|
||||||
|
switch (info.result.status) {
|
||||||
|
case Callback.Status.BEING_EDITED:
|
||||||
|
case Callback.Status.BEING_EDITED_BUT_IS_SAVED:
|
||||||
|
// use it as is
|
||||||
|
return void res.send({ status: 'live' });
|
||||||
|
|
||||||
|
case Callback.Status.CLOSED_WITHOUT_CHANGES:
|
||||||
|
// just cancel it
|
||||||
|
return void res.send({ status: 'expired' });
|
||||||
|
|
||||||
|
case Callback.Status.ERROR_FORCE_SAVING:
|
||||||
|
case Callback.Status.ERROR_SAVING:
|
||||||
|
return void res.status(502).send({ error: info.result.status });
|
||||||
|
|
||||||
|
case Callback.Status.READY_FOR_SAVING:
|
||||||
|
// upload it, have to do it here for correct user stored in url in OO
|
||||||
|
//TODO: Need to fix so company_id is not needed by ooconnector but parsed from key server side
|
||||||
|
await driveService.endEditing(req.params.editing_session_key, info.result.url);
|
||||||
|
return void res.send({ status: 'updated' });
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error(`Unexpected callback status: ${JSON.stringify(info.result)}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class PendingRequest<TResult> {
|
|||||||
export class PendingRequestQueue<TResult> {
|
export class PendingRequestQueue<TResult> {
|
||||||
private queue: PendingRequest<TResult>[] = [];
|
private queue: PendingRequest<TResult>[] = [];
|
||||||
|
|
||||||
constructor(private readonly timeoutMs: number, niquystSamplingRatio: 4) {
|
constructor(private readonly timeoutMs: number, niquystSamplingRatio = 4) {
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
this.flush();
|
this.flush();
|
||||||
}, timeoutMs / niquystSamplingRatio);
|
}, timeoutMs / niquystSamplingRatio);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import apiService from './api.service';
|
|||||||
import logger from '../lib/logger';
|
import logger from '../lib/logger';
|
||||||
import { Stream } from 'stream';
|
import { Stream } from 'stream';
|
||||||
import FormData from 'form-data';
|
import FormData from 'form-data';
|
||||||
|
import { INSTANCE_ID } from '@/config';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Client for Twake Drive's APIs dealing with `DriveItem`s, using {@see apiService}
|
* Client for Twake Drive's APIs dealing with `DriveItem`s, using {@see apiService}
|
||||||
@@ -55,6 +56,7 @@ class DriveService implements IDriveService {
|
|||||||
url: `/internal/services/documents/v1/companies/${company_id}/item/${drive_file_id}/editing_session`,
|
url: `/internal/services/documents/v1/companies/${company_id}/item/${drive_file_id}/editing_session`,
|
||||||
payload: {
|
payload: {
|
||||||
editorApplicationId: 'tdrive_onlyoffice',
|
editorApplicationId: 'tdrive_onlyoffice',
|
||||||
|
appInstanceId: INSTANCE_ID ?? '',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (resource?.editingSessionKey) {
|
if (resource?.editingSessionKey) {
|
||||||
@@ -113,7 +115,7 @@ class DriveService implements IDriveService {
|
|||||||
headers: form.getHeaders(),
|
headers: form.getHeaders(),
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Failed to begin editing session: ', error.stack);
|
logger.error('Failed to end editing session: ', error.stack);
|
||||||
throw error;
|
throw error;
|
||||||
//TODO make monitoring for such kind of errors
|
//TODO make monitoring for such kind of errors
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user