From d94ec46e1df5be9fd92bb674f7826d16a73ae7cb Mon Sep 17 00:00:00 2001 From: Anton SHEPILOV Date: Sun, 25 Aug 2024 22:26:28 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8Added=20editing=20session=20key=20to?= =?UTF-8?q?=20the=20connector=20services?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/controllers/index.controller.ts | 13 +++-- .../src/controllers/onlyoffice.controller.ts | 17 ++++-- .../src/interfaces/drive.interface.ts | 3 ++ .../src/interfaces/routes.interface.ts | 1 + .../src/routes/onlyoffice.route.ts | 2 +- .../src/services/api.service.ts | 20 +++++++ .../src/services/drive.service.ts | 53 +++++++++++++++++-- .../onlyoffice-connector/src/views/index.eta | 4 +- 8 files changed, 97 insertions(+), 16 deletions(-) diff --git a/tdrive/connectors/onlyoffice-connector/src/controllers/index.controller.ts b/tdrive/connectors/onlyoffice-connector/src/controllers/index.controller.ts index 31d9e11b..248b9ba6 100644 --- a/tdrive/connectors/onlyoffice-connector/src/controllers/index.controller.ts +++ b/tdrive/connectors/onlyoffice-connector/src/controllers/index.controller.ts @@ -74,9 +74,9 @@ class IndexController { throw new Error('You do not have access to this file'); } - let editingSessionId = null; + let editingSessionKey = null; if (!preview) { - editingSessionId = driveService.beginEditing(drive_file_id); + editingSessionKey = await driveService.beginEditingSession(company_id, drive_file_id); //TODO catch error and display to the user when we can't stopped editing //TODO Log error with format to be able to set up grafana alert fir such king of errors @@ -87,7 +87,7 @@ class IndexController { user_id: user.id, //To verify that link is opened by the same user company_id, drive_file_id, - editing_session_id: editingSessionId, + editing_session_key: editingSessionKey, file_id: file.id, file_name: file.filename || file?.metadata?.name || '', preview: !!preview, @@ -103,7 +103,7 @@ class IndexController { Utils.joinURL([SERVER_ORIGIN ?? '', SERVER_PREFIX, 'editor'], { token, file_id, - editing_session_id: editingSessionId, + editing_session_key: editingSessionKey, company_id, preview, office_token: officeToken, @@ -123,11 +123,14 @@ class IndexController { const { user } = req; const officeTokenPayload = jwt.verify(office_token, CREDENTIALS_SECRET) as OfficeToken; - const { preview, user_id, company_id, file_name, file_id, drive_file_id } = officeTokenPayload; + const { preview, user_id, company_id, file_name, file_id, drive_file_id, editing_session_key } = officeTokenPayload; if (user_id !== user.id) { throw new Error('You do not have access to this link'); } + if (!preview && !editing_session_key) { + throw new Error('Cant start editing without "editing session key"'); + } const initResponse = await editorService.init(company_id, file_name, file_id, user, preview, drive_file_id || file_id); diff --git a/tdrive/connectors/onlyoffice-connector/src/controllers/onlyoffice.controller.ts b/tdrive/connectors/onlyoffice-connector/src/controllers/onlyoffice.controller.ts index 1cfe7354..9c4aec79 100644 --- a/tdrive/connectors/onlyoffice-connector/src/controllers/onlyoffice.controller.ts +++ b/tdrive/connectors/onlyoffice-connector/src/controllers/onlyoffice.controller.ts @@ -28,7 +28,7 @@ class OnlyOfficeController { const { token } = req.query; const officeTokenPayload = jwt.verify(token, CREDENTIALS_SECRET) as OfficeToken; - const { company_id, drive_file_id, file_id, in_page_token } = officeTokenPayload; + const { company_id, drive_file_id, file_id, in_page_token, editing_session_key } = officeTokenPayload; let fileId = file_id; // check token is an in_page_token @@ -36,9 +36,9 @@ class OnlyOfficeController { if (drive_file_id) { //Get the drive file - const driveFile = await driveService.get({ + const driveFile = await driveService.getByEditingSessionKey({ company_id, - drive_file_id, + editing_session_key, }); if (driveFile) { fileId = driveFile?.item?.last_version_cache?.file_metadata?.external_id; @@ -77,7 +77,7 @@ class OnlyOfficeController { logger.info('OO callback', 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 } = officeTokenPayload; + const { preview, company_id, file_id, /* user_id, */ drive_file_id, in_page_token, editing_session_key } = officeTokenPayload; // 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'); @@ -93,6 +93,14 @@ class OnlyOfficeController { break; case OnlyOffice.Callback.Status.READY_FOR_SAVING: + const driveFile = await driveService.getByEditingSessionKey({ + company_id, + editing_session_key, + }); + if (!driveFile) { + throw new Error('Error getting drive files '); + } + const newVersionFile = await fileService.save({ company_id, file_id, @@ -106,6 +114,7 @@ class OnlyOfficeController { file_id: newVersionFile?.resource?.id, }); logger.info('New version created', version); + return respondToOO(); case OnlyOffice.Callback.Status.CLOSED_WITHOUT_CHANGES: diff --git a/tdrive/connectors/onlyoffice-connector/src/interfaces/drive.interface.ts b/tdrive/connectors/onlyoffice-connector/src/interfaces/drive.interface.ts index 9d274d66..f17be95f 100644 --- a/tdrive/connectors/onlyoffice-connector/src/interfaces/drive.interface.ts +++ b/tdrive/connectors/onlyoffice-connector/src/interfaces/drive.interface.ts @@ -19,4 +19,7 @@ export type DriveRequestParams = { export interface IDriveService { get: (params: DriveRequestParams) => Promise; createVersion: (params: { company_id: string; drive_file_id: string; file_id: string }) => Promise; + beginEditingSession: (company_id: string, drive_file_id: string) => Promise; + endEditing: (company_id: string, editing_session_key: string) => Promise; + getByEditingSessionKey: (params: { company_id: string; editing_session_key: string; user_token?: string }) => Promise; } diff --git a/tdrive/connectors/onlyoffice-connector/src/interfaces/routes.interface.ts b/tdrive/connectors/onlyoffice-connector/src/interfaces/routes.interface.ts index 7a029546..bced3c8d 100644 --- a/tdrive/connectors/onlyoffice-connector/src/interfaces/routes.interface.ts +++ b/tdrive/connectors/onlyoffice-connector/src/interfaces/routes.interface.ts @@ -11,6 +11,7 @@ export interface OfficeToken { file_id: string; file_name: string; preview: boolean; + editing_session_key: string; drive_file_id?: string; in_page_token?: boolean; } diff --git a/tdrive/connectors/onlyoffice-connector/src/routes/onlyoffice.route.ts b/tdrive/connectors/onlyoffice-connector/src/routes/onlyoffice.route.ts index 6bc3c879..43aa3683 100644 --- a/tdrive/connectors/onlyoffice-connector/src/routes/onlyoffice.route.ts +++ b/tdrive/connectors/onlyoffice-connector/src/routes/onlyoffice.route.ts @@ -15,7 +15,7 @@ class OnlyOfficeRoute implements Routes { private initRoutes = () => { this.router.get(`${this.path}:mode/read`, requirementsMiddleware, this.onlyOfficeController.read); - this.router.post(`${this.path}:mode/save`, requirementsMiddleware, this.onlyOfficeController.ooCallback); + this.router.post(`${this.path}:mode/callback`, requirementsMiddleware, this.onlyOfficeController.ooCallback); }; } diff --git a/tdrive/connectors/onlyoffice-connector/src/services/api.service.ts b/tdrive/connectors/onlyoffice-connector/src/services/api.service.ts index fd7bd96e..9aff4fde 100644 --- a/tdrive/connectors/onlyoffice-connector/src/services/api.service.ts +++ b/tdrive/connectors/onlyoffice-connector/src/services/api.service.ts @@ -49,6 +49,26 @@ class ApiService implements IApiService { return await axiosWithToken.get(url, config); }; + public delete = async (params: IApiServiceRequestParams): Promise => { + const { url, token, responseType, headers } = params; + + const axiosWithToken = await this.requireAxios(); + + const config: AxiosRequestConfig = {}; + + if (token) { + config['headers'] = { + Authorization: `Bearer ${token}`, + ...headers, + }; + } + + if (responseType) { + config['responseType'] = responseType; + } + return await axiosWithToken.delete(url, config); + }; + public post = async (params: IApiServiceRequestParams): Promise => { const { url, payload, headers } = params; diff --git a/tdrive/connectors/onlyoffice-connector/src/services/drive.service.ts b/tdrive/connectors/onlyoffice-connector/src/services/drive.service.ts index 7b607393..53c018ca 100644 --- a/tdrive/connectors/onlyoffice-connector/src/services/drive.service.ts +++ b/tdrive/connectors/onlyoffice-connector/src/services/drive.service.ts @@ -49,13 +49,58 @@ class DriveService implements IDriveService { } }; - public beginEditing(drive_file_id: string): string { - return ''; + public async beginEditingSession(company_id: string, drive_file_id: string) { + try { + const resource = await apiService.post<{}, { editingSessionKey: string }>({ + url: `/internal/services/documents/v1/companies/${company_id}/item/${drive_file_id}/editing_session`, + payload: { + editorApplicationId: 'mock_application_id', + }, + }); + if (resource?.editingSessionKey) { + return resource.editingSessionKey; + } else { + throw new Error(`Failed to obtain editing session key, response: ${JSON.stringify(resource)}`); + } + } catch (error) { + logger.error('Failed to begin editing session: ', error.stack); + throw error; + } } - public endEditing(editing_session_id: string) { - return ''; + public async endEditing(company_id: string, editing_session_key: string) { + try { + await apiService.delete<{}>({ + url: `/internal/services/documents/v1/companies/${company_id}/item/editing_session/${editing_session_key}`, + }); + } catch (error) { + logger.error('Failed to begin editing session: ', error.stack); + throw error; + //TODO make monitoring for such kind of errors + } } + + /** + * Get the document information by the editing session key. Just simple call to the drive API + * /item/editing_session/${editing_session_key} + * @param params + */ + public getByEditingSessionKey = async (params: { + company_id: string; + editing_session_key: string; + user_token?: string; + }): Promise => { + try { + const { company_id, editing_session_key } = params; + return await apiService.get({ + url: `/internal/services/documents/v1/companies/${company_id}/item/editing_session/${editing_session_key}`, + token: params.user_token, + }); + } catch (error) { + logger.error('Failed to fetch file metadata by editing session key: ', error.stack); + throw error; + } + }; } export default new DriveService(); diff --git a/tdrive/connectors/onlyoffice-connector/src/views/index.eta b/tdrive/connectors/onlyoffice-connector/src/views/index.eta index a88e8012..2376cfae 100644 --- a/tdrive/connectors/onlyoffice-connector/src/views/index.eta +++ b/tdrive/connectors/onlyoffice-connector/src/views/index.eta @@ -44,7 +44,7 @@ token: "<%= it.file_id %>", type: screen.width < 600 ? 'mobile' : 'desktop', editorConfig: { - callbackUrl: `${window.baseURL}save?file_id=<%= it.file_id %>&company_id=<%= it.company_id %>&token=<%= it.token %>`, + callbackUrl: `${window.baseURL}callback?file_id=<%= it.file_id %>&company_id=<%= it.company_id %>&token=<%= it.token %>`, lang: window.user.language, user: { id: window.user.id, @@ -52,7 +52,7 @@ }, customization: { chat: false, - compactToolbar: true, + compactToolbar: false, about: false, feedback: false, goback: {