Files
workavia-drive/tdrive/connectors/onlyoffice-connector/src/controllers/onlyoffice.controller.ts
T
2024-09-16 20:12:42 +02:00

128 lines
4.8 KiB
TypeScript

import { CREDENTIALS_SECRET } from '@/config';
import { OfficeToken } from '@/interfaces/office-token.interface';
import driveService from '@/services/drive.service';
import fileService from '@/services/file.service';
import logger from '@/lib/logger';
import * as OnlyOffice from '@/services/onlyoffice.service';
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
import * as Utils from '@/utils';
interface RequestQuery {
company_id: string;
file_id: string;
token: string;
}
/** These expose a OnlyOffice document storage service methods, called by the OnlyOffice document editing service
* to load and save files
*/
class OnlyOfficeController {
/**
* Get a file from Twake Drive backend, and proxy it back the previewer/editor (via the document editing service).
*
* Parameters are standard Express middleware.
* @see https://api.onlyoffice.com/editors/open
*/
public read = async (req: Request<{}, {}, {}, RequestQuery>, res: Response, next: NextFunction): Promise<void> => {
try {
const { token } = req.query;
const officeTokenPayload = jwt.verify(token, CREDENTIALS_SECRET) as OfficeToken;
const { company_id, file_id, in_page_token } = officeTokenPayload;
// check token is an in_page_token
if (!in_page_token) throw new Error('Invalid token, must be a in_page_token');
if (!file_id) throw new Error(`File id is missing in the last version cache for ${JSON.stringify(file_id)}`);
const file = await fileService.download({
company_id,
file_id: file_id,
});
file.pipe(res);
} catch (error) {
next(error);
}
};
/**
* Receive a file from OnlyOffice document editing service and save it into Twake Drive backend.
*
* This is the endpoint of the callback url provided to the editor in the browser.
*
* Parameters are standard Express middleware.
* @see https://api.onlyoffice.com/editors/save
* @see https://api.onlyoffice.com/editors/callback
*/
public ooCallback = async (
req: Request<{}, {}, OnlyOffice.Callback.Parameters, RequestQuery>,
res: Response,
next: NextFunction,
): Promise<void> => {
const respondToOO = (error = 0) => void res.send({ error });
try {
const { url, key } = req.body;
const { token } = req.query;
logger.info(
`OO callback status: ${Utils.getKeyForValueSafe(
req.body.status,
OnlyOffice.Callback.Status,
'OnlyOffice.Callback.Status',
)} - ${JSON.stringify(req.body.userdata)}`,
req.body,
);
const officeTokenPayload = jwt.verify(token, CREDENTIALS_SECRET) as OfficeToken;
const { preview, company_id, file_id, /* user_id, */ drive_file_id, in_page_token, editing_session_key } = officeTokenPayload;
// Ignore errors generated by pending request
// try-catch not needed because it is async
// there may be later reasons to wait for callbacks
// to process and eventually respond accordingly to
// OO an error for certain statuses
void OnlyOffice.default.ooCallbackCalled(req.body);
// check token is an in_page_token and allow save
if (!in_page_token) throw new Error('Invalid token, must be a in_page_token');
if (preview) throw new Error('Invalid token, must not be a preview token for save operation');
switch (req.body.status) {
case OnlyOffice.Callback.Status.BEING_EDITED:
// TODO this call back we recieve almost all the time, and here we save
// the user identifiers who start file editing and even control the amount of onlin users
// to have license constraint warning before OnlyOffice error about this
case OnlyOffice.Callback.Status.BEING_EDITED_BUT_IS_SAVED:
// No-op
break;
case OnlyOffice.Callback.Status.READY_FOR_SAVING:
logger.info(`New version for session ${editing_session_key} created`);
return respondToOO();
await driveService.endEditing(editing_session_key, url);
case OnlyOffice.Callback.Status.CLOSED_WITHOUT_CHANGES:
// Save end of transaction
break;
case OnlyOffice.Callback.Status.ERROR_SAVING:
// Save end of transaction
logger.info(`Error saving file ${req.body.url} (key: ${req.body.key})`);
break;
case OnlyOffice.Callback.Status.ERROR_FORCE_SAVING:
// TODO: notify user ?
logger.info(`Error force saving (reason: ${req.body.forcesavetype}) file ${req.body.url} (key: ${req.body.key})`);
return void res.send({ error: 0 });
default:
throw new Error(`Unexpected OO Callback status field: ${req.body.status}`);
}
return respondToOO();
} catch (error) {
next(error);
}
};
}
export default OnlyOfficeController;