From ca267df7ac604999a53ba90092bc97422973514a Mon Sep 17 00:00:00 2001 From: Eric Doughty-Papassideris Date: Fri, 14 Jun 2024 13:54:02 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20=20OOConnector:=20refactor?= =?UTF-8?q?=20a=20bit=20of=20string=20manipulation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/controllers/index.controller.ts | 14 ++++-- .../src/services/api.service.ts | 3 +- .../src/services/editor.service.ts | 8 ++- .../src/services/file.service.ts | 4 +- .../onlyoffice-connector/src/utils.ts | 49 +++++++++++++++++++ 5 files changed, 69 insertions(+), 9 deletions(-) create mode 100644 tdrive/connectors/onlyoffice-connector/src/utils.ts diff --git a/tdrive/connectors/onlyoffice-connector/src/controllers/index.controller.ts b/tdrive/connectors/onlyoffice-connector/src/controllers/index.controller.ts index d97c05a1..08b9f0e5 100644 --- a/tdrive/connectors/onlyoffice-connector/src/controllers/index.controller.ts +++ b/tdrive/connectors/onlyoffice-connector/src/controllers/index.controller.ts @@ -7,6 +7,7 @@ import { DriveFileType } from '@/interfaces/drive.interface'; import fileService from '@/services/file.service'; import { OfficeToken } from '@/interfaces/routes.interface'; import loggerService from '@/services/logger.service'; +import * as Utils from '@/utils'; interface RequestQuery { mode: string; @@ -89,10 +90,13 @@ class IndexController { ); res.redirect( - `${SERVER_ORIGIN ?? ''}/${SERVER_PREFIX.replace( - /(\/+$|^\/+)/gm, - '', - )}/editor?office_token=${officeToken}&token=${token}&file_id=${file_id}&company_id=${company_id}&preview=${preview}`, + Utils.joinURL([SERVER_ORIGIN ?? '', SERVER_PREFIX, 'editor'], { + token, + file_id, + company_id, + preview, + office_token: officeToken, + }) ); } catch (error) { next(error); @@ -126,7 +130,7 @@ class IndexController { res.render('index', { ...initResponse, - server: SERVER_ORIGIN.replace(/\/+$/, '') + '/' + SERVER_PREFIX.replace(/(\/+$|^\/+)/, '') || `${req.protocol}://${req.get('host')}/`, + server: Utils.joinURL([SERVER_ORIGIN, SERVER_PREFIX]), token: inPageToken, }); } catch (error) { diff --git a/tdrive/connectors/onlyoffice-connector/src/services/api.service.ts b/tdrive/connectors/onlyoffice-connector/src/services/api.service.ts index 67c462fd..d1880026 100644 --- a/tdrive/connectors/onlyoffice-connector/src/services/api.service.ts +++ b/tdrive/connectors/onlyoffice-connector/src/services/api.service.ts @@ -7,6 +7,7 @@ import { import axios, { Axios, AxiosRequestConfig, AxiosResponse } from 'axios'; import { CREDENTIALS_ENDPOINT, CREDENTIALS_ID, CREDENTIALS_SECRET, ONLY_OFFICE_SERVER } from '@config'; import loggerService from './logger.service'; +import * as Utils from '@/utils'; /** Client for the Twake Drive backend API on behalf of the plugin (or provided token in parameters) */ class ApiService implements IApiService { @@ -73,7 +74,7 @@ class ApiService implements IApiService { private refreshToken = async (): Promise => { try { const response = await axios.post( - `${CREDENTIALS_ENDPOINT.replace(/\/$/, '')}/api/console/v1/login`, + Utils.joinURL([CREDENTIALS_ENDPOINT, '/api/console/v1/login']), { id: CREDENTIALS_ID, secret: CREDENTIALS_SECRET, diff --git a/tdrive/connectors/onlyoffice-connector/src/services/editor.service.ts b/tdrive/connectors/onlyoffice-connector/src/services/editor.service.ts index 1dc5c29f..5cd82f71 100644 --- a/tdrive/connectors/onlyoffice-connector/src/services/editor.service.ts +++ b/tdrive/connectors/onlyoffice-connector/src/services/editor.service.ts @@ -1,6 +1,7 @@ import { EditConfigInitResult, IEditorService, ModeParametersType } from '@/interfaces/editor.interface'; import { UserType } from '@/interfaces/user.interface'; import { ONLY_OFFICE_SERVER } from '@config'; +import * as Utils from '@/utils'; class EditorService implements IEditorService { public init = async ( @@ -12,12 +13,14 @@ class EditorService implements IEditorService { file_id: string, ): Promise => { const { color, mode: fileMode } = this.getFileMode(file_name); + let [, extension] = Utils.splitFilename(file_name); + extension = extension.toLocaleLowerCase(); return { color, file_id, file_version_id, - file_type: file_name.split('.').pop(), + file_type: extension, filename: file_name, language: user.preferences.locale || 'en', mode: fileMode, @@ -32,7 +35,8 @@ class EditorService implements IEditorService { }; private getFileMode = (filename: string): ModeParametersType => { - const extension = filename.split('.').pop(); + let [, extension] = Utils.splitFilename(filename); + extension = extension.toLocaleLowerCase(); if ( [ diff --git a/tdrive/connectors/onlyoffice-connector/src/services/file.service.ts b/tdrive/connectors/onlyoffice-connector/src/services/file.service.ts index 72fb4769..19c8267c 100644 --- a/tdrive/connectors/onlyoffice-connector/src/services/file.service.ts +++ b/tdrive/connectors/onlyoffice-connector/src/services/file.service.ts @@ -3,8 +3,10 @@ import apiService from './api.service'; import loggerService from './logger.service'; import { Stream } from 'stream'; import FormData from 'form-data'; +import * as Utils from '@/utils'; class FileService implements IFileService { + public get = async (params: FileRequestParams): Promise => { try { const { company_id, file_id } = params; @@ -55,7 +57,7 @@ class FileService implements IFileService { const form = new FormData(); - const nameSplit = (originalFile.metadata.name || '').split('.'); + const nameSplit = Utils.splitFilename(originalFile.metadata.name || ''); const filename = nameSplit[0].replace(/-[0-9]{8}-[0-9]{4}$/, '') + (!create_new ? '.' : `-${new Date().toISOString().split('.')[0].split(':').slice(0, 2).join('').replace(/-/gm, '').split('T').join('-')}.`) + diff --git a/tdrive/connectors/onlyoffice-connector/src/utils.ts b/tdrive/connectors/onlyoffice-connector/src/utils.ts new file mode 100644 index 00000000..414e7af7 --- /dev/null +++ b/tdrive/connectors/onlyoffice-connector/src/utils.ts @@ -0,0 +1,49 @@ +/** Return the key in `obj` of which the value matches the `value` parameter, or `undefined` */ +export function getKeyForValue(value: T, obj: any): string | undefined { + return (Object.entries(obj as { [key: string]: T }).filter(([_key, entryValue]) => value === entryValue)[0] ?? [])[0]; +} + +/** Same as `getKeyForValue` but returns a default string for values not found */ +export function getKeyForValueSafe(value: T, obj: any, valueKind: string): string { + const result = getKeyForValue(value, obj); + return result ?? `(Unknown ${valueKind}: ${JSON.stringify(value)})`; +} + +/** Suitable type for query arguments */ +export type QueryParams = { [key: string]: string | number }; + +/** Compose a URL removing and adding slashes and query parameters as warranted */ +export function joinURL(path: string[], params?: QueryParams) { + let joinedPath = path.map(x => x.replace(/(?:^\/+)+|(?:\/+$)/g, "")).join("/"); + if (path[path.length - 1].endsWith("/")) + joinedPath += "/"; + const paramEntries = Object.entries(params || {}); + if (paramEntries.length === 0) + return joinedPath; + const query = paramEntries.map((p) => p.map(encodeURIComponent).join("=")).join("&"); + return joinedPath + (joinedPath.indexOf("?") > -1 ? "&" : "?") + query; +} + +/** Split a filename into an array `[name, extension]`. Either and both can be + * the empty string. When the extension is the whole name, it is assumed to be + * the name and not the extension. + * @example + * ```js + * splitFilename("") // [ "", "" ] + * splitFilename(".") // [ ".", "" ] + * splitFilename("..") // [ ".", "" ] + * splitFilename("filename") // [ "filename", "" ] + * splitFilename(".dotfile") // [ ".dotfile", "" ] + * splitFilename("a.dotfile") // [ "a", "dotfile" ] + * splitFilename(".a.dotfile") // [ ".a", "dotfile" ] + * splitFilename("a.b.dotfile") // [ "a.b", "dotfile" ] + * splitFilename(".a.b.dotfile") // [ ".a.b", "dotfile" ] + * ``` + */ +export function splitFilename(filename: string): [string, string] { + const parts = filename.split('.'); + if (parts.length < 2 || (parts.length == 2 && parts[0] === "")) + return [filename, ""]; + const extension = parts.pop(); + return [parts.join("."), extension]; +}