@sync-in/server 1.7.0 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +173 -58
- package/environment/environment.dist.yaml +6 -3
- package/migrations/0003_giant_luckman.sql +6 -0
- package/migrations/meta/0003_snapshot.json +2463 -0
- package/migrations/meta/_journal.json +7 -0
- package/package.json +19 -17
- package/server/app.bootstrap.js +5 -2
- package/server/app.bootstrap.js.map +1 -1
- package/server/app.constants.js +0 -4
- package/server/app.constants.js.map +1 -1
- package/server/app.service.js +7 -6
- package/server/app.service.js.map +1 -1
- package/server/applications/files/constants/only-office.js +12 -0
- package/server/applications/files/constants/only-office.js.map +1 -1
- package/server/applications/files/files.config.js +5 -0
- package/server/applications/files/files.config.js.map +1 -1
- package/server/applications/files/files.controller.js +12 -4
- package/server/applications/files/files.controller.js.map +1 -1
- package/server/applications/files/files.controller.spec.js +18 -4
- package/server/applications/files/files.controller.spec.js.map +1 -1
- package/server/applications/files/services/files-content-manager.service.js +6 -6
- package/server/applications/files/services/files-content-manager.service.js.map +1 -1
- package/server/applications/files/services/files-manager.service.js +4 -4
- package/server/applications/files/services/files-manager.service.js.map +1 -1
- package/server/applications/files/services/files-methods.service.js +4 -7
- package/server/applications/files/services/files-methods.service.js.map +1 -1
- package/server/applications/files/services/files-only-office-manager.service.js +2 -2
- package/server/applications/files/services/files-only-office-manager.service.js.map +1 -1
- package/server/applications/files/services/files-parser.service.js +6 -3
- package/server/applications/files/services/files-parser.service.js.map +1 -1
- package/server/applications/files/services/files-scheduler.service.js +51 -3
- package/server/applications/files/services/files-scheduler.service.js.map +1 -1
- package/server/applications/files/services/files-search-manager.service.js +4 -0
- package/server/applications/files/services/files-search-manager.service.js.map +1 -1
- package/server/applications/files/utils/doc-textify/adapters/pdf.js +6 -8
- package/server/applications/files/utils/doc-textify/adapters/pdf.js.map +1 -1
- package/server/applications/notifications/i18n/de.js +56 -0
- package/server/applications/notifications/i18n/de.js.map +1 -0
- package/server/applications/notifications/i18n/es.js +52 -0
- package/server/applications/notifications/i18n/es.js.map +1 -0
- package/server/applications/notifications/i18n/hi.js +52 -0
- package/server/applications/notifications/i18n/hi.js.map +1 -0
- package/server/applications/notifications/i18n/index.js +73 -8
- package/server/applications/notifications/i18n/index.js.map +1 -1
- package/server/applications/notifications/i18n/it.js +52 -0
- package/server/applications/notifications/i18n/it.js.map +1 -0
- package/server/applications/notifications/i18n/ja.js +52 -0
- package/server/applications/notifications/i18n/ja.js.map +1 -0
- package/server/applications/notifications/i18n/ko.js +52 -0
- package/server/applications/notifications/i18n/ko.js.map +1 -0
- package/server/applications/notifications/i18n/pl.js +52 -0
- package/server/applications/notifications/i18n/pl.js.map +1 -0
- package/server/applications/notifications/i18n/pt.js +52 -0
- package/server/applications/notifications/i18n/pt.js.map +1 -0
- package/server/applications/notifications/i18n/pt_br.js +52 -0
- package/server/applications/notifications/i18n/pt_br.js.map +1 -0
- package/server/applications/notifications/i18n/ru.js +52 -0
- package/server/applications/notifications/i18n/ru.js.map +1 -0
- package/server/applications/notifications/i18n/tr.js +52 -0
- package/server/applications/notifications/i18n/tr.js.map +1 -0
- package/server/applications/notifications/i18n/zh.js +52 -0
- package/server/applications/notifications/i18n/zh.js.map +1 -0
- package/server/applications/notifications/mails/models.js +6 -7
- package/server/applications/notifications/mails/models.js.map +1 -1
- package/server/applications/notifications/services/notifications-manager.service.js.map +1 -1
- package/server/applications/shares/dto/create-or-update-share.dto.js +11 -0
- package/server/applications/shares/dto/create-or-update-share.dto.js.map +1 -1
- package/server/applications/shares/interfaces/share-props.interface.js.map +1 -1
- package/server/applications/shares/schemas/share.interface.js.map +1 -1
- package/server/applications/shares/schemas/shares.schema.js +9 -0
- package/server/applications/shares/schemas/shares.schema.js.map +1 -1
- package/server/applications/shares/services/shares-manager.service.js +46 -17
- package/server/applications/shares/services/shares-manager.service.js.map +1 -1
- package/server/applications/shares/services/shares-queries.service.js +24 -5
- package/server/applications/shares/services/shares-queries.service.js.map +1 -1
- package/server/applications/spaces/constants/cache.js +4 -0
- package/server/applications/spaces/constants/cache.js.map +1 -1
- package/server/applications/spaces/dto/create-or-update-space.dto.js +5 -0
- package/server/applications/spaces/dto/create-or-update-space.dto.js.map +1 -1
- package/server/applications/spaces/guards/space.guard.js +3 -3
- package/server/applications/spaces/guards/space.guard.js.map +1 -1
- package/server/applications/spaces/models/space-props.model.js.map +1 -1
- package/server/applications/spaces/models/space.model.js.map +1 -1
- package/server/applications/spaces/schemas/space.interface.js.map +1 -1
- package/server/applications/spaces/schemas/spaces.schema.js +1 -0
- package/server/applications/spaces/schemas/spaces.schema.js.map +1 -1
- package/server/applications/spaces/services/spaces-browser.service.js.map +1 -1
- package/server/applications/spaces/services/spaces-manager.service.js +34 -31
- package/server/applications/spaces/services/spaces-manager.service.js.map +1 -1
- package/server/applications/spaces/services/spaces-queries.service.js +23 -7
- package/server/applications/spaces/services/spaces-queries.service.js.map +1 -1
- package/server/applications/spaces/services/spaces-scheduler.service.js +21 -20
- package/server/applications/spaces/services/spaces-scheduler.service.js.map +1 -1
- package/server/applications/spaces/spaces.controller.js +4 -2
- package/server/applications/spaces/spaces.controller.js.map +1 -1
- package/server/applications/spaces/utils/paths.js +14 -16
- package/server/applications/spaces/utils/paths.js.map +1 -1
- package/server/applications/sync/services/sync-manager.service.js +4 -3
- package/server/applications/sync/services/sync-manager.service.js.map +1 -1
- package/server/applications/sync/services/sync-paths-manager.service.js +1 -1
- package/server/applications/sync/services/sync-paths-manager.service.js.map +1 -1
- package/server/applications/sync/services/sync-paths-manager.service.spec.js +1 -1
- package/server/applications/sync/services/sync-paths-manager.service.spec.js.map +1 -1
- package/server/applications/sync/sync.controller.js +2 -1
- package/server/applications/sync/sync.controller.js.map +1 -1
- package/server/applications/users/constants/routes.js +5 -0
- package/server/applications/users/constants/routes.js.map +1 -1
- package/server/applications/users/constants/user.js +0 -16
- package/server/applications/users/constants/user.js.map +1 -1
- package/server/applications/users/dto/user-properties.dto.js +10 -0
- package/server/applications/users/dto/user-properties.dto.js.map +1 -1
- package/server/applications/users/models/user.model.js.map +1 -1
- package/server/applications/users/schemas/user.interface.js.map +1 -1
- package/server/applications/users/schemas/users.schema.js +3 -2
- package/server/applications/users/schemas/users.schema.js.map +1 -1
- package/server/applications/users/services/admin-users-manager.service.js +1 -0
- package/server/applications/users/services/admin-users-manager.service.js.map +1 -1
- package/server/applications/users/services/admin-users-manager.service.spec.js +2 -1
- package/server/applications/users/services/admin-users-manager.service.spec.js.map +1 -1
- package/server/applications/users/services/users-manager.service.js +7 -2
- package/server/applications/users/services/users-manager.service.js.map +1 -1
- package/server/applications/users/services/users-manager.service.spec.js +1 -0
- package/server/applications/users/services/users-manager.service.spec.js.map +1 -1
- package/server/applications/users/services/users-queries.service.js +18 -4
- package/server/applications/users/services/users-queries.service.js.map +1 -1
- package/server/applications/users/users.controller.js +15 -0
- package/server/applications/users/users.controller.js.map +1 -1
- package/server/applications/users/users.gateway.js +6 -0
- package/server/applications/users/users.gateway.js.map +1 -1
- package/server/applications/users/utils/test.js +2 -2
- package/server/applications/users/utils/test.js.map +1 -1
- package/server/applications/webdav/constants/routes.js +2 -2
- package/server/applications/webdav/constants/routes.js.map +1 -1
- package/server/applications/webdav/constants/webdav.js +2 -2
- package/server/applications/webdav/constants/webdav.js.map +1 -1
- package/server/applications/webdav/filters/webdav.filter.js +2 -2
- package/server/applications/webdav/filters/webdav.filter.js.map +1 -1
- package/server/applications/webdav/filters/webdav.filter.spec.js +2 -2
- package/server/applications/webdav/filters/webdav.filter.spec.js.map +1 -1
- package/server/applications/webdav/services/webdav-methods.service.js +3 -2
- package/server/applications/webdav/services/webdav-methods.service.js.map +1 -1
- package/server/applications/webdav/utils/webdav.js +1 -2
- package/server/applications/webdav/utils/webdav.js.map +1 -1
- package/server/authentication/auth.config.js +2 -2
- package/server/authentication/auth.config.js.map +1 -1
- package/server/authentication/guards/auth-basic.strategy.js +2 -2
- package/server/authentication/guards/auth-basic.strategy.js.map +1 -1
- package/server/common/i18n.js +52 -0
- package/server/common/i18n.js.map +1 -0
- package/server/common/image.js +63 -43
- package/server/common/image.js.map +1 -1
- package/server/common/interfaces.js.map +1 -1
- package/server/common/shared.js +5 -2
- package/server/common/shared.js.map +1 -1
- package/server/configuration/config.validation.js +3 -3
- package/server/configuration/config.validation.js.map +1 -1
- package/server/infrastructure/cache/adapters/mysql-cache.adapter.js +8 -6
- package/server/infrastructure/cache/adapters/mysql-cache.adapter.js.map +1 -1
- package/server/infrastructure/cache/adapters/redis-cache.adapter.js +22 -17
- package/server/infrastructure/cache/adapters/redis-cache.adapter.js.map +1 -1
- package/server/infrastructure/cache/cache.e2e-spec.js +1 -0
- package/server/infrastructure/cache/cache.e2e-spec.js.map +1 -1
- package/server/infrastructure/cache/cache.module.js +1 -14
- package/server/infrastructure/cache/cache.module.js.map +1 -1
- package/server/infrastructure/cache/services/cache.service.js.map +1 -1
- package/server/infrastructure/database/database.module.js +20 -1
- package/server/infrastructure/database/database.module.js.map +1 -1
- package/server/infrastructure/database/utils.js +48 -0
- package/server/infrastructure/database/utils.js.map +1 -1
- package/server/infrastructure/scheduler/scheduler.module.js +1 -1
- package/server/infrastructure/scheduler/scheduler.module.js.map +1 -1
- package/server/infrastructure/websocket/adapters/cluster.adapter.js +1 -3
- package/server/infrastructure/websocket/adapters/cluster.adapter.js.map +1 -1
- package/static/3rdpartylicenses.txt +137 -163
- package/static/chunk-2KLC4T2Z.js +1 -0
- package/static/chunk-2VMSXRCB.js +12 -0
- package/static/chunk-3GMLWAFZ.js +1 -0
- package/static/chunk-3OHSRRKH.js +4 -0
- package/static/chunk-3R4WKOHQ.js +1 -0
- package/static/{chunk-7ITZXYYJ.js → chunk-3R74L4UU.js} +1 -1
- package/static/chunk-3XVM35O2.js +1 -0
- package/static/chunk-3YVRP3VM.js +2 -0
- package/static/chunk-5NMSIIQB.js +1 -0
- package/static/chunk-5UKZLU5H.js +1 -0
- package/static/chunk-AF24EYXU.js +1 -0
- package/static/chunk-AKQVEHO6.js +2 -0
- package/static/chunk-BCVX464U.js +2 -0
- package/static/chunk-BQV4FRM6.js +1 -0
- package/static/{chunk-EVIE5F2U.js → chunk-CETH7UYS.js} +1 -1
- package/static/chunk-CHJ64RJM.js +1 -0
- package/static/chunk-DIT6W7VM.js +562 -0
- package/static/chunk-DKSEQTMX.js +1 -0
- package/static/chunk-DM4NXKEP.js +1 -0
- package/static/chunk-DPUVSXRB.js +1 -0
- package/static/chunk-DSWEWLXJ.js +1 -0
- package/static/chunk-FJE6BOFL.js +1 -0
- package/static/chunk-FZ3JPGYZ.js +1 -0
- package/static/chunk-IQSKQXC3.js +1 -0
- package/static/chunk-ITUFI2BJ.js +1 -0
- package/static/chunk-JPT5WEAT.js +1 -0
- package/static/chunk-LCTZJ537.js +1 -0
- package/static/chunk-LK2UCQJ6.js +1 -0
- package/static/chunk-LNTUR3GU.js +1 -0
- package/static/chunk-LP5TBXEN.js +7 -0
- package/static/{chunk-IPAC4VAF.js → chunk-LVSNIS5P.js} +1 -1
- package/static/{chunk-SIPE37PA.js → chunk-MTVSJTIW.js} +1 -1
- package/static/chunk-N3U6637P.js +1 -0
- package/static/chunk-NNV4OXSB.js +1 -0
- package/static/chunk-O6FYXVHI.js +1 -0
- package/static/chunk-OOGP4WSH.js +2 -0
- package/static/chunk-PB4AIT7O.js +1 -0
- package/static/chunk-PCWDQPOM.js +2 -0
- package/static/chunk-PNR6M34W.js +1 -0
- package/static/chunk-PVDHBQRM.js +1 -0
- package/static/chunk-Q5KM7LTX.js +1 -0
- package/static/chunk-QHC6ZPQ4.js +1 -0
- package/static/chunk-QMRBZHE4.js +1 -0
- package/static/chunk-QO6BTONN.js +1 -0
- package/static/chunk-QSJRY3TF.js +1 -0
- package/static/chunk-QUUIRSYT.js +1 -0
- package/static/chunk-RFH46UW3.js +1 -0
- package/static/{chunk-PTGDOWV3.js → chunk-RSXHRKM5.js} +1 -1
- package/static/chunk-RV3VZJPZ.js +1 -0
- package/static/{chunk-QNJFQVYI.js → chunk-S7HNXVRB.js} +1 -1
- package/static/chunk-SBZ572Q4.js +2 -0
- package/static/chunk-SJR5R3Y4.js +1 -0
- package/static/chunk-SLHTEGRU.js +1 -0
- package/static/{chunk-SH5EVL4E.js → chunk-SSFF27P2.js} +1 -1
- package/static/chunk-UNCPXHHT.js +1 -0
- package/static/chunk-URHTCJ7G.js +1 -0
- package/static/chunk-V3LHHZYN.js +1 -0
- package/static/{chunk-DJYJ66UF.js → chunk-VJTXJ43D.js} +1 -1
- package/static/chunk-VQQKMY2C.js +1 -0
- package/static/{chunk-IQOALFYU.js → chunk-WSSU2HXE.js} +1 -1
- package/static/chunk-XDZGW64M.js +3 -0
- package/static/chunk-XTRDKGKG.js +1 -0
- package/static/chunk-YLWTEC3X.js +1 -0
- package/static/chunk-Z5J5F5SX.js +1 -0
- package/static/chunk-ZIJQRARU.js +1 -0
- package/static/index.html +2 -2
- package/static/main-4H5BJY3J.js +9 -0
- package/static/scripts-WRDOQIU5.js +24 -0
- package/static/{styles-A5VYX3CE.css → styles-2C2UNCNB.css} +1 -1
- package/server/applications/spaces/interfaces/space-quota.interface.js +0 -10
- package/server/applications/spaces/interfaces/space-quota.interface.js.map +0 -1
- package/static/chunk-22EANI6R.js +0 -1
- package/static/chunk-3GFGJYMK.js +0 -1
- package/static/chunk-4YGJGZZZ.js +0 -1
- package/static/chunk-5K7HEX3C.js +0 -27
- package/static/chunk-5KLMS6A4.js +0 -1
- package/static/chunk-ATP3BFHV.js +0 -562
- package/static/chunk-BB4G55KE.js +0 -1
- package/static/chunk-EWKSX76T.js +0 -1
- package/static/chunk-FHLACA7V.js +0 -1
- package/static/chunk-GCATNU55.js +0 -1
- package/static/chunk-GYODPCIE.js +0 -1
- package/static/chunk-HZTFYLM5.js +0 -1
- package/static/chunk-JSUKJT6Z.js +0 -1
- package/static/chunk-JXZCNFW7.js +0 -1
- package/static/chunk-LTGFCQR7.js +0 -1
- package/static/chunk-LV3PYKWO.js +0 -1
- package/static/chunk-N2WFNW6M.js +0 -7
- package/static/chunk-ORMRCEGT.js +0 -1
- package/static/chunk-OUTBJSMW.js +0 -1
- package/static/chunk-RS2PX32L.js +0 -1
- package/static/chunk-RSSWH3S2.js +0 -1
- package/static/chunk-RTRJ3KFH.js +0 -1
- package/static/chunk-TKTCBDOG.js +0 -1
- package/static/chunk-V6K2N46L.js +0 -1
- package/static/chunk-XLCCZSQL.js +0 -4
- package/static/chunk-YPEH66GG.js +0 -1
- package/static/chunk-YPOIUQ57.js +0 -1
- package/static/chunk-ZKCFO2OA.js +0 -4
- package/static/main-MZ7HWZXO.js +0 -9
- package/static/scripts-VZVAP2P4.js +0 -30
|
@@ -19,8 +19,8 @@ const _https = /*#__PURE__*/ _interop_require_default(require("https"));
|
|
|
19
19
|
const _nodecrypto = /*#__PURE__*/ _interop_require_default(require("node:crypto"));
|
|
20
20
|
const _nodeos = /*#__PURE__*/ _interop_require_default(require("node:os"));
|
|
21
21
|
const _nodepath = /*#__PURE__*/ _interop_require_default(require("node:path"));
|
|
22
|
-
const _appconstants = require("../../../app.constants");
|
|
23
22
|
const _functions = require("../../../common/functions");
|
|
23
|
+
const _shared = require("../../../common/shared");
|
|
24
24
|
const _configenvironment = require("../../../configuration/config.environment");
|
|
25
25
|
const _cacheservice = require("../../../infrastructure/cache/services/cache.service");
|
|
26
26
|
const _contextmanagerservice = require("../../../infrastructure/context/services/context-manager.service");
|
|
@@ -246,7 +246,7 @@ let FilesOnlyOfficeManager = class FilesOnlyOfficeManager {
|
|
|
246
246
|
lockroot: null,
|
|
247
247
|
locktoken: null,
|
|
248
248
|
lockscope: _webdav.LOCK_SCOPE.SHARED,
|
|
249
|
-
owner: `${
|
|
249
|
+
owner: `${_shared.SERVER_NAME} - ${user.fullName} (${user.email})`
|
|
250
250
|
});
|
|
251
251
|
if (!ok) {
|
|
252
252
|
throw new Error('document is locked');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../backend/src/applications/files/services/files-only-office-manager.service.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { HttpService } from '@nestjs/axios'\nimport { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common'\nimport { JwtService } from '@nestjs/jwt'\nimport { AxiosResponse } from 'axios'\nimport https from 'https'\nimport crypto from 'node:crypto'\nimport os from 'node:os'\nimport path from 'node:path'\nimport { SERVER_NAME } from '../../../app.constants'\nimport { JwtIdentityPayload } from '../../../authentication/interfaces/jwt-payload.interface'\nimport { convertHumanTimeToSeconds, generateShortUUID } from '../../../common/functions'\nimport { configuration } from '../../../configuration/config.environment'\nimport { Cache } from '../../../infrastructure/cache/services/cache.service'\nimport { ContextManager } from '../../../infrastructure/context/services/context-manager.service'\nimport { HTTP_METHOD } from '../../applications.constants'\nimport { SPACE_OPERATION } from '../../spaces/constants/spaces'\nimport { FastifySpaceRequest } from '../../spaces/interfaces/space-request.interface'\nimport type { SpaceEnv } from '../../spaces/models/space-env.model'\nimport { haveSpaceEnvPermissions } from '../../spaces/utils/permissions'\nimport type { UserModel } from '../../users/models/user.model'\nimport { getAvatarBase64 } from '../../users/utils/avatar'\nimport { DEPTH, LOCK_SCOPE } from '../../webdav/constants/webdav'\nimport { CACHE_ONLY_OFFICE } from '../constants/cache'\nimport {\n ONLY_OFFICE_CONVERT_ERROR,\n ONLY_OFFICE_CONVERT_EXTENSIONS,\n ONLY_OFFICE_EXTENSIONS,\n ONLY_OFFICE_INTERNAL_URI,\n ONLY_OFFICE_TOKEN_QUERY_PARAM_NAME\n} from '../constants/only-office'\nimport { API_FILES_ONLY_OFFICE_CALLBACK, API_FILES_ONLY_OFFICE_DOCUMENT } from '../constants/routes'\nimport type { FileProps } from '../interfaces/file-props.interface'\nimport { OnlyOfficeCallBack, OnlyOfficeConfig, OnlyOfficeConvertForm, OnlyOfficeReqConfig } from '../interfaces/only-office-config.interface'\nimport { copyFileContent, fileSize, getProps, isPathExists, isPathIsDir, removeFiles, uniqueFilePathFromDir, writeFromStream } from '../utils/files'\nimport { FilesLockManager } from './files-lock-manager.service'\nimport { FilesQueries } from './files-queries.service'\n\n@Injectable()\nexport class FilesOnlyOfficeManager {\n private logger = new Logger(FilesOnlyOfficeManager.name)\n private readonly externalOnlyOfficeServer = configuration.applications.files.onlyoffice.externalServer || null\n private readonly rejectUnauthorized: boolean = !configuration.applications.files.onlyoffice?.verifySSL\n private readonly convertUrl = this.externalOnlyOfficeServer ? `${this.externalOnlyOfficeServer}/ConvertService.ashx` : null\n private readonly expiration = convertHumanTimeToSeconds(configuration.auth.token.refresh.expiration)\n private readonly mobileRegex: RegExp = /android|webos|iphone|ipad|ipod|blackberry|windows phone|opera mini|iemobile|mobile/i\n\n constructor(\n private readonly http: HttpService,\n private readonly contextManager: ContextManager,\n private readonly cache: Cache,\n private readonly jwt: JwtService,\n private readonly filesLockManager: FilesLockManager,\n private readonly filesQueries: FilesQueries\n ) {}\n\n getStatus(): { enabled: boolean } {\n return { enabled: configuration.applications.files.onlyoffice.enabled }\n }\n\n async getSettings(user: UserModel, space: SpaceEnv, mode: 'edit' | 'view', req: FastifySpaceRequest): Promise<OnlyOfficeReqConfig> {\n if (!(await isPathExists(space.realPath))) {\n throw new HttpException('Document not found', HttpStatus.NOT_FOUND)\n }\n if (await isPathIsDir(space.realPath)) {\n throw new HttpException('Document must be a file', HttpStatus.BAD_REQUEST)\n }\n const fileExtension = path.extname(space.realPath).slice(1)\n if (!ONLY_OFFICE_EXTENSIONS.VIEWABLE.has(fileExtension) && !ONLY_OFFICE_EXTENSIONS.EDITABLE.has(fileExtension)) {\n throw new HttpException('Document not supported', HttpStatus.BAD_REQUEST)\n }\n if (mode === 'edit' && (!ONLY_OFFICE_EXTENSIONS.EDITABLE.has(fileExtension) || !haveSpaceEnvPermissions(space, SPACE_OPERATION.MODIFY))) {\n mode = 'view'\n }\n if (mode === 'edit') {\n // check lock conflicts\n try {\n await this.filesLockManager.checkConflicts(space.dbFile, DEPTH.RESOURCE, { userId: user.id, lockScope: LOCK_SCOPE.SHARED })\n } catch {\n throw new HttpException('The file is locked', HttpStatus.LOCKED)\n }\n }\n const isMobile: boolean = this.mobileRegex.test(req.headers['user-agent'])\n const fileProps: FileProps = await getProps(space.realPath, space.dbFile.path)\n const fileId: string = ((await this.filesQueries.getSpaceFileId(fileProps, space.dbFile)) || fileProps.id).toString()\n const userToken: string = await this.genUserToken(user)\n const fileUrl = this.getDocumentUrl(space, userToken)\n const callBackUrl = this.getCallBackUrl(space, userToken, fileId)\n const config: OnlyOfficeReqConfig = await this.genConfiguration(user, space, mode, fileId, fileUrl, fileExtension, callBackUrl, isMobile)\n config.config.token = await this.genPayloadToken(config.config)\n return config\n }\n\n async callBack(user: UserModel, space: SpaceEnv, token: string, fileId: string) {\n const callBackData: OnlyOfficeCallBack = await this.jwt.verifyAsync(token, { secret: configuration.applications.files.onlyoffice.secret })\n try {\n switch (callBackData.status) {\n case 1:\n await this.checkFileLock(user, space, callBackData)\n this.logger.debug(`document is being edited : ${space.url}`)\n break\n case 2:\n await this.checkFileLock(user, space, callBackData)\n if (callBackData.notmodified) {\n this.logger.debug(`document was edited but closed with no changes : ${space.url}`)\n } else {\n this.logger.debug(`document was edited but not saved (let's do it) : ${space.url}`)\n await this.saveDocument(space, callBackData.url)\n }\n await this.removeDocumentKey(fileId, space)\n break\n case 3:\n this.logger.error(`document cannot be saved, an error has occurred (try to save it) : ${space.url}`)\n await this.saveDocument(space, callBackData.url)\n break\n case 4:\n await this.removeFileLock(user.id, space)\n await this.removeDocumentKey(fileId, space)\n this.logger.debug(`document was closed with no changes : ${space.url}`)\n break\n case 6:\n this.logger.debug(`document is edited but save was requested : ${space.url}`)\n await this.saveDocument(space, callBackData.url)\n break\n case 7:\n this.logger.error(`document cannot be force saved, an error has occurred (try to save it) : ${space.url}`)\n await this.saveDocument(space, callBackData.url)\n break\n default:\n this.logger.error('unhandled case')\n }\n } catch (e) {\n this.logger.error(`${this.callBack.name} - ${e.message} : ${space.url}`)\n return { error: e.message }\n }\n return { error: 0 }\n }\n\n private async genConfiguration(\n user: UserModel,\n space: SpaceEnv,\n mode: 'edit' | 'view',\n fileId: string,\n fileUrl: string,\n fileExtension: string,\n callBackUrl: string,\n isMobile: boolean\n ): Promise<OnlyOfficeReqConfig> {\n const documentType = ONLY_OFFICE_EXTENSIONS.EDITABLE.get(fileExtension) || ONLY_OFFICE_EXTENSIONS.VIEWABLE.get(fileExtension)\n return {\n documentServerUrl: this.externalOnlyOfficeServer || `${this.contextManager.get('headerOriginUrl')}${ONLY_OFFICE_INTERNAL_URI}`,\n config: {\n type: isMobile ? 'mobile' : 'desktop',\n height: '100%',\n width: '100%',\n documentType: documentType,\n document: {\n title: path.basename(space.relativeUrl),\n fileType: fileExtension,\n key: await this.getDocumentKey(fileId),\n permissions: {\n download: true,\n edit: mode === 'edit',\n changeHistory: false,\n comment: true,\n fillForms: true,\n print: true,\n review: mode === 'edit'\n },\n url: fileUrl\n },\n editorConfig: {\n mode: mode,\n lang: 'en',\n region: 'en',\n callbackUrl: callBackUrl,\n user: { id: user.id.toString(), name: `${user.fullName} (${user.email})`, image: await getAvatarBase64(user.login) },\n coEditing: {\n mode: 'fast',\n change: true\n },\n embedded: {\n embedUrl: fileUrl,\n saveUrl: fileUrl,\n shareUrl: fileUrl,\n toolbarDocked: 'top'\n },\n customization: {\n about: false,\n autosave: false,\n forcesave: true,\n zoom: documentType === 'slide' ? 60 : 90,\n help: false,\n features: { featuresTips: false },\n plugins: false\n }\n }\n }\n }\n }\n\n private getDocumentUrl(space: SpaceEnv, token: string): string {\n // user refresh token is used here for long session\n return `${this.contextManager.get('headerOriginUrl')}${API_FILES_ONLY_OFFICE_DOCUMENT}/${space.url}?${ONLY_OFFICE_TOKEN_QUERY_PARAM_NAME}=${token}`\n }\n\n private getCallBackUrl(space: SpaceEnv, token: string, fileId: string): string {\n // user refresh token is used here for long session\n return `${this.contextManager.get('headerOriginUrl')}${API_FILES_ONLY_OFFICE_CALLBACK}/${space.url}?fid=${fileId}&${ONLY_OFFICE_TOKEN_QUERY_PARAM_NAME}=${token}`\n }\n\n private genPayloadToken(payload: OnlyOfficeConfig | OnlyOfficeConvertForm): Promise<string> {\n return this.jwt.signAsync(payload, { secret: configuration.applications.files.onlyoffice.secret, expiresIn: 60 })\n }\n\n private genUserToken(user: UserModel): Promise<string> {\n // use refresh expiration to allow long session\n return this.jwt.signAsync(\n {\n identity: {\n id: user.id,\n login: user.login,\n email: user.email,\n fullName: user.fullName,\n language: user.language,\n role: user.role,\n applications: user.applications\n } satisfies JwtIdentityPayload\n },\n {\n secret: configuration.auth.token.access.secret,\n expiresIn: this.expiration\n }\n )\n }\n\n private async checkFileLock(user: UserModel, space: SpaceEnv, callBackData: OnlyOfficeCallBack) {\n for (const action of callBackData.actions) {\n if (action.type === 0) {\n // disconnect\n await this.removeFileLock(parseInt(action.userid), space)\n } else if (action.type === 1) {\n // connect\n await this.genFileLock(user, space)\n }\n }\n }\n\n private async genFileLock(user: UserModel, space: SpaceEnv): Promise<void> {\n const [ok, _fileLock] = await this.filesLockManager.create(user, space.dbFile, DEPTH.RESOURCE, this.expiration, {\n lockroot: null,\n locktoken: null,\n lockscope: LOCK_SCOPE.SHARED,\n owner: `${SERVER_NAME} - ${user.fullName} (${user.email})`\n })\n if (!ok) {\n throw new Error('document is locked')\n }\n }\n\n private async removeFileLock(userId: number, space: SpaceEnv): Promise<void> {\n for (const lock of await this.filesLockManager.getLocksByPath(space.dbFile)) {\n if (lock.owner.id === userId) {\n await this.filesLockManager.removeLock(lock.key)\n }\n }\n }\n\n private async removeDocumentKey(fileId: string, space: SpaceEnv): Promise<void> {\n if (!(await this.filesLockManager.isPathLocked(space.dbFile))) {\n const cacheKey = this.getCacheKey(fileId)\n const r = await this.cache.del(cacheKey)\n this.logger.debug(`${this.removeDocumentKey.name} - ${cacheKey} ${r ? '' : 'not'} removed`)\n }\n }\n\n private async getDocumentKey(fileId: string): Promise<string> {\n const cacheKey = this.getCacheKey(fileId)\n const existingDocKey: string = await this.cache.get(cacheKey)\n if (existingDocKey) {\n return existingDocKey\n }\n const docKey = generateShortUUID(64)\n await this.cache.set(cacheKey, docKey, this.expiration)\n this.logger.debug(`${this.getDocumentKey.name} - ${cacheKey} (${docKey}) created`)\n return docKey\n }\n\n private async saveDocument(space: SpaceEnv, url: string): Promise<void> {\n /* url format:\n https://onlyoffice-server.com/cache/files/data/-33120641_7158/output.pptx/output.pptx\n ?md5=duFHKC-5d47s-RRcYn3hAw&expires=1739400549&shardkey=-33120641&filename=output.pptx\n */\n const urlParams = new URLSearchParams(url.split('?').at(-1))\n // it is not the md5 of the file but a md5 generated by the combination of the elements of the url\n const md5: string = urlParams.get('md5')\n const tmpFilePath = await uniqueFilePathFromDir(path.join(os.tmpdir(), `${md5}-${urlParams.get('filename')}`))\n\n // convert remote file to the local file with the current extension if these extensions aren't equal\n const localExtension = path.extname(space.realPath).slice(1)\n const remoteExtension = path.extname(urlParams.get('filename')).slice(1)\n\n let downloadUrl: string\n if (localExtension !== remoteExtension && !ONLY_OFFICE_CONVERT_EXTENSIONS.ALLOW_AUTO.has(localExtension)) {\n if (ONLY_OFFICE_CONVERT_EXTENSIONS.FROM.has(remoteExtension) && ONLY_OFFICE_CONVERT_EXTENSIONS.TO.has(localExtension)) {\n downloadUrl = await this.convertDocument(urlParams.get('shardkey'), url, remoteExtension, localExtension, space.url)\n } else {\n throw new Error(`document cannot be converted from ${remoteExtension} -> ${localExtension} : ${space.url}`)\n }\n } else {\n downloadUrl = url\n }\n\n // download file\n let res: AxiosResponse\n try {\n res = await this.http.axiosRef({\n method: HTTP_METHOD.GET,\n url: downloadUrl,\n responseType: 'stream',\n httpsAgent: new https.Agent({ rejectUnauthorized: this.rejectUnauthorized })\n })\n await writeFromStream(tmpFilePath, res.data)\n } catch (e) {\n throw new Error(`unable to get document : ${e.message}`)\n }\n\n // try to verify the downloaded size\n const contentLength = parseInt(res.headers['content-length'], 10)\n if (!isNaN(contentLength)) {\n if (contentLength === 0) {\n this.logger.debug(`${this.saveDocument.name} - content length is 0 : ${space.url}`)\n return\n }\n const tmpFileSize = await fileSize(tmpFilePath)\n if (tmpFileSize !== contentLength) {\n throw new Error(`document size differs (${tmpFileSize} != ${contentLength})`)\n }\n }\n // copy contents to avoid inode changes (fileId in some cases)\n try {\n await copyFileContent(tmpFilePath, space.realPath)\n await removeFiles(tmpFilePath)\n } catch (e) {\n throw new Error(`unable to save document : ${e.message}`)\n }\n }\n\n private async convertDocument(id: string, url: string, fileType: string, outputType: string, spaceUrl: string): Promise<string> {\n const key: string = `${id}-${crypto.randomBytes(20).toString('hex')}`.slice(0, 20).replace('-', '_')\n const payload: OnlyOfficeConvertForm = {\n key: key,\n url: url,\n filetype: fileType,\n outputtype: outputType,\n async: false\n }\n payload.token = await this.genPayloadToken(payload)\n let result: { fileUrl?: string; fileType?: string; endConvert?: boolean; error?: number }\n try {\n const res: AxiosResponse = await this.http.axiosRef({\n method: HTTP_METHOD.POST,\n url: this.convertUrl,\n data: payload,\n httpsAgent: new https.Agent({ rejectUnauthorized: this.rejectUnauthorized })\n })\n result = res.data\n } catch (e) {\n throw new Error(`convert failed with status : ${e.response.status}`)\n }\n if (result.error) {\n throw new Error(`convert failed with reason : ${ONLY_OFFICE_CONVERT_ERROR.get(result.error)}`)\n }\n if (result.endConvert) {\n this.logger.log(`${this.convertDocument.name} - ${fileType} -> ${outputType} : ${spaceUrl}`)\n return result.fileUrl\n }\n }\n\n private getCacheKey(fileId: string): string {\n return `${CACHE_ONLY_OFFICE}|${fileId}`\n }\n}\n"],"names":["FilesOnlyOfficeManager","getStatus","enabled","configuration","applications","files","onlyoffice","getSettings","user","space","mode","req","isPathExists","realPath","HttpException","HttpStatus","NOT_FOUND","isPathIsDir","BAD_REQUEST","fileExtension","path","extname","slice","ONLY_OFFICE_EXTENSIONS","VIEWABLE","has","EDITABLE","haveSpaceEnvPermissions","SPACE_OPERATION","MODIFY","filesLockManager","checkConflicts","dbFile","DEPTH","RESOURCE","userId","id","lockScope","LOCK_SCOPE","SHARED","LOCKED","isMobile","mobileRegex","test","headers","fileProps","getProps","fileId","filesQueries","getSpaceFileId","toString","userToken","genUserToken","fileUrl","getDocumentUrl","callBackUrl","getCallBackUrl","config","genConfiguration","token","genPayloadToken","callBack","callBackData","jwt","verifyAsync","secret","status","checkFileLock","logger","debug","url","notmodified","saveDocument","removeDocumentKey","error","removeFileLock","e","name","message","documentType","get","documentServerUrl","externalOnlyOfficeServer","contextManager","ONLY_OFFICE_INTERNAL_URI","type","height","width","document","title","basename","relativeUrl","fileType","key","getDocumentKey","permissions","download","edit","changeHistory","comment","fillForms","print","review","editorConfig","lang","region","callbackUrl","fullName","email","image","getAvatarBase64","login","coEditing","change","embedded","embedUrl","saveUrl","shareUrl","toolbarDocked","customization","about","autosave","forcesave","zoom","help","features","featuresTips","plugins","API_FILES_ONLY_OFFICE_DOCUMENT","ONLY_OFFICE_TOKEN_QUERY_PARAM_NAME","API_FILES_ONLY_OFFICE_CALLBACK","payload","signAsync","expiresIn","identity","language","role","auth","access","expiration","action","actions","parseInt","userid","genFileLock","ok","_fileLock","create","lockroot","locktoken","lockscope","owner","SERVER_NAME","Error","lock","getLocksByPath","removeLock","isPathLocked","cacheKey","getCacheKey","r","cache","del","existingDocKey","docKey","generateShortUUID","set","urlParams","URLSearchParams","split","at","md5","tmpFilePath","uniqueFilePathFromDir","join","os","tmpdir","localExtension","remoteExtension","downloadUrl","ONLY_OFFICE_CONVERT_EXTENSIONS","ALLOW_AUTO","FROM","TO","convertDocument","res","http","axiosRef","method","HTTP_METHOD","GET","responseType","httpsAgent","https","Agent","rejectUnauthorized","writeFromStream","data","contentLength","isNaN","tmpFileSize","fileSize","copyFileContent","removeFiles","outputType","spaceUrl","crypto","randomBytes","replace","filetype","outputtype","async","result","POST","convertUrl","response","ONLY_OFFICE_CONVERT_ERROR","endConvert","log","CACHE_ONLY_OFFICE","Logger","externalServer","verifySSL","convertHumanTimeToSeconds","refresh"],"mappings":"AAAA;;;;CAIC;;;;+BAwCYA;;;eAAAA;;;uBAtCe;wBACkC;qBACnC;8DAET;mEACC;+DACJ;iEACE;8BACW;2BAEiC;mCAC/B;8BACR;uCACS;uCACH;wBACI;6BAGQ;wBAER;wBACE;uBACA;4BAO3B;wBACwE;uBAGqD;yCACnG;qCACJ;;;;;;;;;;;;;;;AAGtB,IAAA,AAAMA,yBAAN,MAAMA;IAiBXC,YAAkC;QAChC,OAAO;YAAEC,SAASC,gCAAa,CAACC,YAAY,CAACC,KAAK,CAACC,UAAU,CAACJ,OAAO;QAAC;IACxE;IAEA,MAAMK,YAAYC,IAAe,EAAEC,KAAe,EAAEC,IAAqB,EAAEC,GAAwB,EAAgC;QACjI,IAAI,CAAE,MAAMC,IAAAA,mBAAY,EAACH,MAAMI,QAAQ,GAAI;YACzC,MAAM,IAAIC,qBAAa,CAAC,sBAAsBC,kBAAU,CAACC,SAAS;QACpE;QACA,IAAI,MAAMC,IAAAA,kBAAW,EAACR,MAAMI,QAAQ,GAAG;YACrC,MAAM,IAAIC,qBAAa,CAAC,2BAA2BC,kBAAU,CAACG,WAAW;QAC3E;QACA,MAAMC,gBAAgBC,iBAAI,CAACC,OAAO,CAACZ,MAAMI,QAAQ,EAAES,KAAK,CAAC;QACzD,IAAI,CAACC,kCAAsB,CAACC,QAAQ,CAACC,GAAG,CAACN,kBAAkB,CAACI,kCAAsB,CAACG,QAAQ,CAACD,GAAG,CAACN,gBAAgB;YAC9G,MAAM,IAAIL,qBAAa,CAAC,0BAA0BC,kBAAU,CAACG,WAAW;QAC1E;QACA,IAAIR,SAAS,UAAW,CAAA,CAACa,kCAAsB,CAACG,QAAQ,CAACD,GAAG,CAACN,kBAAkB,CAACQ,IAAAA,oCAAuB,EAAClB,OAAOmB,uBAAe,CAACC,MAAM,CAAA,GAAI;YACvInB,OAAO;QACT;QACA,IAAIA,SAAS,QAAQ;YACnB,uBAAuB;YACvB,IAAI;gBACF,MAAM,IAAI,CAACoB,gBAAgB,CAACC,cAAc,CAACtB,MAAMuB,MAAM,EAAEC,aAAK,CAACC,QAAQ,EAAE;oBAAEC,QAAQ3B,KAAK4B,EAAE;oBAAEC,WAAWC,kBAAU,CAACC,MAAM;gBAAC;YAC3H,EAAE,OAAM;gBACN,MAAM,IAAIzB,qBAAa,CAAC,sBAAsBC,kBAAU,CAACyB,MAAM;YACjE;QACF;QACA,MAAMC,WAAoB,IAAI,CAACC,WAAW,CAACC,IAAI,CAAChC,IAAIiC,OAAO,CAAC,aAAa;QACzE,MAAMC,YAAuB,MAAMC,IAAAA,eAAQ,EAACrC,MAAMI,QAAQ,EAAEJ,MAAMuB,MAAM,CAACZ,IAAI;QAC7E,MAAM2B,SAAiB,AAAC,CAAA,AAAC,MAAM,IAAI,CAACC,YAAY,CAACC,cAAc,CAACJ,WAAWpC,MAAMuB,MAAM,KAAMa,UAAUT,EAAE,AAAD,EAAGc,QAAQ;QACnH,MAAMC,YAAoB,MAAM,IAAI,CAACC,YAAY,CAAC5C;QAClD,MAAM6C,UAAU,IAAI,CAACC,cAAc,CAAC7C,OAAO0C;QAC3C,MAAMI,cAAc,IAAI,CAACC,cAAc,CAAC/C,OAAO0C,WAAWJ;QAC1D,MAAMU,SAA8B,MAAM,IAAI,CAACC,gBAAgB,CAAClD,MAAMC,OAAOC,MAAMqC,QAAQM,SAASlC,eAAeoC,aAAad;QAChIgB,OAAOA,MAAM,CAACE,KAAK,GAAG,MAAM,IAAI,CAACC,eAAe,CAACH,OAAOA,MAAM;QAC9D,OAAOA;IACT;IAEA,MAAMI,SAASrD,IAAe,EAAEC,KAAe,EAAEkD,KAAa,EAAEZ,MAAc,EAAE;QAC9E,MAAMe,eAAmC,MAAM,IAAI,CAACC,GAAG,CAACC,WAAW,CAACL,OAAO;YAAEM,QAAQ9D,gCAAa,CAACC,YAAY,CAACC,KAAK,CAACC,UAAU,CAAC2D,MAAM;QAAC;QACxI,IAAI;YACF,OAAQH,aAAaI,MAAM;gBACzB,KAAK;oBACH,MAAM,IAAI,CAACC,aAAa,CAAC3D,MAAMC,OAAOqD;oBACtC,IAAI,CAACM,MAAM,CAACC,KAAK,CAAC,CAAC,2BAA2B,EAAE5D,MAAM6D,GAAG,EAAE;oBAC3D;gBACF,KAAK;oBACH,MAAM,IAAI,CAACH,aAAa,CAAC3D,MAAMC,OAAOqD;oBACtC,IAAIA,aAAaS,WAAW,EAAE;wBAC5B,IAAI,CAACH,MAAM,CAACC,KAAK,CAAC,CAAC,iDAAiD,EAAE5D,MAAM6D,GAAG,EAAE;oBACnF,OAAO;wBACL,IAAI,CAACF,MAAM,CAACC,KAAK,CAAC,CAAC,kDAAkD,EAAE5D,MAAM6D,GAAG,EAAE;wBAClF,MAAM,IAAI,CAACE,YAAY,CAAC/D,OAAOqD,aAAaQ,GAAG;oBACjD;oBACA,MAAM,IAAI,CAACG,iBAAiB,CAAC1B,QAAQtC;oBACrC;gBACF,KAAK;oBACH,IAAI,CAAC2D,MAAM,CAACM,KAAK,CAAC,CAAC,mEAAmE,EAAEjE,MAAM6D,GAAG,EAAE;oBACnG,MAAM,IAAI,CAACE,YAAY,CAAC/D,OAAOqD,aAAaQ,GAAG;oBAC/C;gBACF,KAAK;oBACH,MAAM,IAAI,CAACK,cAAc,CAACnE,KAAK4B,EAAE,EAAE3B;oBACnC,MAAM,IAAI,CAACgE,iBAAiB,CAAC1B,QAAQtC;oBACrC,IAAI,CAAC2D,MAAM,CAACC,KAAK,CAAC,CAAC,sCAAsC,EAAE5D,MAAM6D,GAAG,EAAE;oBACtE;gBACF,KAAK;oBACH,IAAI,CAACF,MAAM,CAACC,KAAK,CAAC,CAAC,4CAA4C,EAAE5D,MAAM6D,GAAG,EAAE;oBAC5E,MAAM,IAAI,CAACE,YAAY,CAAC/D,OAAOqD,aAAaQ,GAAG;oBAC/C;gBACF,KAAK;oBACH,IAAI,CAACF,MAAM,CAACM,KAAK,CAAC,CAAC,yEAAyE,EAAEjE,MAAM6D,GAAG,EAAE;oBACzG,MAAM,IAAI,CAACE,YAAY,CAAC/D,OAAOqD,aAAaQ,GAAG;oBAC/C;gBACF;oBACE,IAAI,CAACF,MAAM,CAACM,KAAK,CAAC;YACtB;QACF,EAAE,OAAOE,GAAG;YACV,IAAI,CAACR,MAAM,CAACM,KAAK,CAAC,GAAG,IAAI,CAACb,QAAQ,CAACgB,IAAI,CAAC,GAAG,EAAED,EAAEE,OAAO,CAAC,GAAG,EAAErE,MAAM6D,GAAG,EAAE;YACvE,OAAO;gBAAEI,OAAOE,EAAEE,OAAO;YAAC;QAC5B;QACA,OAAO;YAAEJ,OAAO;QAAE;IACpB;IAEA,MAAchB,iBACZlD,IAAe,EACfC,KAAe,EACfC,IAAqB,EACrBqC,MAAc,EACdM,OAAe,EACflC,aAAqB,EACrBoC,WAAmB,EACnBd,QAAiB,EACa;QAC9B,MAAMsC,eAAexD,kCAAsB,CAACG,QAAQ,CAACsD,GAAG,CAAC7D,kBAAkBI,kCAAsB,CAACC,QAAQ,CAACwD,GAAG,CAAC7D;QAC/G,OAAO;YACL8D,mBAAmB,IAAI,CAACC,wBAAwB,IAAI,GAAG,IAAI,CAACC,cAAc,CAACH,GAAG,CAAC,qBAAqBI,oCAAwB,EAAE;YAC9H3B,QAAQ;gBACN4B,MAAM5C,WAAW,WAAW;gBAC5B6C,QAAQ;gBACRC,OAAO;gBACPR,cAAcA;gBACdS,UAAU;oBACRC,OAAOrE,iBAAI,CAACsE,QAAQ,CAACjF,MAAMkF,WAAW;oBACtCC,UAAUzE;oBACV0E,KAAK,MAAM,IAAI,CAACC,cAAc,CAAC/C;oBAC/BgD,aAAa;wBACXC,UAAU;wBACVC,MAAMvF,SAAS;wBACfwF,eAAe;wBACfC,SAAS;wBACTC,WAAW;wBACXC,OAAO;wBACPC,QAAQ5F,SAAS;oBACnB;oBACA4D,KAAKjB;gBACP;gBACAkD,cAAc;oBACZ7F,MAAMA;oBACN8F,MAAM;oBACNC,QAAQ;oBACRC,aAAanD;oBACb/C,MAAM;wBAAE4B,IAAI5B,KAAK4B,EAAE,CAACc,QAAQ;wBAAI2B,MAAM,GAAGrE,KAAKmG,QAAQ,CAAC,EAAE,EAAEnG,KAAKoG,KAAK,CAAC,CAAC,CAAC;wBAAEC,OAAO,MAAMC,IAAAA,uBAAe,EAACtG,KAAKuG,KAAK;oBAAE;oBACnHC,WAAW;wBACTtG,MAAM;wBACNuG,QAAQ;oBACV;oBACAC,UAAU;wBACRC,UAAU9D;wBACV+D,SAAS/D;wBACTgE,UAAUhE;wBACViE,eAAe;oBACjB;oBACAC,eAAe;wBACbC,OAAO;wBACPC,UAAU;wBACVC,WAAW;wBACXC,MAAM5C,iBAAiB,UAAU,KAAK;wBACtC6C,MAAM;wBACNC,UAAU;4BAAEC,cAAc;wBAAM;wBAChCC,SAAS;oBACX;gBACF;YACF;QACF;IACF;IAEQzE,eAAe7C,KAAe,EAAEkD,KAAa,EAAU;QAC7D,mDAAmD;QACnD,OAAO,GAAG,IAAI,CAACwB,cAAc,CAACH,GAAG,CAAC,qBAAqBgD,sCAA8B,CAAC,CAAC,EAAEvH,MAAM6D,GAAG,CAAC,CAAC,EAAE2D,8CAAkC,CAAC,CAAC,EAAEtE,OAAO;IACrJ;IAEQH,eAAe/C,KAAe,EAAEkD,KAAa,EAAEZ,MAAc,EAAU;QAC7E,mDAAmD;QACnD,OAAO,GAAG,IAAI,CAACoC,cAAc,CAACH,GAAG,CAAC,qBAAqBkD,sCAA8B,CAAC,CAAC,EAAEzH,MAAM6D,GAAG,CAAC,KAAK,EAAEvB,OAAO,CAAC,EAAEkF,8CAAkC,CAAC,CAAC,EAAEtE,OAAO;IACnK;IAEQC,gBAAgBuE,OAAiD,EAAmB;QAC1F,OAAO,IAAI,CAACpE,GAAG,CAACqE,SAAS,CAACD,SAAS;YAAElE,QAAQ9D,gCAAa,CAACC,YAAY,CAACC,KAAK,CAACC,UAAU,CAAC2D,MAAM;YAAEoE,WAAW;QAAG;IACjH;IAEQjF,aAAa5C,IAAe,EAAmB;QACrD,+CAA+C;QAC/C,OAAO,IAAI,CAACuD,GAAG,CAACqE,SAAS,CACvB;YACEE,UAAU;gBACRlG,IAAI5B,KAAK4B,EAAE;gBACX2E,OAAOvG,KAAKuG,KAAK;gBACjBH,OAAOpG,KAAKoG,KAAK;gBACjBD,UAAUnG,KAAKmG,QAAQ;gBACvB4B,UAAU/H,KAAK+H,QAAQ;gBACvBC,MAAMhI,KAAKgI,IAAI;gBACfpI,cAAcI,KAAKJ,YAAY;YACjC;QACF,GACA;YACE6D,QAAQ9D,gCAAa,CAACsI,IAAI,CAAC9E,KAAK,CAAC+E,MAAM,CAACzE,MAAM;YAC9CoE,WAAW,IAAI,CAACM,UAAU;QAC5B;IAEJ;IAEA,MAAcxE,cAAc3D,IAAe,EAAEC,KAAe,EAAEqD,YAAgC,EAAE;QAC9F,KAAK,MAAM8E,UAAU9E,aAAa+E,OAAO,CAAE;YACzC,IAAID,OAAOvD,IAAI,KAAK,GAAG;gBACrB,aAAa;gBACb,MAAM,IAAI,CAACV,cAAc,CAACmE,SAASF,OAAOG,MAAM,GAAGtI;YACrD,OAAO,IAAImI,OAAOvD,IAAI,KAAK,GAAG;gBAC5B,UAAU;gBACV,MAAM,IAAI,CAAC2D,WAAW,CAACxI,MAAMC;YAC/B;QACF;IACF;IAEA,MAAcuI,YAAYxI,IAAe,EAAEC,KAAe,EAAiB;QACzE,MAAM,CAACwI,IAAIC,UAAU,GAAG,MAAM,IAAI,CAACpH,gBAAgB,CAACqH,MAAM,CAAC3I,MAAMC,MAAMuB,MAAM,EAAEC,aAAK,CAACC,QAAQ,EAAE,IAAI,CAACyG,UAAU,EAAE;YAC9GS,UAAU;YACVC,WAAW;YACXC,WAAWhH,kBAAU,CAACC,MAAM;YAC5BgH,OAAO,GAAGC,yBAAW,CAAC,GAAG,EAAEhJ,KAAKmG,QAAQ,CAAC,EAAE,EAAEnG,KAAKoG,KAAK,CAAC,CAAC,CAAC;QAC5D;QACA,IAAI,CAACqC,IAAI;YACP,MAAM,IAAIQ,MAAM;QAClB;IACF;IAEA,MAAc9E,eAAexC,MAAc,EAAE1B,KAAe,EAAiB;QAC3E,KAAK,MAAMiJ,QAAQ,CAAA,MAAM,IAAI,CAAC5H,gBAAgB,CAAC6H,cAAc,CAAClJ,MAAMuB,MAAM,CAAA,EAAG;YAC3E,IAAI0H,KAAKH,KAAK,CAACnH,EAAE,KAAKD,QAAQ;gBAC5B,MAAM,IAAI,CAACL,gBAAgB,CAAC8H,UAAU,CAACF,KAAK7D,GAAG;YACjD;QACF;IACF;IAEA,MAAcpB,kBAAkB1B,MAAc,EAAEtC,KAAe,EAAiB;QAC9E,IAAI,CAAE,MAAM,IAAI,CAACqB,gBAAgB,CAAC+H,YAAY,CAACpJ,MAAMuB,MAAM,GAAI;YAC7D,MAAM8H,WAAW,IAAI,CAACC,WAAW,CAAChH;YAClC,MAAMiH,IAAI,MAAM,IAAI,CAACC,KAAK,CAACC,GAAG,CAACJ;YAC/B,IAAI,CAAC1F,MAAM,CAACC,KAAK,CAAC,GAAG,IAAI,CAACI,iBAAiB,CAACI,IAAI,CAAC,GAAG,EAAEiF,SAAS,CAAC,EAAEE,IAAI,KAAK,MAAM,QAAQ,CAAC;QAC5F;IACF;IAEA,MAAclE,eAAe/C,MAAc,EAAmB;QAC5D,MAAM+G,WAAW,IAAI,CAACC,WAAW,CAAChH;QAClC,MAAMoH,iBAAyB,MAAM,IAAI,CAACF,KAAK,CAACjF,GAAG,CAAC8E;QACpD,IAAIK,gBAAgB;YAClB,OAAOA;QACT;QACA,MAAMC,SAASC,IAAAA,4BAAiB,EAAC;QACjC,MAAM,IAAI,CAACJ,KAAK,CAACK,GAAG,CAACR,UAAUM,QAAQ,IAAI,CAACzB,UAAU;QACtD,IAAI,CAACvE,MAAM,CAACC,KAAK,CAAC,GAAG,IAAI,CAACyB,cAAc,CAACjB,IAAI,CAAC,GAAG,EAAEiF,SAAS,EAAE,EAAEM,OAAO,SAAS,CAAC;QACjF,OAAOA;IACT;IAEA,MAAc5F,aAAa/D,KAAe,EAAE6D,GAAW,EAAiB;QACtE;;;KAGC,GACD,MAAMiG,YAAY,IAAIC,gBAAgBlG,IAAImG,KAAK,CAAC,KAAKC,EAAE,CAAC,CAAC;QACzD,kGAAkG;QAClG,MAAMC,MAAcJ,UAAUvF,GAAG,CAAC;QAClC,MAAM4F,cAAc,MAAMC,IAAAA,4BAAqB,EAACzJ,iBAAI,CAAC0J,IAAI,CAACC,eAAE,CAACC,MAAM,IAAI,GAAGL,IAAI,CAAC,EAAEJ,UAAUvF,GAAG,CAAC,aAAa;QAE5G,oGAAoG;QACpG,MAAMiG,iBAAiB7J,iBAAI,CAACC,OAAO,CAACZ,MAAMI,QAAQ,EAAES,KAAK,CAAC;QAC1D,MAAM4J,kBAAkB9J,iBAAI,CAACC,OAAO,CAACkJ,UAAUvF,GAAG,CAAC,aAAa1D,KAAK,CAAC;QAEtE,IAAI6J;QACJ,IAAIF,mBAAmBC,mBAAmB,CAACE,0CAA8B,CAACC,UAAU,CAAC5J,GAAG,CAACwJ,iBAAiB;YACxG,IAAIG,0CAA8B,CAACE,IAAI,CAAC7J,GAAG,CAACyJ,oBAAoBE,0CAA8B,CAACG,EAAE,CAAC9J,GAAG,CAACwJ,iBAAiB;gBACrHE,cAAc,MAAM,IAAI,CAACK,eAAe,CAACjB,UAAUvF,GAAG,CAAC,aAAaV,KAAK4G,iBAAiBD,gBAAgBxK,MAAM6D,GAAG;YACrH,OAAO;gBACL,MAAM,IAAImF,MAAM,CAAC,kCAAkC,EAAEyB,gBAAgB,IAAI,EAAED,eAAe,GAAG,EAAExK,MAAM6D,GAAG,EAAE;YAC5G;QACF,OAAO;YACL6G,cAAc7G;QAChB;QAEA,gBAAgB;QAChB,IAAImH;QACJ,IAAI;YACFA,MAAM,MAAM,IAAI,CAACC,IAAI,CAACC,QAAQ,CAAC;gBAC7BC,QAAQC,kCAAW,CAACC,GAAG;gBACvBxH,KAAK6G;gBACLY,cAAc;gBACdC,YAAY,IAAIC,cAAK,CAACC,KAAK,CAAC;oBAAEC,oBAAoB,IAAI,CAACA,kBAAkB;gBAAC;YAC5E;YACA,MAAMC,IAAAA,sBAAe,EAACxB,aAAaa,IAAIY,IAAI;QAC7C,EAAE,OAAOzH,GAAG;YACV,MAAM,IAAI6E,MAAM,CAAC,yBAAyB,EAAE7E,EAAEE,OAAO,EAAE;QACzD;QAEA,oCAAoC;QACpC,MAAMwH,gBAAgBxD,SAAS2C,IAAI7I,OAAO,CAAC,iBAAiB,EAAE;QAC9D,IAAI,CAAC2J,MAAMD,gBAAgB;YACzB,IAAIA,kBAAkB,GAAG;gBACvB,IAAI,CAAClI,MAAM,CAACC,KAAK,CAAC,GAAG,IAAI,CAACG,YAAY,CAACK,IAAI,CAAC,yBAAyB,EAAEpE,MAAM6D,GAAG,EAAE;gBAClF;YACF;YACA,MAAMkI,cAAc,MAAMC,IAAAA,eAAQ,EAAC7B;YACnC,IAAI4B,gBAAgBF,eAAe;gBACjC,MAAM,IAAI7C,MAAM,CAAC,uBAAuB,EAAE+C,YAAY,IAAI,EAAEF,cAAc,CAAC,CAAC;YAC9E;QACF;QACA,8DAA8D;QAC9D,IAAI;YACF,MAAMI,IAAAA,sBAAe,EAAC9B,aAAanK,MAAMI,QAAQ;YACjD,MAAM8L,IAAAA,kBAAW,EAAC/B;QACpB,EAAE,OAAOhG,GAAG;YACV,MAAM,IAAI6E,MAAM,CAAC,0BAA0B,EAAE7E,EAAEE,OAAO,EAAE;QAC1D;IACF;IAEA,MAAc0G,gBAAgBpJ,EAAU,EAAEkC,GAAW,EAAEsB,QAAgB,EAAEgH,UAAkB,EAAEC,QAAgB,EAAmB;QAC9H,MAAMhH,MAAc,GAAGzD,GAAG,CAAC,EAAE0K,mBAAM,CAACC,WAAW,CAAC,IAAI7J,QAAQ,CAAC,QAAQ,CAAC5B,KAAK,CAAC,GAAG,IAAI0L,OAAO,CAAC,KAAK;QAChG,MAAM7E,UAAiC;YACrCtC,KAAKA;YACLvB,KAAKA;YACL2I,UAAUrH;YACVsH,YAAYN;YACZO,OAAO;QACT;QACAhF,QAAQxE,KAAK,GAAG,MAAM,IAAI,CAACC,eAAe,CAACuE;QAC3C,IAAIiF;QACJ,IAAI;YACF,MAAM3B,MAAqB,MAAM,IAAI,CAACC,IAAI,CAACC,QAAQ,CAAC;gBAClDC,QAAQC,kCAAW,CAACwB,IAAI;gBACxB/I,KAAK,IAAI,CAACgJ,UAAU;gBACpBjB,MAAMlE;gBACN6D,YAAY,IAAIC,cAAK,CAACC,KAAK,CAAC;oBAAEC,oBAAoB,IAAI,CAACA,kBAAkB;gBAAC;YAC5E;YACAiB,SAAS3B,IAAIY,IAAI;QACnB,EAAE,OAAOzH,GAAG;YACV,MAAM,IAAI6E,MAAM,CAAC,6BAA6B,EAAE7E,EAAE2I,QAAQ,CAACrJ,MAAM,EAAE;QACrE;QACA,IAAIkJ,OAAO1I,KAAK,EAAE;YAChB,MAAM,IAAI+E,MAAM,CAAC,6BAA6B,EAAE+D,qCAAyB,CAACxI,GAAG,CAACoI,OAAO1I,KAAK,GAAG;QAC/F;QACA,IAAI0I,OAAOK,UAAU,EAAE;YACrB,IAAI,CAACrJ,MAAM,CAACsJ,GAAG,CAAC,GAAG,IAAI,CAAClC,eAAe,CAAC3G,IAAI,CAAC,GAAG,EAAEe,SAAS,IAAI,EAAEgH,WAAW,GAAG,EAAEC,UAAU;YAC3F,OAAOO,OAAO/J,OAAO;QACvB;IACF;IAEQ0G,YAAYhH,MAAc,EAAU;QAC1C,OAAO,GAAG4K,wBAAiB,CAAC,CAAC,EAAE5K,QAAQ;IACzC;IA9UA,YACE,AAAiB2I,IAAiB,EAClC,AAAiBvG,cAA8B,EAC/C,AAAiB8E,KAAY,EAC7B,AAAiBlG,GAAe,EAChC,AAAiBjC,gBAAkC,EACnD,AAAiBkB,YAA0B,CAC3C;aANiB0I,OAAAA;aACAvG,iBAAAA;aACA8E,QAAAA;aACAlG,MAAAA;aACAjC,mBAAAA;aACAkB,eAAAA;aAbXoB,SAAS,IAAIwJ,cAAM,CAAC5N,uBAAuB6E,IAAI;aACtCK,2BAA2B/E,gCAAa,CAACC,YAAY,CAACC,KAAK,CAACC,UAAU,CAACuN,cAAc,IAAI;aACzF1B,qBAA8B,CAAChM,gCAAa,CAACC,YAAY,CAACC,KAAK,CAACC,UAAU,EAAEwN;aAC5ER,aAAa,IAAI,CAACpI,wBAAwB,GAAG,GAAG,IAAI,CAACA,wBAAwB,CAAC,oBAAoB,CAAC,GAAG;aACtGyD,aAAaoF,IAAAA,oCAAyB,EAAC5N,gCAAa,CAACsI,IAAI,CAAC9E,KAAK,CAACqK,OAAO,CAACrF,UAAU;aAClFjG,cAAsB;IASpC;AAwUL"}
|
|
1
|
+
{"version":3,"sources":["../../../../../backend/src/applications/files/services/files-only-office-manager.service.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { HttpService } from '@nestjs/axios'\nimport { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common'\nimport { JwtService } from '@nestjs/jwt'\nimport { AxiosResponse } from 'axios'\nimport https from 'https'\nimport crypto from 'node:crypto'\nimport os from 'node:os'\nimport path from 'node:path'\nimport { JwtIdentityPayload } from '../../../authentication/interfaces/jwt-payload.interface'\nimport { convertHumanTimeToSeconds, generateShortUUID } from '../../../common/functions'\nimport { SERVER_NAME } from '../../../common/shared'\nimport { configuration } from '../../../configuration/config.environment'\nimport { Cache } from '../../../infrastructure/cache/services/cache.service'\nimport { ContextManager } from '../../../infrastructure/context/services/context-manager.service'\nimport { HTTP_METHOD } from '../../applications.constants'\nimport { SPACE_OPERATION } from '../../spaces/constants/spaces'\nimport { FastifySpaceRequest } from '../../spaces/interfaces/space-request.interface'\nimport type { SpaceEnv } from '../../spaces/models/space-env.model'\nimport { haveSpaceEnvPermissions } from '../../spaces/utils/permissions'\nimport type { UserModel } from '../../users/models/user.model'\nimport { getAvatarBase64 } from '../../users/utils/avatar'\nimport { DEPTH, LOCK_SCOPE } from '../../webdav/constants/webdav'\nimport { CACHE_ONLY_OFFICE } from '../constants/cache'\nimport {\n ONLY_OFFICE_CONVERT_ERROR,\n ONLY_OFFICE_CONVERT_EXTENSIONS,\n ONLY_OFFICE_EXTENSIONS,\n ONLY_OFFICE_INTERNAL_URI,\n ONLY_OFFICE_TOKEN_QUERY_PARAM_NAME\n} from '../constants/only-office'\nimport { API_FILES_ONLY_OFFICE_CALLBACK, API_FILES_ONLY_OFFICE_DOCUMENT } from '../constants/routes'\nimport type { FileProps } from '../interfaces/file-props.interface'\nimport { OnlyOfficeCallBack, OnlyOfficeConfig, OnlyOfficeConvertForm, OnlyOfficeReqConfig } from '../interfaces/only-office-config.interface'\nimport { copyFileContent, fileSize, getProps, isPathExists, isPathIsDir, removeFiles, uniqueFilePathFromDir, writeFromStream } from '../utils/files'\nimport { FilesLockManager } from './files-lock-manager.service'\nimport { FilesQueries } from './files-queries.service'\n\n@Injectable()\nexport class FilesOnlyOfficeManager {\n private logger = new Logger(FilesOnlyOfficeManager.name)\n private readonly externalOnlyOfficeServer = configuration.applications.files.onlyoffice.externalServer || null\n private readonly rejectUnauthorized: boolean = !configuration.applications.files.onlyoffice?.verifySSL\n private readonly convertUrl = this.externalOnlyOfficeServer ? `${this.externalOnlyOfficeServer}/ConvertService.ashx` : null\n private readonly expiration = convertHumanTimeToSeconds(configuration.auth.token.refresh.expiration)\n private readonly mobileRegex: RegExp = /android|webos|iphone|ipad|ipod|blackberry|windows phone|opera mini|iemobile|mobile/i\n\n constructor(\n private readonly http: HttpService,\n private readonly contextManager: ContextManager,\n private readonly cache: Cache,\n private readonly jwt: JwtService,\n private readonly filesLockManager: FilesLockManager,\n private readonly filesQueries: FilesQueries\n ) {}\n\n getStatus(): { enabled: boolean } {\n return { enabled: configuration.applications.files.onlyoffice.enabled }\n }\n\n async getSettings(user: UserModel, space: SpaceEnv, mode: 'edit' | 'view', req: FastifySpaceRequest): Promise<OnlyOfficeReqConfig> {\n if (!(await isPathExists(space.realPath))) {\n throw new HttpException('Document not found', HttpStatus.NOT_FOUND)\n }\n if (await isPathIsDir(space.realPath)) {\n throw new HttpException('Document must be a file', HttpStatus.BAD_REQUEST)\n }\n const fileExtension = path.extname(space.realPath).slice(1)\n if (!ONLY_OFFICE_EXTENSIONS.VIEWABLE.has(fileExtension) && !ONLY_OFFICE_EXTENSIONS.EDITABLE.has(fileExtension)) {\n throw new HttpException('Document not supported', HttpStatus.BAD_REQUEST)\n }\n if (mode === 'edit' && (!ONLY_OFFICE_EXTENSIONS.EDITABLE.has(fileExtension) || !haveSpaceEnvPermissions(space, SPACE_OPERATION.MODIFY))) {\n mode = 'view'\n }\n if (mode === 'edit') {\n // check lock conflicts\n try {\n await this.filesLockManager.checkConflicts(space.dbFile, DEPTH.RESOURCE, { userId: user.id, lockScope: LOCK_SCOPE.SHARED })\n } catch {\n throw new HttpException('The file is locked', HttpStatus.LOCKED)\n }\n }\n const isMobile: boolean = this.mobileRegex.test(req.headers['user-agent'])\n const fileProps: FileProps = await getProps(space.realPath, space.dbFile.path)\n const fileId: string = ((await this.filesQueries.getSpaceFileId(fileProps, space.dbFile)) || fileProps.id).toString()\n const userToken: string = await this.genUserToken(user)\n const fileUrl = this.getDocumentUrl(space, userToken)\n const callBackUrl = this.getCallBackUrl(space, userToken, fileId)\n const config: OnlyOfficeReqConfig = await this.genConfiguration(user, space, mode, fileId, fileUrl, fileExtension, callBackUrl, isMobile)\n config.config.token = await this.genPayloadToken(config.config)\n return config\n }\n\n async callBack(user: UserModel, space: SpaceEnv, token: string, fileId: string) {\n const callBackData: OnlyOfficeCallBack = await this.jwt.verifyAsync(token, { secret: configuration.applications.files.onlyoffice.secret })\n try {\n switch (callBackData.status) {\n case 1:\n await this.checkFileLock(user, space, callBackData)\n this.logger.debug(`document is being edited : ${space.url}`)\n break\n case 2:\n await this.checkFileLock(user, space, callBackData)\n if (callBackData.notmodified) {\n this.logger.debug(`document was edited but closed with no changes : ${space.url}`)\n } else {\n this.logger.debug(`document was edited but not saved (let's do it) : ${space.url}`)\n await this.saveDocument(space, callBackData.url)\n }\n await this.removeDocumentKey(fileId, space)\n break\n case 3:\n this.logger.error(`document cannot be saved, an error has occurred (try to save it) : ${space.url}`)\n await this.saveDocument(space, callBackData.url)\n break\n case 4:\n await this.removeFileLock(user.id, space)\n await this.removeDocumentKey(fileId, space)\n this.logger.debug(`document was closed with no changes : ${space.url}`)\n break\n case 6:\n this.logger.debug(`document is edited but save was requested : ${space.url}`)\n await this.saveDocument(space, callBackData.url)\n break\n case 7:\n this.logger.error(`document cannot be force saved, an error has occurred (try to save it) : ${space.url}`)\n await this.saveDocument(space, callBackData.url)\n break\n default:\n this.logger.error('unhandled case')\n }\n } catch (e) {\n this.logger.error(`${this.callBack.name} - ${e.message} : ${space.url}`)\n return { error: e.message }\n }\n return { error: 0 }\n }\n\n private async genConfiguration(\n user: UserModel,\n space: SpaceEnv,\n mode: 'edit' | 'view',\n fileId: string,\n fileUrl: string,\n fileExtension: string,\n callBackUrl: string,\n isMobile: boolean\n ): Promise<OnlyOfficeReqConfig> {\n const documentType = ONLY_OFFICE_EXTENSIONS.EDITABLE.get(fileExtension) || ONLY_OFFICE_EXTENSIONS.VIEWABLE.get(fileExtension)\n return {\n documentServerUrl: this.externalOnlyOfficeServer || `${this.contextManager.get('headerOriginUrl')}${ONLY_OFFICE_INTERNAL_URI}`,\n config: {\n type: isMobile ? 'mobile' : 'desktop',\n height: '100%',\n width: '100%',\n documentType: documentType,\n document: {\n title: path.basename(space.relativeUrl),\n fileType: fileExtension,\n key: await this.getDocumentKey(fileId),\n permissions: {\n download: true,\n edit: mode === 'edit',\n changeHistory: false,\n comment: true,\n fillForms: true,\n print: true,\n review: mode === 'edit'\n },\n url: fileUrl\n },\n editorConfig: {\n mode: mode,\n lang: 'en',\n region: 'en',\n callbackUrl: callBackUrl,\n user: { id: user.id.toString(), name: `${user.fullName} (${user.email})`, image: await getAvatarBase64(user.login) },\n coEditing: {\n mode: 'fast',\n change: true\n },\n embedded: {\n embedUrl: fileUrl,\n saveUrl: fileUrl,\n shareUrl: fileUrl,\n toolbarDocked: 'top'\n },\n customization: {\n about: false,\n autosave: false,\n forcesave: true,\n zoom: documentType === 'slide' ? 60 : 90,\n help: false,\n features: { featuresTips: false },\n plugins: false\n }\n }\n }\n }\n }\n\n private getDocumentUrl(space: SpaceEnv, token: string): string {\n // user refresh token is used here for long session\n return `${this.contextManager.get('headerOriginUrl')}${API_FILES_ONLY_OFFICE_DOCUMENT}/${space.url}?${ONLY_OFFICE_TOKEN_QUERY_PARAM_NAME}=${token}`\n }\n\n private getCallBackUrl(space: SpaceEnv, token: string, fileId: string): string {\n // user refresh token is used here for long session\n return `${this.contextManager.get('headerOriginUrl')}${API_FILES_ONLY_OFFICE_CALLBACK}/${space.url}?fid=${fileId}&${ONLY_OFFICE_TOKEN_QUERY_PARAM_NAME}=${token}`\n }\n\n private genPayloadToken(payload: OnlyOfficeConfig | OnlyOfficeConvertForm): Promise<string> {\n return this.jwt.signAsync(payload, { secret: configuration.applications.files.onlyoffice.secret, expiresIn: 60 })\n }\n\n private genUserToken(user: UserModel): Promise<string> {\n // use refresh expiration to allow long session\n return this.jwt.signAsync(\n {\n identity: {\n id: user.id,\n login: user.login,\n email: user.email,\n fullName: user.fullName,\n language: user.language,\n role: user.role,\n applications: user.applications\n } satisfies JwtIdentityPayload\n },\n {\n secret: configuration.auth.token.access.secret,\n expiresIn: this.expiration\n }\n )\n }\n\n private async checkFileLock(user: UserModel, space: SpaceEnv, callBackData: OnlyOfficeCallBack) {\n for (const action of callBackData.actions) {\n if (action.type === 0) {\n // disconnect\n await this.removeFileLock(parseInt(action.userid), space)\n } else if (action.type === 1) {\n // connect\n await this.genFileLock(user, space)\n }\n }\n }\n\n private async genFileLock(user: UserModel, space: SpaceEnv): Promise<void> {\n const [ok, _fileLock] = await this.filesLockManager.create(user, space.dbFile, DEPTH.RESOURCE, this.expiration, {\n lockroot: null,\n locktoken: null,\n lockscope: LOCK_SCOPE.SHARED,\n owner: `${SERVER_NAME} - ${user.fullName} (${user.email})`\n })\n if (!ok) {\n throw new Error('document is locked')\n }\n }\n\n private async removeFileLock(userId: number, space: SpaceEnv): Promise<void> {\n for (const lock of await this.filesLockManager.getLocksByPath(space.dbFile)) {\n if (lock.owner.id === userId) {\n await this.filesLockManager.removeLock(lock.key)\n }\n }\n }\n\n private async removeDocumentKey(fileId: string, space: SpaceEnv): Promise<void> {\n if (!(await this.filesLockManager.isPathLocked(space.dbFile))) {\n const cacheKey = this.getCacheKey(fileId)\n const r = await this.cache.del(cacheKey)\n this.logger.debug(`${this.removeDocumentKey.name} - ${cacheKey} ${r ? '' : 'not'} removed`)\n }\n }\n\n private async getDocumentKey(fileId: string): Promise<string> {\n const cacheKey = this.getCacheKey(fileId)\n const existingDocKey: string = await this.cache.get(cacheKey)\n if (existingDocKey) {\n return existingDocKey\n }\n const docKey = generateShortUUID(64)\n await this.cache.set(cacheKey, docKey, this.expiration)\n this.logger.debug(`${this.getDocumentKey.name} - ${cacheKey} (${docKey}) created`)\n return docKey\n }\n\n private async saveDocument(space: SpaceEnv, url: string): Promise<void> {\n /* url format:\n https://onlyoffice-server.com/cache/files/data/-33120641_7158/output.pptx/output.pptx\n ?md5=duFHKC-5d47s-RRcYn3hAw&expires=1739400549&shardkey=-33120641&filename=output.pptx\n */\n const urlParams = new URLSearchParams(url.split('?').at(-1))\n // it is not the md5 of the file but a md5 generated by the combination of the elements of the url\n const md5: string = urlParams.get('md5')\n const tmpFilePath = await uniqueFilePathFromDir(path.join(os.tmpdir(), `${md5}-${urlParams.get('filename')}`))\n\n // convert remote file to the local file with the current extension if these extensions aren't equal\n const localExtension = path.extname(space.realPath).slice(1)\n const remoteExtension = path.extname(urlParams.get('filename')).slice(1)\n\n let downloadUrl: string\n if (localExtension !== remoteExtension && !ONLY_OFFICE_CONVERT_EXTENSIONS.ALLOW_AUTO.has(localExtension)) {\n if (ONLY_OFFICE_CONVERT_EXTENSIONS.FROM.has(remoteExtension) && ONLY_OFFICE_CONVERT_EXTENSIONS.TO.has(localExtension)) {\n downloadUrl = await this.convertDocument(urlParams.get('shardkey'), url, remoteExtension, localExtension, space.url)\n } else {\n throw new Error(`document cannot be converted from ${remoteExtension} -> ${localExtension} : ${space.url}`)\n }\n } else {\n downloadUrl = url\n }\n\n // download file\n let res: AxiosResponse\n try {\n res = await this.http.axiosRef({\n method: HTTP_METHOD.GET,\n url: downloadUrl,\n responseType: 'stream',\n httpsAgent: new https.Agent({ rejectUnauthorized: this.rejectUnauthorized })\n })\n await writeFromStream(tmpFilePath, res.data)\n } catch (e) {\n throw new Error(`unable to get document : ${e.message}`)\n }\n\n // try to verify the downloaded size\n const contentLength = parseInt(res.headers['content-length'], 10)\n if (!isNaN(contentLength)) {\n if (contentLength === 0) {\n this.logger.debug(`${this.saveDocument.name} - content length is 0 : ${space.url}`)\n return\n }\n const tmpFileSize = await fileSize(tmpFilePath)\n if (tmpFileSize !== contentLength) {\n throw new Error(`document size differs (${tmpFileSize} != ${contentLength})`)\n }\n }\n // copy contents to avoid inode changes (fileId in some cases)\n try {\n await copyFileContent(tmpFilePath, space.realPath)\n await removeFiles(tmpFilePath)\n } catch (e) {\n throw new Error(`unable to save document : ${e.message}`)\n }\n }\n\n private async convertDocument(id: string, url: string, fileType: string, outputType: string, spaceUrl: string): Promise<string> {\n const key: string = `${id}-${crypto.randomBytes(20).toString('hex')}`.slice(0, 20).replace('-', '_')\n const payload: OnlyOfficeConvertForm = {\n key: key,\n url: url,\n filetype: fileType,\n outputtype: outputType,\n async: false\n }\n payload.token = await this.genPayloadToken(payload)\n let result: { fileUrl?: string; fileType?: string; endConvert?: boolean; error?: number }\n try {\n const res: AxiosResponse = await this.http.axiosRef({\n method: HTTP_METHOD.POST,\n url: this.convertUrl,\n data: payload,\n httpsAgent: new https.Agent({ rejectUnauthorized: this.rejectUnauthorized })\n })\n result = res.data\n } catch (e) {\n throw new Error(`convert failed with status : ${e.response.status}`)\n }\n if (result.error) {\n throw new Error(`convert failed with reason : ${ONLY_OFFICE_CONVERT_ERROR.get(result.error)}`)\n }\n if (result.endConvert) {\n this.logger.log(`${this.convertDocument.name} - ${fileType} -> ${outputType} : ${spaceUrl}`)\n return result.fileUrl\n }\n }\n\n private getCacheKey(fileId: string): string {\n return `${CACHE_ONLY_OFFICE}|${fileId}`\n }\n}\n"],"names":["FilesOnlyOfficeManager","getStatus","enabled","configuration","applications","files","onlyoffice","getSettings","user","space","mode","req","isPathExists","realPath","HttpException","HttpStatus","NOT_FOUND","isPathIsDir","BAD_REQUEST","fileExtension","path","extname","slice","ONLY_OFFICE_EXTENSIONS","VIEWABLE","has","EDITABLE","haveSpaceEnvPermissions","SPACE_OPERATION","MODIFY","filesLockManager","checkConflicts","dbFile","DEPTH","RESOURCE","userId","id","lockScope","LOCK_SCOPE","SHARED","LOCKED","isMobile","mobileRegex","test","headers","fileProps","getProps","fileId","filesQueries","getSpaceFileId","toString","userToken","genUserToken","fileUrl","getDocumentUrl","callBackUrl","getCallBackUrl","config","genConfiguration","token","genPayloadToken","callBack","callBackData","jwt","verifyAsync","secret","status","checkFileLock","logger","debug","url","notmodified","saveDocument","removeDocumentKey","error","removeFileLock","e","name","message","documentType","get","documentServerUrl","externalOnlyOfficeServer","contextManager","ONLY_OFFICE_INTERNAL_URI","type","height","width","document","title","basename","relativeUrl","fileType","key","getDocumentKey","permissions","download","edit","changeHistory","comment","fillForms","print","review","editorConfig","lang","region","callbackUrl","fullName","email","image","getAvatarBase64","login","coEditing","change","embedded","embedUrl","saveUrl","shareUrl","toolbarDocked","customization","about","autosave","forcesave","zoom","help","features","featuresTips","plugins","API_FILES_ONLY_OFFICE_DOCUMENT","ONLY_OFFICE_TOKEN_QUERY_PARAM_NAME","API_FILES_ONLY_OFFICE_CALLBACK","payload","signAsync","expiresIn","identity","language","role","auth","access","expiration","action","actions","parseInt","userid","genFileLock","ok","_fileLock","create","lockroot","locktoken","lockscope","owner","SERVER_NAME","Error","lock","getLocksByPath","removeLock","isPathLocked","cacheKey","getCacheKey","r","cache","del","existingDocKey","docKey","generateShortUUID","set","urlParams","URLSearchParams","split","at","md5","tmpFilePath","uniqueFilePathFromDir","join","os","tmpdir","localExtension","remoteExtension","downloadUrl","ONLY_OFFICE_CONVERT_EXTENSIONS","ALLOW_AUTO","FROM","TO","convertDocument","res","http","axiosRef","method","HTTP_METHOD","GET","responseType","httpsAgent","https","Agent","rejectUnauthorized","writeFromStream","data","contentLength","isNaN","tmpFileSize","fileSize","copyFileContent","removeFiles","outputType","spaceUrl","crypto","randomBytes","replace","filetype","outputtype","async","result","POST","convertUrl","response","ONLY_OFFICE_CONVERT_ERROR","endConvert","log","CACHE_ONLY_OFFICE","Logger","externalServer","verifySSL","convertHumanTimeToSeconds","refresh"],"mappings":"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"}
|
|
@@ -59,6 +59,7 @@ let FilesParser = class FilesParser {
|
|
|
59
59
|
id: _usersschema.users.id,
|
|
60
60
|
login: _usersschema.users.login
|
|
61
61
|
}).from(_usersschema.users).where((0, _drizzleorm.and)(...[
|
|
62
|
+
(0, _drizzleorm.eq)(_usersschema.users.storageIndexing, true),
|
|
62
63
|
(0, _drizzleorm.lte)(_usersschema.users.role, _user.USER_ROLE.USER),
|
|
63
64
|
...userId ? [
|
|
64
65
|
(0, _drizzleorm.eq)(_usersschema.users.id, userId)
|
|
@@ -95,7 +96,7 @@ let FilesParser = class FilesParser {
|
|
|
95
96
|
fromOwner: _usersschema.users.login
|
|
96
97
|
}
|
|
97
98
|
})
|
|
98
|
-
}).from(_spacesschema.spaces).leftJoin(_spacesrootsschema.spacesRoots, (0, _drizzleorm.eq)(_spacesrootsschema.spacesRoots.spaceId, _spacesschema.spaces.id)).leftJoin(_filesschema.files, (0, _drizzleorm.eq)(_filesschema.files.id, _spacesrootsschema.spacesRoots.fileId)).leftJoin(_usersschema.users, (0, _drizzleorm.eq)(_usersschema.users.id, _filesschema.files.ownerId)).where((0, _drizzleorm.and)(...spaceIds ? [
|
|
99
|
+
}).from(_spacesschema.spaces).leftJoin(_spacesrootsschema.spacesRoots, (0, _drizzleorm.eq)(_spacesrootsschema.spacesRoots.spaceId, _spacesschema.spaces.id)).leftJoin(_filesschema.files, (0, _drizzleorm.eq)(_filesschema.files.id, _spacesrootsschema.spacesRoots.fileId)).leftJoin(_usersschema.users, (0, _drizzleorm.eq)(_usersschema.users.id, _filesschema.files.ownerId)).where((0, _drizzleorm.and)((0, _drizzleorm.eq)(_spacesschema.spaces.storageIndexing, true), ...spaceIds ? [
|
|
99
100
|
(0, _drizzleorm.inArray)(_spacesschema.spaces.id, spaceIds)
|
|
100
101
|
] : [])).groupBy(_spacesschema.spaces.id))){
|
|
101
102
|
const spaceFilesPath = _spacemodel.SpaceModel.getFilesPath(space.alias);
|
|
@@ -140,8 +141,8 @@ let FilesParser = class FilesParser {
|
|
|
140
141
|
fromOwner: _usersschema.users.login,
|
|
141
142
|
fromSpace: _spacesschema.spaces.alias
|
|
142
143
|
}
|
|
143
|
-
}).from(_sharesschema.shares).leftJoin(_spacesrootsschema.spacesRoots, (0, _drizzleorm.eq)(_spacesrootsschema.spacesRoots.id, _sharesschema.shares.spaceRootId)).leftJoin(_filesschema.files, (0, _drizzleorm.or)(//
|
|
144
|
-
(0, _drizzleorm.and)((0, _drizzleorm.isNotNull)(_sharesschema.shares.fileId), (0, _drizzleorm.eq)(_filesschema.files.id, _sharesschema.shares.fileId)), (0, _drizzleorm.and)((0, _drizzleorm.isNull)(_sharesschema.shares.externalPath), (0, _drizzleorm.isNull)(_sharesschema.shares.fileId), (0, _drizzleorm.isNotNull)(_spacesrootsschema.spacesRoots.fileId), (0, _drizzleorm.eq)(_filesschema.files.id, _spacesrootsschema.spacesRoots.fileId)))).leftJoin(_spacesschema.spaces, (0, _drizzleorm.and)((0, _drizzleorm.isNull)(_sharesschema.shares.externalPath), (0, _drizzleorm.isNotNull)(_filesschema.files.spaceId), (0, _drizzleorm.eq)(_spacesschema.spaces.id, _filesschema.files.spaceId))).leftJoin(_usersschema.users, (0, _drizzleorm.eq)(_usersschema.users.id, _filesschema.files.ownerId)).where((0, _drizzleorm.and)(...[
|
|
144
|
+
}).from(_sharesschema.shares).leftJoin(_spacesrootsschema.spacesRoots, (0, _drizzleorm.eq)(_spacesrootsschema.spacesRoots.id, _sharesschema.shares.spaceRootId)).leftJoin(_filesschema.files, (0, _drizzleorm.or)(// If the child share is from a share with an external path, the child share should have an external path and a fileId
|
|
145
|
+
(0, _drizzleorm.and)((0, _drizzleorm.isNotNull)(_sharesschema.shares.fileId), (0, _drizzleorm.eq)(_filesschema.files.id, _sharesschema.shares.fileId)), (0, _drizzleorm.and)((0, _drizzleorm.isNull)(_sharesschema.shares.externalPath), (0, _drizzleorm.isNull)(_sharesschema.shares.fileId), (0, _drizzleorm.isNotNull)(_spacesrootsschema.spacesRoots.fileId), (0, _drizzleorm.eq)(_filesschema.files.id, _spacesrootsschema.spacesRoots.fileId)))).leftJoin(_spacesschema.spaces, (0, _drizzleorm.and)((0, _drizzleorm.isNull)(_sharesschema.shares.externalPath), (0, _drizzleorm.isNotNull)(_filesschema.files.spaceId), (0, _drizzleorm.eq)(_spacesschema.spaces.id, _filesschema.files.spaceId), (0, _drizzleorm.eq)(_spacesschema.spaces.storageIndexing, true))).leftJoin(_usersschema.users, (0, _drizzleorm.and)((0, _drizzleorm.eq)(_usersschema.users.id, _filesschema.files.ownerId), (0, _drizzleorm.eq)(_usersschema.users.storageIndexing, true))).where((0, _drizzleorm.and)((0, _drizzleorm.eq)(_sharesschema.shares.storageIndexing, true), ...[
|
|
145
146
|
(0, _drizzleorm.eq)(_sharesschema.shares.type, _shares.SHARE_TYPE.COMMON),
|
|
146
147
|
...shareIds ? [
|
|
147
148
|
(0, _drizzleorm.inArray)(_sharesschema.shares.id, shareIds)
|
|
@@ -154,6 +155,8 @@ let FilesParser = class FilesParser {
|
|
|
154
155
|
shareFilesPath = _nodepath.default.join(_usermodel.UserModel.getFilesPath(share.file.fromOwner), share.file.path);
|
|
155
156
|
} else if (share.file.fromSpace) {
|
|
156
157
|
shareFilesPath = _nodepath.default.join(_spacemodel.SpaceModel.getFilesPath(share.file.fromSpace), share.file.path);
|
|
158
|
+
} else {
|
|
159
|
+
continue;
|
|
157
160
|
}
|
|
158
161
|
if (!await (0, _files.isPathExists)(shareFilesPath)) {
|
|
159
162
|
this.logger.warn(`${this.sharePaths.name} - share path does not exist : ${shareFilesPath}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../backend/src/applications/files/services/files-parser.service.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { Inject, Injectable, Logger } from '@nestjs/common'\nimport { and, eq, inArray, isNotNull, isNull, lte, or, sql } from 'drizzle-orm'\nimport path from 'node:path'\nimport { DB_TOKEN_PROVIDER } from '../../../infrastructure/database/constants'\nimport { DBSchema } from '../../../infrastructure/database/interfaces/database.interface'\nimport { concatDistinctObjectsInArray } from '../../../infrastructure/database/utils'\nimport { SHARE_TYPE } from '../../shares/constants/shares'\nimport { shares } from '../../shares/schemas/shares.schema'\nimport { SPACE_ALIAS, SPACE_REPOSITORY } from '../../spaces/constants/spaces'\nimport { SpaceModel } from '../../spaces/models/space.model'\nimport { spacesRoots } from '../../spaces/schemas/spaces-roots.schema'\nimport { spaces } from '../../spaces/schemas/spaces.schema'\nimport { USER_ROLE } from '../../users/constants/user'\nimport { UserModel } from '../../users/models/user.model'\nimport { users } from '../../users/schemas/users.schema'\nimport { FileParseContext, FileParseType } from '../interfaces/file-parse-index'\nimport { filePathSQL, files } from '../schemas/files.schema'\nimport { isPathExists } from '../utils/files'\n\n@Injectable()\nexport class FilesParser {\n private readonly logger = new Logger(FilesParser.name)\n\n constructor(@Inject(DB_TOKEN_PROVIDER) private readonly db: DBSchema) {}\n\n async *allPaths(userId?: number, spaceIds?: number[], shareIds?: number[]): AsyncGenerator<[number, FileParseType, FileParseContext[]]> {\n yield* this.userPaths(userId)\n yield* this.spacePaths(spaceIds)\n yield* this.sharePaths(shareIds)\n }\n\n async *userPaths(userId?: number): AsyncGenerator<[number, FileParseType, FileParseContext[]]> {\n for (const user of await this.db\n .select({\n id: users.id,\n login: users.login\n })\n .from(users)\n .where(and(...[lte(users.role, USER_ROLE.USER), ...(userId ? [eq(users.id, userId)] : [])]))) {\n const userFilesPath = UserModel.getFilesPath(user.login)\n if (!(await isPathExists(userFilesPath))) {\n this.logger.warn(`${this.userPaths.name} - user path does not exist : ${userFilesPath}`)\n continue\n }\n yield [user.id, 'user', [{ realPath: userFilesPath, pathPrefix: `${SPACE_REPOSITORY.FILES}/${SPACE_ALIAS.PERSONAL}`, isDir: true }]]\n }\n }\n\n async *spacePaths(spaceIds?: number[]): AsyncGenerator<[number, FileParseType, FileParseContext[]]> {\n for (const space of await this.db\n .select({\n id: spaces.id,\n alias: spaces.alias,\n roots: concatDistinctObjectsInArray(spacesRoots.alias, {\n alias: spacesRoots.alias,\n externalPath: spacesRoots.externalPath,\n isDir: sql<boolean>`IF (${spacesRoots.externalPath} IS NOT NULL, 1, ${files.isDir})`,\n file: {\n path: filePathSQL(files),\n fromOwner: users.login\n }\n })\n })\n .from(spaces)\n .leftJoin(spacesRoots, eq(spacesRoots.spaceId, spaces.id))\n .leftJoin(files, eq(files.id, spacesRoots.fileId))\n .leftJoin(users, eq(users.id, files.ownerId))\n .where(and(...(spaceIds ? [inArray(spaces.id, spaceIds)] : [])))\n .groupBy(spaces.id)) {\n const spaceFilesPath = SpaceModel.getFilesPath(space.alias)\n if (!(await isPathExists(spaceFilesPath))) {\n this.logger.warn(`${this.spacePaths.name} - space path does not exist : ${spaceFilesPath}`)\n continue\n }\n const spacePath = [{ realPath: spaceFilesPath, pathPrefix: `${SPACE_REPOSITORY.FILES}/${space.alias}`, isDir: true }]\n const rootPaths = space.roots.map(\n (r: any): FileParseContext =>\n r.externalPath\n ? {\n realPath: r.externalPath,\n pathPrefix: `${SPACE_REPOSITORY.FILES}/${space.alias}/${r.alias}`,\n isDir: r.isDir\n }\n : {\n realPath: path.join(UserModel.getFilesPath(r.file.fromOwner), r.file.path),\n pathPrefix: `${SPACE_REPOSITORY.FILES}/${space.alias}/${r.alias}`,\n isDir: r.isDir\n }\n )\n yield [space.id, 'space', [...spacePath, ...rootPaths]]\n }\n }\n\n async *sharePaths(shareIds?: number[]): AsyncGenerator<[number, FileParseType, FileParseContext[]]> {\n for (const share of await this.db\n .select({\n id: shares.id,\n alias: shares.alias,\n externalPath: sql<string>`IF (${shares.externalPath} IS NOT NULL, ${shares.externalPath}, ${spacesRoots.externalPath})`,\n isDir: sql<boolean>`IF (${shares.externalPath} IS NOT NULL, 1, ${files.isDir})`,\n file: { path: sql<string>`IF (${files.id} IS NOT NULL, ${filePathSQL(files)}, '.')`, fromOwner: users.login, fromSpace: spaces.alias }\n })\n .from(shares)\n .leftJoin(spacesRoots, eq(spacesRoots.id, shares.spaceRootId))\n .leftJoin(\n files,\n or(\n // if the child share is from a share with an external path, the child share should have an external path and a fileId\n and(isNotNull(shares.fileId), eq(files.id, shares.fileId)),\n and(isNull(shares.externalPath), isNull(shares.fileId), isNotNull(spacesRoots.fileId), eq(files.id, spacesRoots.fileId))\n )\n )\n .leftJoin(spaces, and(isNull(shares.externalPath), isNotNull(files.spaceId), eq(spaces.id, files.spaceId)))\n .leftJoin(users, eq(users.id, files.ownerId))\n .where(and(...[eq(shares.type, SHARE_TYPE.COMMON), ...(shareIds ? [inArray(shares.id, shareIds)] : [])]))\n .groupBy(shares.id)) {\n let shareFilesPath: string\n if (share.externalPath) {\n shareFilesPath = path.join(share.externalPath, share.file.path)\n } else if (share.file.fromOwner) {\n shareFilesPath = path.join(UserModel.getFilesPath(share.file.fromOwner), share.file.path)\n } else if (share.file.fromSpace) {\n shareFilesPath = path.join(SpaceModel.getFilesPath(share.file.fromSpace), share.file.path)\n }\n if (!(await isPathExists(shareFilesPath))) {\n this.logger.warn(`${this.sharePaths.name} - share path does not exist : ${shareFilesPath}`)\n continue\n }\n yield [share.id, 'share', [{ realPath: shareFilesPath, pathPrefix: `${SPACE_REPOSITORY.SHARES}/${share.alias}`, isDir: share.isDir }]]\n }\n }\n}\n"],"names":["FilesParser","allPaths","userId","spaceIds","shareIds","userPaths","spacePaths","sharePaths","user","db","select","id","users","login","from","where","and","lte","role","USER_ROLE","USER","eq","userFilesPath","UserModel","getFilesPath","isPathExists","logger","warn","name","realPath","pathPrefix","SPACE_REPOSITORY","FILES","SPACE_ALIAS","PERSONAL","isDir","space","spaces","alias","roots","concatDistinctObjectsInArray","spacesRoots","externalPath","sql","files","file","path","filePathSQL","fromOwner","leftJoin","spaceId","fileId","ownerId","inArray","groupBy","spaceFilesPath","SpaceModel","spacePath","rootPaths","map","r","join","share","shares","fromSpace","spaceRootId","or","isNotNull","isNull","type","SHARE_TYPE","COMMON","shareFilesPath","SHARES","Logger"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../backend/src/applications/files/services/files-parser.service.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { Inject, Injectable, Logger } from '@nestjs/common'\nimport { and, eq, inArray, isNotNull, isNull, lte, or, sql } from 'drizzle-orm'\nimport path from 'node:path'\nimport { DB_TOKEN_PROVIDER } from '../../../infrastructure/database/constants'\nimport { DBSchema } from '../../../infrastructure/database/interfaces/database.interface'\nimport { concatDistinctObjectsInArray } from '../../../infrastructure/database/utils'\nimport { SHARE_TYPE } from '../../shares/constants/shares'\nimport { shares } from '../../shares/schemas/shares.schema'\nimport { SPACE_ALIAS, SPACE_REPOSITORY } from '../../spaces/constants/spaces'\nimport { SpaceModel } from '../../spaces/models/space.model'\nimport { spacesRoots } from '../../spaces/schemas/spaces-roots.schema'\nimport { spaces } from '../../spaces/schemas/spaces.schema'\nimport { USER_ROLE } from '../../users/constants/user'\nimport { UserModel } from '../../users/models/user.model'\nimport { users } from '../../users/schemas/users.schema'\nimport { FileParseContext, FileParseType } from '../interfaces/file-parse-index'\nimport { filePathSQL, files } from '../schemas/files.schema'\nimport { isPathExists } from '../utils/files'\n\n@Injectable()\nexport class FilesParser {\n private readonly logger = new Logger(FilesParser.name)\n\n constructor(@Inject(DB_TOKEN_PROVIDER) private readonly db: DBSchema) {}\n\n async *allPaths(userId?: number, spaceIds?: number[], shareIds?: number[]): AsyncGenerator<[number, FileParseType, FileParseContext[]]> {\n yield* this.userPaths(userId)\n yield* this.spacePaths(spaceIds)\n yield* this.sharePaths(shareIds)\n }\n\n async *userPaths(userId?: number): AsyncGenerator<[number, FileParseType, FileParseContext[]]> {\n for (const user of await this.db\n .select({\n id: users.id,\n login: users.login\n })\n .from(users)\n .where(and(...[eq(users.storageIndexing, true), lte(users.role, USER_ROLE.USER), ...(userId ? [eq(users.id, userId)] : [])]))) {\n const userFilesPath = UserModel.getFilesPath(user.login)\n if (!(await isPathExists(userFilesPath))) {\n this.logger.warn(`${this.userPaths.name} - user path does not exist : ${userFilesPath}`)\n continue\n }\n yield [user.id, 'user', [{ realPath: userFilesPath, pathPrefix: `${SPACE_REPOSITORY.FILES}/${SPACE_ALIAS.PERSONAL}`, isDir: true }]]\n }\n }\n\n async *spacePaths(spaceIds?: number[]): AsyncGenerator<[number, FileParseType, FileParseContext[]]> {\n for (const space of await this.db\n .select({\n id: spaces.id,\n alias: spaces.alias,\n roots: concatDistinctObjectsInArray(spacesRoots.alias, {\n alias: spacesRoots.alias,\n externalPath: spacesRoots.externalPath,\n isDir: sql<boolean>`IF (${spacesRoots.externalPath} IS NOT NULL, 1, ${files.isDir})`,\n file: {\n path: filePathSQL(files),\n fromOwner: users.login\n }\n })\n })\n .from(spaces)\n .leftJoin(spacesRoots, eq(spacesRoots.spaceId, spaces.id))\n .leftJoin(files, eq(files.id, spacesRoots.fileId))\n .leftJoin(users, eq(users.id, files.ownerId))\n .where(and(eq(spaces.storageIndexing, true), ...(spaceIds ? [inArray(spaces.id, spaceIds)] : [])))\n .groupBy(spaces.id)) {\n const spaceFilesPath = SpaceModel.getFilesPath(space.alias)\n if (!(await isPathExists(spaceFilesPath))) {\n this.logger.warn(`${this.spacePaths.name} - space path does not exist : ${spaceFilesPath}`)\n continue\n }\n const spacePath = [{ realPath: spaceFilesPath, pathPrefix: `${SPACE_REPOSITORY.FILES}/${space.alias}`, isDir: true }]\n const rootPaths = space.roots.map(\n (r: any): FileParseContext =>\n r.externalPath\n ? {\n realPath: r.externalPath,\n pathPrefix: `${SPACE_REPOSITORY.FILES}/${space.alias}/${r.alias}`,\n isDir: r.isDir\n }\n : {\n realPath: path.join(UserModel.getFilesPath(r.file.fromOwner), r.file.path),\n pathPrefix: `${SPACE_REPOSITORY.FILES}/${space.alias}/${r.alias}`,\n isDir: r.isDir\n }\n )\n yield [space.id, 'space', [...spacePath, ...rootPaths]]\n }\n }\n\n async *sharePaths(shareIds?: number[]): AsyncGenerator<[number, FileParseType, FileParseContext[]]> {\n for (const share of await this.db\n .select({\n id: shares.id,\n alias: shares.alias,\n externalPath: sql<string>`IF (${shares.externalPath} IS NOT NULL, ${shares.externalPath}, ${spacesRoots.externalPath})`,\n isDir: sql<boolean>`IF (${shares.externalPath} IS NOT NULL, 1, ${files.isDir})`,\n file: { path: sql<string>`IF (${files.id} IS NOT NULL, ${filePathSQL(files)}, '.')`, fromOwner: users.login, fromSpace: spaces.alias }\n })\n .from(shares)\n .leftJoin(spacesRoots, eq(spacesRoots.id, shares.spaceRootId))\n .leftJoin(\n files,\n or(\n // If the child share is from a share with an external path, the child share should have an external path and a fileId\n and(isNotNull(shares.fileId), eq(files.id, shares.fileId)),\n and(isNull(shares.externalPath), isNull(shares.fileId), isNotNull(spacesRoots.fileId), eq(files.id, spacesRoots.fileId))\n )\n )\n .leftJoin(spaces, and(isNull(shares.externalPath), isNotNull(files.spaceId), eq(spaces.id, files.spaceId), eq(spaces.storageIndexing, true)))\n .leftJoin(users, and(eq(users.id, files.ownerId), eq(users.storageIndexing, true)))\n .where(and(eq(shares.storageIndexing, true), ...[eq(shares.type, SHARE_TYPE.COMMON), ...(shareIds ? [inArray(shares.id, shareIds)] : [])]))\n .groupBy(shares.id)) {\n let shareFilesPath: string\n if (share.externalPath) {\n shareFilesPath = path.join(share.externalPath, share.file.path)\n } else if (share.file.fromOwner) {\n shareFilesPath = path.join(UserModel.getFilesPath(share.file.fromOwner), share.file.path)\n } else if (share.file.fromSpace) {\n shareFilesPath = path.join(SpaceModel.getFilesPath(share.file.fromSpace), share.file.path)\n } else {\n // Exclude shares that don’t match these cases (join conditions)\n continue\n }\n if (!(await isPathExists(shareFilesPath))) {\n this.logger.warn(`${this.sharePaths.name} - share path does not exist : ${shareFilesPath}`)\n continue\n }\n yield [share.id, 'share', [{ realPath: shareFilesPath, pathPrefix: `${SPACE_REPOSITORY.SHARES}/${share.alias}`, isDir: share.isDir }]]\n }\n }\n}\n"],"names":["FilesParser","allPaths","userId","spaceIds","shareIds","userPaths","spacePaths","sharePaths","user","db","select","id","users","login","from","where","and","eq","storageIndexing","lte","role","USER_ROLE","USER","userFilesPath","UserModel","getFilesPath","isPathExists","logger","warn","name","realPath","pathPrefix","SPACE_REPOSITORY","FILES","SPACE_ALIAS","PERSONAL","isDir","space","spaces","alias","roots","concatDistinctObjectsInArray","spacesRoots","externalPath","sql","files","file","path","filePathSQL","fromOwner","leftJoin","spaceId","fileId","ownerId","inArray","groupBy","spaceFilesPath","SpaceModel","spacePath","rootPaths","map","r","join","share","shares","fromSpace","spaceRootId","or","isNotNull","isNull","type","SHARE_TYPE","COMMON","shareFilesPath","SHARES","Logger"],"mappings":"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"}
|
|
@@ -15,17 +15,21 @@ Object.defineProperty(exports, "FilesScheduler", {
|
|
|
15
15
|
const _common = require("@nestjs/common");
|
|
16
16
|
const _schedule = require("@nestjs/schedule");
|
|
17
17
|
const _drizzleorm = require("drizzle-orm");
|
|
18
|
+
const _mysqlcore = require("drizzle-orm/mysql-core");
|
|
18
19
|
const _promises = /*#__PURE__*/ _interop_require_default(require("node:fs/promises"));
|
|
19
20
|
const _nodepath = /*#__PURE__*/ _interop_require_default(require("node:path"));
|
|
21
|
+
const _configenvironment = require("../../../configuration/config.environment");
|
|
20
22
|
const _cacheservice = require("../../../infrastructure/cache/services/cache.service");
|
|
21
23
|
const _constants = require("../../../infrastructure/database/constants");
|
|
22
24
|
const _databaseinterface = require("../../../infrastructure/database/interfaces/database.interface");
|
|
25
|
+
const _utils = require("../../../infrastructure/database/utils");
|
|
23
26
|
const _user = require("../../users/constants/user");
|
|
24
27
|
const _usermodel = require("../../users/models/user.model");
|
|
25
28
|
const _usersschema = require("../../users/schemas/users.schema");
|
|
26
29
|
const _cache = require("../constants/cache");
|
|
27
30
|
const _filetask = require("../models/file-task");
|
|
28
31
|
const _filesrecentsschema = require("../schemas/files-recents.schema");
|
|
32
|
+
const _filesschema = require("../schemas/files.schema");
|
|
29
33
|
const _files = require("../utils/files");
|
|
30
34
|
const _filescontentmanagerservice = require("./files-content-manager.service");
|
|
31
35
|
const _filestasksmanagerservice = require("./files-tasks-manager.service");
|
|
@@ -78,7 +82,6 @@ let FilesScheduler = class FilesScheduler {
|
|
|
78
82
|
}).from(_usersschema.users))){
|
|
79
83
|
const userTasksPath = _usermodel.UserModel.getTasksPath(user.login, user.role === _user.USER_ROLE.GUEST, user.role === _user.USER_ROLE.LINK);
|
|
80
84
|
if (!await (0, _files.isPathExists)(userTasksPath)) {
|
|
81
|
-
this.logger.verbose(`${this.cleanupUserTaskFiles.name} - *${user.login}* tasks path does not exist`);
|
|
82
85
|
continue;
|
|
83
86
|
}
|
|
84
87
|
if (await (0, _files.dirHasChildren)(userTasksPath, false)) {
|
|
@@ -128,10 +131,49 @@ let FilesScheduler = class FilesScheduler {
|
|
|
128
131
|
this.logger.log(`${this.clearRecentFiles.name} - ${nbCleared} records cleared - END`);
|
|
129
132
|
}
|
|
130
133
|
async indexContentFiles() {
|
|
134
|
+
// Conditional loading of file content indexing
|
|
135
|
+
if (!_configenvironment.configuration.applications.files.contentIndexing) return;
|
|
131
136
|
this.logger.log(`${this.indexContentFiles.name} - START`);
|
|
132
137
|
await this.filesContentManager.parseAndIndexAllFiles();
|
|
133
138
|
this.logger.log(`${this.indexContentFiles.name} - END`);
|
|
134
139
|
}
|
|
140
|
+
async deleteOrphanFiles() {
|
|
141
|
+
this.logger.log(`${this.deleteOrphanFiles.name} - START`);
|
|
142
|
+
const selects = [];
|
|
143
|
+
for (const table of (0, _utils.getTablesWithFileIdColumn)()){
|
|
144
|
+
selects.push(this.db.selectDistinct({
|
|
145
|
+
id: table.fileId
|
|
146
|
+
}).from(table).where((0, _drizzleorm.isNotNull)(table.fileId)));
|
|
147
|
+
}
|
|
148
|
+
if (selects.length === 0) {
|
|
149
|
+
this.logger.warn(`${this.deleteOrphanFiles.name} - no tables with fileId column`);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const unionSub = (selects.length === 1 ? selects[0] : (0, _mysqlcore.unionAll)(...selects)).as('u');
|
|
153
|
+
// Debug
|
|
154
|
+
// const [preview] = (await this.db.execute(sql`
|
|
155
|
+
// SELECT f.id
|
|
156
|
+
// FROM ${files} AS f
|
|
157
|
+
// LEFT JOIN ${unionSub} ON ${unionSub.id} = f.id
|
|
158
|
+
// WHERE ${unionSub.id} IS NULL
|
|
159
|
+
// `)) as any[]
|
|
160
|
+
// console.log(preview.length, preview)
|
|
161
|
+
const deleteQuery = (0, _drizzleorm.sql)`
|
|
162
|
+
DELETE f
|
|
163
|
+
FROM ${_filesschema.files} AS f
|
|
164
|
+
LEFT JOIN ${unionSub} ON ${unionSub.id} = f.id
|
|
165
|
+
WHERE ${unionSub.id} IS NULL
|
|
166
|
+
`;
|
|
167
|
+
try {
|
|
168
|
+
await this.db.transaction(async (tx)=>{
|
|
169
|
+
const [r] = await tx.execute(deleteQuery);
|
|
170
|
+
this.logger.log(`${this.deleteOrphanFiles.name} - files: ${r.affectedRows}`);
|
|
171
|
+
});
|
|
172
|
+
} catch (e) {
|
|
173
|
+
this.logger.log(`${this.deleteOrphanFiles.name} - ${e}`);
|
|
174
|
+
}
|
|
175
|
+
this.logger.log(`${this.deleteOrphanFiles.name} - END`);
|
|
176
|
+
}
|
|
135
177
|
constructor(db, cache, filesContentManager){
|
|
136
178
|
this.db = db;
|
|
137
179
|
this.cache = cache;
|
|
@@ -153,18 +195,24 @@ _ts_decorate([
|
|
|
153
195
|
], FilesScheduler.prototype, "cleanupUserTaskFiles", null);
|
|
154
196
|
_ts_decorate([
|
|
155
197
|
(0, _schedule.Timeout)(30000),
|
|
156
|
-
(0, _schedule.Cron)(_schedule.CronExpression.
|
|
198
|
+
(0, _schedule.Cron)(_schedule.CronExpression.EVERY_8_HOURS),
|
|
157
199
|
_ts_metadata("design:type", Function),
|
|
158
200
|
_ts_metadata("design:paramtypes", []),
|
|
159
201
|
_ts_metadata("design:returntype", Promise)
|
|
160
202
|
], FilesScheduler.prototype, "clearRecentFiles", null);
|
|
161
203
|
_ts_decorate([
|
|
162
|
-
(0, _schedule.Timeout)(
|
|
204
|
+
(0, _schedule.Timeout)(120000),
|
|
163
205
|
(0, _schedule.Cron)(_schedule.CronExpression.EVERY_4_HOURS),
|
|
164
206
|
_ts_metadata("design:type", Function),
|
|
165
207
|
_ts_metadata("design:paramtypes", []),
|
|
166
208
|
_ts_metadata("design:returntype", Promise)
|
|
167
209
|
], FilesScheduler.prototype, "indexContentFiles", null);
|
|
210
|
+
_ts_decorate([
|
|
211
|
+
(0, _schedule.Cron)(_schedule.CronExpression.EVERY_DAY_AT_4AM),
|
|
212
|
+
_ts_metadata("design:type", Function),
|
|
213
|
+
_ts_metadata("design:paramtypes", []),
|
|
214
|
+
_ts_metadata("design:returntype", Promise)
|
|
215
|
+
], FilesScheduler.prototype, "deleteOrphanFiles", null);
|
|
168
216
|
FilesScheduler = _ts_decorate([
|
|
169
217
|
(0, _common.Injectable)(),
|
|
170
218
|
_ts_param(0, (0, _common.Inject)(_constants.DB_TOKEN_PROVIDER)),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../backend/src/applications/files/services/files-scheduler.service.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { Inject, Injectable, Logger } from '@nestjs/common'\nimport { Cron, CronExpression, Timeout } from '@nestjs/schedule'\nimport { sql } from 'drizzle-orm'\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { Cache } from '../../../infrastructure/cache/services/cache.service'\nimport { DB_TOKEN_PROVIDER } from '../../../infrastructure/database/constants'\nimport { DBSchema } from '../../../infrastructure/database/interfaces/database.interface'\nimport { USER_ROLE } from '../../users/constants/user'\nimport { UserModel } from '../../users/models/user.model'\nimport { users } from '../../users/schemas/users.schema'\nimport { CACHE_TASK_PREFIX } from '../constants/cache'\nimport { FileTask, FileTaskStatus } from '../models/file-task'\nimport { filesRecents } from '../schemas/files-recents.schema'\nimport { dirHasChildren, isPathExists, removeFiles } from '../utils/files'\nimport { FilesContentManager } from './files-content-manager.service'\nimport { FilesTasksManager } from './files-tasks-manager.service'\n\n@Injectable()\nexport class FilesScheduler {\n private readonly logger = new Logger(FilesScheduler.name)\n\n constructor(\n @Inject(DB_TOKEN_PROVIDER) private readonly db: DBSchema,\n private readonly cache: Cache,\n private readonly filesContentManager: FilesContentManager\n ) {}\n\n @Timeout(30000)\n async cleanupInterruptedTasks(): Promise<void> {\n this.logger.log(`${this.cleanupInterruptedTasks.name} - START`)\n try {\n let nb = 0\n const keys = await this.cache.keys(`${CACHE_TASK_PREFIX}-*`)\n for (const key of keys) {\n const task = await this.cache.get(key)\n if (task && task.status === FileTaskStatus.PENDING) {\n task.status = FileTaskStatus.ERROR\n task.result = 'Interrupted'\n nb++\n this.cache.set(key, task).catch((e: Error) => this.logger.error(`${this.cleanupInterruptedTasks.name} - ${e}`))\n }\n }\n this.logger.log(`${this.cleanupInterruptedTasks.name} - ${nb} tasks cleaned : END`)\n } catch (e) {\n this.logger.error(`${this.cleanupInterruptedTasks.name} - ${e}`)\n }\n }\n\n @Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT)\n async cleanupUserTaskFiles(): Promise<void> {\n this.logger.log(`${this.cleanupUserTaskFiles.name} - START`)\n try {\n for (const user of await this.db\n .select({\n id: users.id,\n login: users.login,\n role: users.role\n })\n .from(users)) {\n const userTasksPath = UserModel.getTasksPath(user.login, user.role === USER_ROLE.GUEST, user.role === USER_ROLE.LINK)\n if (!(await isPathExists(userTasksPath))) {\n this.logger.verbose(`${this.cleanupUserTaskFiles.name} - *${user.login}* tasks path does not exist`)\n continue\n }\n if (await dirHasChildren(userTasksPath, false)) {\n const cacheKey = FilesTasksManager.getCacheKey(user.id)\n const keys = await this.cache.keys(cacheKey)\n const excludeFiles = (await this.cache.mget(keys))\n .filter((task: FileTask) => task && task.status === FileTaskStatus.PENDING && task.props.compressInDirectory === false)\n .map((task: FileTask) => task.name)\n for (const f of (await fs.readdir(userTasksPath)).filter((f: string) => excludeFiles.indexOf(f) === -1)) {\n try {\n removeFiles(path.join(userTasksPath, f)).catch((e: Error) => this.logger.error(`${this.cleanupUserTaskFiles.name} - ${e}`))\n } catch (e) {\n this.logger.error(`${this.cleanupUserTaskFiles.name} - unable to remove ${path.join(userTasksPath, f)} : ${e}`)\n }\n }\n }\n }\n } catch (e) {\n this.logger.error(`${this.cleanupUserTaskFiles.name} - ${e}`)\n }\n this.logger.log(`${this.cleanupUserTaskFiles.name} - END`)\n }\n\n @Timeout(30000)\n @Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT)\n async clearRecentFiles(): Promise<void> {\n this.logger.log(`${this.clearRecentFiles.name} - START`)\n const keepNumber = 100\n let nbCleared = 0\n try {\n for (const fk of [filesRecents.ownerId, filesRecents.spaceId, filesRecents.shareId]) {\n const [r] = await this.db.execute(sql`\n DELETE\n FROM ${filesRecents}\n WHERE ${fk} IS NOT NULL\n AND id NOT IN (SELECT id\n FROM (SELECT id,\n ROW_NUMBER() OVER (PARTITION BY ${fk} ORDER BY ${filesRecents.mtime}) AS rn\n FROM ${filesRecents}\n WHERE ${fk} IS NOT NULL) AS ranked\n WHERE ranked.rn <= ${keepNumber})\n `)\n nbCleared += r.affectedRows\n }\n } catch (e) {\n this.logger.error(`${this.clearRecentFiles.name} - ${e}`)\n }\n this.logger.log(`${this.clearRecentFiles.name} - ${nbCleared} records cleared - END`)\n }\n\n @Timeout(60000)\n @Cron(CronExpression.EVERY_4_HOURS)\n async indexContentFiles(): Promise<void> {\n this.logger.log(`${this.indexContentFiles.name} - START`)\n await this.filesContentManager.parseAndIndexAllFiles()\n this.logger.log(`${this.indexContentFiles.name} - END`)\n }\n}\n"],"names":["FilesScheduler","cleanupInterruptedTasks","logger","log","name","nb","keys","cache","CACHE_TASK_PREFIX","key","task","get","status","FileTaskStatus","PENDING","ERROR","result","set","catch","e","error","cleanupUserTaskFiles","user","db","select","id","users","login","role","from","userTasksPath","UserModel","getTasksPath","USER_ROLE","GUEST","LINK","isPathExists","verbose","dirHasChildren","cacheKey","FilesTasksManager","getCacheKey","excludeFiles","mget","filter","props","compressInDirectory","map","f","fs","readdir","indexOf","removeFiles","path","join","clearRecentFiles","keepNumber","nbCleared","fk","filesRecents","ownerId","spaceId","shareId","r","execute","sql","mtime","affectedRows","indexContentFiles","filesContentManager","parseAndIndexAllFiles","Logger","EVERY_DAY_AT_MIDNIGHT","EVERY_4_HOURS"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../backend/src/applications/files/services/files-scheduler.service.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { Inject, Injectable, Logger } from '@nestjs/common'\nimport { Cron, CronExpression, Timeout } from '@nestjs/schedule'\nimport { isNotNull, sql } from 'drizzle-orm'\nimport { unionAll } from 'drizzle-orm/mysql-core'\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { configuration } from '../../../configuration/config.environment'\nimport { Cache } from '../../../infrastructure/cache/services/cache.service'\nimport { DB_TOKEN_PROVIDER } from '../../../infrastructure/database/constants'\nimport { DBSchema } from '../../../infrastructure/database/interfaces/database.interface'\nimport { getTablesWithFileIdColumn } from '../../../infrastructure/database/utils'\nimport { USER_ROLE } from '../../users/constants/user'\nimport { UserModel } from '../../users/models/user.model'\nimport { users } from '../../users/schemas/users.schema'\nimport { CACHE_TASK_PREFIX } from '../constants/cache'\nimport { FileTask, FileTaskStatus } from '../models/file-task'\nimport { filesRecents } from '../schemas/files-recents.schema'\nimport { files } from '../schemas/files.schema'\nimport { dirHasChildren, isPathExists, removeFiles } from '../utils/files'\nimport { FilesContentManager } from './files-content-manager.service'\nimport { FilesTasksManager } from './files-tasks-manager.service'\n\n@Injectable()\nexport class FilesScheduler {\n private readonly logger = new Logger(FilesScheduler.name)\n\n constructor(\n @Inject(DB_TOKEN_PROVIDER) private readonly db: DBSchema,\n private readonly cache: Cache,\n private readonly filesContentManager: FilesContentManager\n ) {}\n\n @Timeout(30000)\n async cleanupInterruptedTasks(): Promise<void> {\n this.logger.log(`${this.cleanupInterruptedTasks.name} - START`)\n try {\n let nb = 0\n const keys = await this.cache.keys(`${CACHE_TASK_PREFIX}-*`)\n for (const key of keys) {\n const task = await this.cache.get(key)\n if (task && task.status === FileTaskStatus.PENDING) {\n task.status = FileTaskStatus.ERROR\n task.result = 'Interrupted'\n nb++\n this.cache.set(key, task).catch((e: Error) => this.logger.error(`${this.cleanupInterruptedTasks.name} - ${e}`))\n }\n }\n this.logger.log(`${this.cleanupInterruptedTasks.name} - ${nb} tasks cleaned : END`)\n } catch (e) {\n this.logger.error(`${this.cleanupInterruptedTasks.name} - ${e}`)\n }\n }\n\n @Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT)\n async cleanupUserTaskFiles(): Promise<void> {\n this.logger.log(`${this.cleanupUserTaskFiles.name} - START`)\n try {\n for (const user of await this.db\n .select({\n id: users.id,\n login: users.login,\n role: users.role\n })\n .from(users)) {\n const userTasksPath = UserModel.getTasksPath(user.login, user.role === USER_ROLE.GUEST, user.role === USER_ROLE.LINK)\n if (!(await isPathExists(userTasksPath))) {\n continue\n }\n if (await dirHasChildren(userTasksPath, false)) {\n const cacheKey = FilesTasksManager.getCacheKey(user.id)\n const keys = await this.cache.keys(cacheKey)\n const excludeFiles = (await this.cache.mget(keys))\n .filter((task: FileTask) => task && task.status === FileTaskStatus.PENDING && task.props.compressInDirectory === false)\n .map((task: FileTask) => task.name)\n for (const f of (await fs.readdir(userTasksPath)).filter((f: string) => excludeFiles.indexOf(f) === -1)) {\n try {\n removeFiles(path.join(userTasksPath, f)).catch((e: Error) => this.logger.error(`${this.cleanupUserTaskFiles.name} - ${e}`))\n } catch (e) {\n this.logger.error(`${this.cleanupUserTaskFiles.name} - unable to remove ${path.join(userTasksPath, f)} : ${e}`)\n }\n }\n }\n }\n } catch (e) {\n this.logger.error(`${this.cleanupUserTaskFiles.name} - ${e}`)\n }\n this.logger.log(`${this.cleanupUserTaskFiles.name} - END`)\n }\n\n @Timeout(30000)\n @Cron(CronExpression.EVERY_8_HOURS)\n async clearRecentFiles(): Promise<void> {\n this.logger.log(`${this.clearRecentFiles.name} - START`)\n const keepNumber = 100\n let nbCleared = 0\n try {\n for (const fk of [filesRecents.ownerId, filesRecents.spaceId, filesRecents.shareId]) {\n const [r] = await this.db.execute(sql`\n DELETE\n FROM ${filesRecents}\n WHERE ${fk} IS NOT NULL\n AND id NOT IN (SELECT id\n FROM (SELECT id,\n ROW_NUMBER() OVER (PARTITION BY ${fk} ORDER BY ${filesRecents.mtime}) AS rn\n FROM ${filesRecents}\n WHERE ${fk} IS NOT NULL) AS ranked\n WHERE ranked.rn <= ${keepNumber})\n `)\n nbCleared += r.affectedRows\n }\n } catch (e) {\n this.logger.error(`${this.clearRecentFiles.name} - ${e}`)\n }\n this.logger.log(`${this.clearRecentFiles.name} - ${nbCleared} records cleared - END`)\n }\n\n @Timeout(120000)\n @Cron(CronExpression.EVERY_4_HOURS)\n async indexContentFiles(): Promise<void> {\n // Conditional loading of file content indexing\n if (!configuration.applications.files.contentIndexing) return\n this.logger.log(`${this.indexContentFiles.name} - START`)\n await this.filesContentManager.parseAndIndexAllFiles()\n this.logger.log(`${this.indexContentFiles.name} - END`)\n }\n\n @Cron(CronExpression.EVERY_DAY_AT_4AM)\n async deleteOrphanFiles() {\n this.logger.log(`${this.deleteOrphanFiles.name} - START`)\n const selects: any[] = []\n for (const table of getTablesWithFileIdColumn()) {\n selects.push(this.db.selectDistinct({ id: table.fileId }).from(table).where(isNotNull(table.fileId)))\n }\n if (selects.length === 0) {\n this.logger.warn(`${this.deleteOrphanFiles.name} - no tables with fileId column`)\n return\n }\n const unionSub = (selects.length === 1 ? selects[0] : unionAll(...(selects as [any, any, ...any[]]))).as('u')\n // Debug\n // const [preview] = (await this.db.execute(sql`\n // SELECT f.id\n // FROM ${files} AS f\n // LEFT JOIN ${unionSub} ON ${unionSub.id} = f.id\n // WHERE ${unionSub.id} IS NULL\n // `)) as any[]\n // console.log(preview.length, preview)\n const deleteQuery = sql`\n DELETE f\n FROM ${files} AS f\n LEFT JOIN ${unionSub} ON ${unionSub.id} = f.id\n WHERE ${unionSub.id} IS NULL\n `\n try {\n await this.db.transaction(async (tx) => {\n const [r] = await tx.execute(deleteQuery)\n this.logger.log(`${this.deleteOrphanFiles.name} - files: ${r.affectedRows}`)\n })\n } catch (e) {\n this.logger.log(`${this.deleteOrphanFiles.name} - ${e}`)\n }\n this.logger.log(`${this.deleteOrphanFiles.name} - END`)\n }\n}\n"],"names":["FilesScheduler","cleanupInterruptedTasks","logger","log","name","nb","keys","cache","CACHE_TASK_PREFIX","key","task","get","status","FileTaskStatus","PENDING","ERROR","result","set","catch","e","error","cleanupUserTaskFiles","user","db","select","id","users","login","role","from","userTasksPath","UserModel","getTasksPath","USER_ROLE","GUEST","LINK","isPathExists","dirHasChildren","cacheKey","FilesTasksManager","getCacheKey","excludeFiles","mget","filter","props","compressInDirectory","map","f","fs","readdir","indexOf","removeFiles","path","join","clearRecentFiles","keepNumber","nbCleared","fk","filesRecents","ownerId","spaceId","shareId","r","execute","sql","mtime","affectedRows","indexContentFiles","configuration","applications","files","contentIndexing","filesContentManager","parseAndIndexAllFiles","deleteOrphanFiles","selects","table","getTablesWithFileIdColumn","push","selectDistinct","fileId","where","isNotNull","length","warn","unionSub","unionAll","as","deleteQuery","transaction","tx","Logger","EVERY_DAY_AT_MIDNIGHT","EVERY_8_HOURS","EVERY_4_HOURS","EVERY_DAY_AT_4AM"],"mappings":"AAAA;;;;CAIC;;;;+BAyBYA;;;eAAAA;;;wBAvB8B;0BACG;4BACf;2BACN;iEACV;iEACE;mCACa;8BACR;2BACY;mCACT;uBACiB;sBAChB;2BACA;6BACJ;uBACY;0BACO;oCACZ;6BACP;uBACoC;4CACtB;0CACF;;;;;;;;;;;;;;;;;;;;AAG3B,IAAA,AAAMA,iBAAN,MAAMA;IASX,MACMC,0BAAyC;QAC7C,IAAI,CAACC,MAAM,CAACC,GAAG,CAAC,GAAG,IAAI,CAACF,uBAAuB,CAACG,IAAI,CAAC,QAAQ,CAAC;QAC9D,IAAI;YACF,IAAIC,KAAK;YACT,MAAMC,OAAO,MAAM,IAAI,CAACC,KAAK,CAACD,IAAI,CAAC,GAAGE,wBAAiB,CAAC,EAAE,CAAC;YAC3D,KAAK,MAAMC,OAAOH,KAAM;gBACtB,MAAMI,OAAO,MAAM,IAAI,CAACH,KAAK,CAACI,GAAG,CAACF;gBAClC,IAAIC,QAAQA,KAAKE,MAAM,KAAKC,wBAAc,CAACC,OAAO,EAAE;oBAClDJ,KAAKE,MAAM,GAAGC,wBAAc,CAACE,KAAK;oBAClCL,KAAKM,MAAM,GAAG;oBACdX;oBACA,IAAI,CAACE,KAAK,CAACU,GAAG,CAACR,KAAKC,MAAMQ,KAAK,CAAC,CAACC,IAAa,IAAI,CAACjB,MAAM,CAACkB,KAAK,CAAC,GAAG,IAAI,CAACnB,uBAAuB,CAACG,IAAI,CAAC,GAAG,EAAEe,GAAG;gBAC/G;YACF;YACA,IAAI,CAACjB,MAAM,CAACC,GAAG,CAAC,GAAG,IAAI,CAACF,uBAAuB,CAACG,IAAI,CAAC,GAAG,EAAEC,GAAG,oBAAoB,CAAC;QACpF,EAAE,OAAOc,GAAG;YACV,IAAI,CAACjB,MAAM,CAACkB,KAAK,CAAC,GAAG,IAAI,CAACnB,uBAAuB,CAACG,IAAI,CAAC,GAAG,EAAEe,GAAG;QACjE;IACF;IAEA,MACME,uBAAsC;QAC1C,IAAI,CAACnB,MAAM,CAACC,GAAG,CAAC,GAAG,IAAI,CAACkB,oBAAoB,CAACjB,IAAI,CAAC,QAAQ,CAAC;QAC3D,IAAI;YACF,KAAK,MAAMkB,QAAQ,CAAA,MAAM,IAAI,CAACC,EAAE,CAC7BC,MAAM,CAAC;gBACNC,IAAIC,kBAAK,CAACD,EAAE;gBACZE,OAAOD,kBAAK,CAACC,KAAK;gBAClBC,MAAMF,kBAAK,CAACE,IAAI;YAClB,GACCC,IAAI,CAACH,kBAAK,CAAA,EAAG;gBACd,MAAMI,gBAAgBC,oBAAS,CAACC,YAAY,CAACV,KAAKK,KAAK,EAAEL,KAAKM,IAAI,KAAKK,eAAS,CAACC,KAAK,EAAEZ,KAAKM,IAAI,KAAKK,eAAS,CAACE,IAAI;gBACpH,IAAI,CAAE,MAAMC,IAAAA,mBAAY,EAACN,gBAAiB;oBACxC;gBACF;gBACA,IAAI,MAAMO,IAAAA,qBAAc,EAACP,eAAe,QAAQ;oBAC9C,MAAMQ,WAAWC,2CAAiB,CAACC,WAAW,CAAClB,KAAKG,EAAE;oBACtD,MAAMnB,OAAO,MAAM,IAAI,CAACC,KAAK,CAACD,IAAI,CAACgC;oBACnC,MAAMG,eAAe,AAAC,CAAA,MAAM,IAAI,CAAClC,KAAK,CAACmC,IAAI,CAACpC,KAAI,EAC7CqC,MAAM,CAAC,CAACjC,OAAmBA,QAAQA,KAAKE,MAAM,KAAKC,wBAAc,CAACC,OAAO,IAAIJ,KAAKkC,KAAK,CAACC,mBAAmB,KAAK,OAChHC,GAAG,CAAC,CAACpC,OAAmBA,KAAKN,IAAI;oBACpC,KAAK,MAAM2C,KAAK,AAAC,CAAA,MAAMC,iBAAE,CAACC,OAAO,CAACnB,cAAa,EAAGa,MAAM,CAAC,CAACI,IAAcN,aAAaS,OAAO,CAACH,OAAO,CAAC,GAAI;wBACvG,IAAI;4BACFI,IAAAA,kBAAW,EAACC,iBAAI,CAACC,IAAI,CAACvB,eAAeiB,IAAI7B,KAAK,CAAC,CAACC,IAAa,IAAI,CAACjB,MAAM,CAACkB,KAAK,CAAC,GAAG,IAAI,CAACC,oBAAoB,CAACjB,IAAI,CAAC,GAAG,EAAEe,GAAG;wBAC3H,EAAE,OAAOA,GAAG;4BACV,IAAI,CAACjB,MAAM,CAACkB,KAAK,CAAC,GAAG,IAAI,CAACC,oBAAoB,CAACjB,IAAI,CAAC,oBAAoB,EAAEgD,iBAAI,CAACC,IAAI,CAACvB,eAAeiB,GAAG,GAAG,EAAE5B,GAAG;wBAChH;oBACF;gBACF;YACF;QACF,EAAE,OAAOA,GAAG;YACV,IAAI,CAACjB,MAAM,CAACkB,KAAK,CAAC,GAAG,IAAI,CAACC,oBAAoB,CAACjB,IAAI,CAAC,GAAG,EAAEe,GAAG;QAC9D;QACA,IAAI,CAACjB,MAAM,CAACC,GAAG,CAAC,GAAG,IAAI,CAACkB,oBAAoB,CAACjB,IAAI,CAAC,MAAM,CAAC;IAC3D;IAEA,MAEMkD,mBAAkC;QACtC,IAAI,CAACpD,MAAM,CAACC,GAAG,CAAC,GAAG,IAAI,CAACmD,gBAAgB,CAAClD,IAAI,CAAC,QAAQ,CAAC;QACvD,MAAMmD,aAAa;QACnB,IAAIC,YAAY;QAChB,IAAI;YACF,KAAK,MAAMC,MAAM;gBAACC,gCAAY,CAACC,OAAO;gBAAED,gCAAY,CAACE,OAAO;gBAAEF,gCAAY,CAACG,OAAO;aAAC,CAAE;gBACnF,MAAM,CAACC,EAAE,GAAG,MAAM,IAAI,CAACvC,EAAE,CAACwC,OAAO,CAACC,IAAAA,eAAG,CAAA,CAAC;;eAE/B,EAAEN,gCAAY,CAAC;gBACd,EAAED,GAAG;;;wEAGmD,EAAEA,GAAG,UAAU,EAAEC,gCAAY,CAACO,KAAK,CAAC;sCACtE,EAAEP,gCAAY,CAAC;uCACd,EAAED,GAAG;8CACE,EAAEF,WAAW;QACnD,CAAC;gBACDC,aAAaM,EAAEI,YAAY;YAC7B;QACF,EAAE,OAAO/C,GAAG;YACV,IAAI,CAACjB,MAAM,CAACkB,KAAK,CAAC,GAAG,IAAI,CAACkC,gBAAgB,CAAClD,IAAI,CAAC,GAAG,EAAEe,GAAG;QAC1D;QACA,IAAI,CAACjB,MAAM,CAACC,GAAG,CAAC,GAAG,IAAI,CAACmD,gBAAgB,CAAClD,IAAI,CAAC,GAAG,EAAEoD,UAAU,sBAAsB,CAAC;IACtF;IAEA,MAEMW,oBAAmC;QACvC,+CAA+C;QAC/C,IAAI,CAACC,gCAAa,CAACC,YAAY,CAACC,KAAK,CAACC,eAAe,EAAE;QACvD,IAAI,CAACrE,MAAM,CAACC,GAAG,CAAC,GAAG,IAAI,CAACgE,iBAAiB,CAAC/D,IAAI,CAAC,QAAQ,CAAC;QACxD,MAAM,IAAI,CAACoE,mBAAmB,CAACC,qBAAqB;QACpD,IAAI,CAACvE,MAAM,CAACC,GAAG,CAAC,GAAG,IAAI,CAACgE,iBAAiB,CAAC/D,IAAI,CAAC,MAAM,CAAC;IACxD;IAEA,MACMsE,oBAAoB;QACxB,IAAI,CAACxE,MAAM,CAACC,GAAG,CAAC,GAAG,IAAI,CAACuE,iBAAiB,CAACtE,IAAI,CAAC,QAAQ,CAAC;QACxD,MAAMuE,UAAiB,EAAE;QACzB,KAAK,MAAMC,SAASC,IAAAA,gCAAyB,IAAI;YAC/CF,QAAQG,IAAI,CAAC,IAAI,CAACvD,EAAE,CAACwD,cAAc,CAAC;gBAAEtD,IAAImD,MAAMI,MAAM;YAAC,GAAGnD,IAAI,CAAC+C,OAAOK,KAAK,CAACC,IAAAA,qBAAS,EAACN,MAAMI,MAAM;QACpG;QACA,IAAIL,QAAQQ,MAAM,KAAK,GAAG;YACxB,IAAI,CAACjF,MAAM,CAACkF,IAAI,CAAC,GAAG,IAAI,CAACV,iBAAiB,CAACtE,IAAI,CAAC,+BAA+B,CAAC;YAChF;QACF;QACA,MAAMiF,WAAW,AAACV,CAAAA,QAAQQ,MAAM,KAAK,IAAIR,OAAO,CAAC,EAAE,GAAGW,IAAAA,mBAAQ,KAAKX,QAAgC,EAAGY,EAAE,CAAC;QACzG,QAAQ;QACR,gDAAgD;QAChD,gBAAgB;QAChB,uBAAuB;QACvB,mDAAmD;QACnD,iCAAiC;QACjC,eAAe;QACf,uCAAuC;QACvC,MAAMC,cAAcxB,IAAAA,eAAG,CAAA,CAAC;;WAEjB,EAAEM,kBAAK,CAAC;gBACH,EAAEe,SAAS,IAAI,EAAEA,SAAS5D,EAAE,CAAC;YACjC,EAAE4D,SAAS5D,EAAE,CAAC;IACtB,CAAC;QACD,IAAI;YACF,MAAM,IAAI,CAACF,EAAE,CAACkE,WAAW,CAAC,OAAOC;gBAC/B,MAAM,CAAC5B,EAAE,GAAG,MAAM4B,GAAG3B,OAAO,CAACyB;gBAC7B,IAAI,CAACtF,MAAM,CAACC,GAAG,CAAC,GAAG,IAAI,CAACuE,iBAAiB,CAACtE,IAAI,CAAC,UAAU,EAAE0D,EAAEI,YAAY,EAAE;YAC7E;QACF,EAAE,OAAO/C,GAAG;YACV,IAAI,CAACjB,MAAM,CAACC,GAAG,CAAC,GAAG,IAAI,CAACuE,iBAAiB,CAACtE,IAAI,CAAC,GAAG,EAAEe,GAAG;QACzD;QACA,IAAI,CAACjB,MAAM,CAACC,GAAG,CAAC,GAAG,IAAI,CAACuE,iBAAiB,CAACtE,IAAI,CAAC,MAAM,CAAC;IACxD;IAvIA,YACE,AAA4CmB,EAAY,EACxD,AAAiBhB,KAAY,EAC7B,AAAiBiE,mBAAwC,CACzD;aAH4CjD,KAAAA;aAC3BhB,QAAAA;aACAiE,sBAAAA;aALFtE,SAAS,IAAIyF,cAAM,CAAC3F,eAAeI,IAAI;IAMrD;AAoIL;;;;;;;;iDA7GuBwF;;;;;;;iDAqCAC;;;;;;;iDA2BAC;;;;;;iDASAC"}
|
|
@@ -15,6 +15,7 @@ Object.defineProperty(exports, "FilesSearchManager", {
|
|
|
15
15
|
const _common = require("@nestjs/common");
|
|
16
16
|
const _promises = /*#__PURE__*/ _interop_require_default(require("node:fs/promises"));
|
|
17
17
|
const _nodepath = /*#__PURE__*/ _interop_require_default(require("node:path"));
|
|
18
|
+
const _configenvironment = require("../../../configuration/config.environment");
|
|
18
19
|
const _sharesqueriesservice = require("../../shares/services/shares-queries.service");
|
|
19
20
|
const _spacesqueriesservice = require("../../spaces/services/spaces-queries.service");
|
|
20
21
|
const _indexing = require("../constants/indexing");
|
|
@@ -43,6 +44,9 @@ let FilesSearchManager = class FilesSearchManager {
|
|
|
43
44
|
this.sharesQueries.shareIds(user.id, +user.isAdmin)
|
|
44
45
|
]);
|
|
45
46
|
if (search.fullText) {
|
|
47
|
+
if (!_configenvironment.configuration.applications.files.contentIndexing) {
|
|
48
|
+
throw new _common.HttpException('Full-text search is disabled', _common.HttpStatus.BAD_REQUEST);
|
|
49
|
+
}
|
|
46
50
|
return await this.searchFullText(user.id, spaceIds, shareIds, search.content, search.limit);
|
|
47
51
|
} else {
|
|
48
52
|
return await this.searchFileNames(user.id, spaceIds, shareIds, search.content, search.limit);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../backend/src/applications/files/services/files-search-manager.service.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common'\nimport fs from 'fs/promises'\nimport { Stats } from 'node:fs'\nimport path from 'node:path'\nimport { SharesQueries } from '../../shares/services/shares-queries.service'\nimport { SpacesQueries } from '../../spaces/services/spaces-queries.service'\nimport { UserModel } from '../../users/models/user.model'\nimport { shareIndexPrefix, spaceIndexPrefix, userIndexPrefix } from '../constants/indexing'\nimport { SearchFilesDto } from '../dto/file-operations.dto'\nimport { FilesIndexer } from '../models/files-indexer'\nimport { FileContent } from '../schemas/file-content.interface'\nimport { dirName, fileName, getMimeType } from '../utils/files'\nimport { genRegexPositiveAndNegativeTerms } from '../utils/files-search'\nimport { FilesParser } from './files-parser.service'\n\n@Injectable()\nexport class FilesSearchManager {\n private readonly logger = new Logger(FilesSearchManager.name)\n\n constructor(\n private readonly filesIndexer: FilesIndexer,\n private readonly filesParser: FilesParser,\n private readonly spacesQueries: SpacesQueries,\n private readonly sharesQueries: SharesQueries\n ) {}\n\n async search(user: UserModel, search: SearchFilesDto): Promise<FileContent[]> {\n const [spaceIds, shareIds] = await Promise.all([this.spacesQueries.spaceIds(user.id), this.sharesQueries.shareIds(user.id, +user.isAdmin)])\n if (search.fullText) {\n return await this.searchFullText(user.id, spaceIds, shareIds, search.content, search.limit)\n } else {\n return await this.searchFileNames(user.id, spaceIds, shareIds, search.content, search.limit)\n }\n }\n\n private async searchFullText(userId: number, spaceIds: number[], shareIds: number[], search: string, limit: number): Promise<FileContent[]> {\n const indexNames = await this.filesIndexer.existingIndexes([\n `${userIndexPrefix}${userId}`,\n ...spaceIds.map((id) => `${spaceIndexPrefix}${id}`),\n ...shareIds.map((id) => `${shareIndexPrefix}${id}`)\n ])\n if (indexNames.length === 0) {\n return []\n }\n try {\n return await this.filesIndexer.searchRecords(indexNames, search, limit)\n } catch (e) {\n this.logger.error(`${this.searchFullText.name} - ${JSON.stringify(indexNames)} - ${search} : ${e}`)\n let msg: string\n if (/Invalid regular expression/.test(e.message)) {\n msg = 'SyntaxError (check special characters)'\n } else {\n msg = e.message\n }\n throw new HttpException(msg, HttpStatus.BAD_REQUEST)\n }\n }\n\n private async searchFileNames(userId: number, spaceIds: number[], shareIds: number[], search: string, limit: number): Promise<FileContent[]> {\n const fileContents: FileContent[] = []\n const regexpTerms = genRegexPositiveAndNegativeTerms(search)\n for await (const [_id, _type, paths] of this.filesParser.allPaths(userId, spaceIds, shareIds)) {\n for (const p of paths) {\n const regexBasePath = new RegExp(`^/?${p.realPath}/?`)\n if (!p.isDir) {\n const f = await this.analyzeFile(p.realPath, p.pathPrefix, regexBasePath, regexpTerms)\n if (f !== null) {\n fileContents.push(f)\n }\n continue\n }\n for await (const fileContent of this.parseFileNames(p.realPath, p.pathPrefix, regexBasePath, regexpTerms)) {\n fileContents.push(fileContent)\n if (fileContents.length >= limit) {\n return fileContents\n }\n }\n }\n }\n return fileContents\n }\n\n private async *parseFileNames(dir: string, pathPrefix: string, regexBasePath: RegExp, regexpTerms: RegExp): AsyncGenerator<FileContent> {\n try {\n for (const entry of await fs.readdir(dir, { withFileTypes: true })) {\n const realPath = path.join(entry.parentPath, entry.name)\n const fileContent = await this.analyzeFile(realPath, pathPrefix, regexBasePath, regexpTerms)\n if (fileContent !== null) {\n yield fileContent\n }\n if (entry.isDirectory()) {\n yield* this.parseFileNames(realPath, pathPrefix, regexBasePath, regexpTerms)\n }\n }\n } catch (e) {\n this.logger.warn(`${this.parseFileNames.name} - unable to parse: ${dir} (${e})`)\n }\n }\n\n private async analyzeFile(realPath: string, pathPrefix: string, regexBasePath: RegExp, regexpTerms: RegExp): Promise<FileContent> {\n const filePath = realPath.replace(regexBasePath, '')\n if (!regexpTerms.test(filePath)) return null\n const stats: Stats = await fs.stat(realPath)\n return {\n id: stats.ino,\n path: path.join(pathPrefix, dirName(filePath)),\n name: fileName(filePath),\n mime: getMimeType(realPath, stats.isDirectory()),\n size: stats.size,\n mtime: stats.mtime.getTime()\n }\n }\n}\n"],"names":["FilesSearchManager","search","user","spaceIds","shareIds","Promise","all","spacesQueries","id","sharesQueries","isAdmin","fullText","searchFullText","content","limit","searchFileNames","userId","indexNames","filesIndexer","existingIndexes","userIndexPrefix","map","spaceIndexPrefix","shareIndexPrefix","length","searchRecords","e","logger","error","name","JSON","stringify","msg","test","message","
|
|
1
|
+
{"version":3,"sources":["../../../../../backend/src/applications/files/services/files-search-manager.service.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common'\nimport fs from 'fs/promises'\nimport { Stats } from 'node:fs'\nimport path from 'node:path'\nimport { configuration } from '../../../configuration/config.environment'\nimport { SharesQueries } from '../../shares/services/shares-queries.service'\nimport { SpacesQueries } from '../../spaces/services/spaces-queries.service'\nimport { UserModel } from '../../users/models/user.model'\nimport { shareIndexPrefix, spaceIndexPrefix, userIndexPrefix } from '../constants/indexing'\nimport { SearchFilesDto } from '../dto/file-operations.dto'\nimport { FilesIndexer } from '../models/files-indexer'\nimport { FileContent } from '../schemas/file-content.interface'\nimport { dirName, fileName, getMimeType } from '../utils/files'\nimport { genRegexPositiveAndNegativeTerms } from '../utils/files-search'\nimport { FilesParser } from './files-parser.service'\n\n@Injectable()\nexport class FilesSearchManager {\n private readonly logger = new Logger(FilesSearchManager.name)\n\n constructor(\n private readonly filesIndexer: FilesIndexer,\n private readonly filesParser: FilesParser,\n private readonly spacesQueries: SpacesQueries,\n private readonly sharesQueries: SharesQueries\n ) {}\n\n async search(user: UserModel, search: SearchFilesDto): Promise<FileContent[]> {\n const [spaceIds, shareIds] = await Promise.all([this.spacesQueries.spaceIds(user.id), this.sharesQueries.shareIds(user.id, +user.isAdmin)])\n if (search.fullText) {\n if (!configuration.applications.files.contentIndexing) {\n throw new HttpException('Full-text search is disabled', HttpStatus.BAD_REQUEST)\n }\n return await this.searchFullText(user.id, spaceIds, shareIds, search.content, search.limit)\n } else {\n return await this.searchFileNames(user.id, spaceIds, shareIds, search.content, search.limit)\n }\n }\n\n private async searchFullText(userId: number, spaceIds: number[], shareIds: number[], search: string, limit: number): Promise<FileContent[]> {\n const indexNames = await this.filesIndexer.existingIndexes([\n `${userIndexPrefix}${userId}`,\n ...spaceIds.map((id) => `${spaceIndexPrefix}${id}`),\n ...shareIds.map((id) => `${shareIndexPrefix}${id}`)\n ])\n if (indexNames.length === 0) {\n return []\n }\n try {\n return await this.filesIndexer.searchRecords(indexNames, search, limit)\n } catch (e) {\n this.logger.error(`${this.searchFullText.name} - ${JSON.stringify(indexNames)} - ${search} : ${e}`)\n let msg: string\n if (/Invalid regular expression/.test(e.message)) {\n msg = 'SyntaxError (check special characters)'\n } else {\n msg = e.message\n }\n throw new HttpException(msg, HttpStatus.BAD_REQUEST)\n }\n }\n\n private async searchFileNames(userId: number, spaceIds: number[], shareIds: number[], search: string, limit: number): Promise<FileContent[]> {\n const fileContents: FileContent[] = []\n const regexpTerms = genRegexPositiveAndNegativeTerms(search)\n for await (const [_id, _type, paths] of this.filesParser.allPaths(userId, spaceIds, shareIds)) {\n for (const p of paths) {\n const regexBasePath = new RegExp(`^/?${p.realPath}/?`)\n if (!p.isDir) {\n const f = await this.analyzeFile(p.realPath, p.pathPrefix, regexBasePath, regexpTerms)\n if (f !== null) {\n fileContents.push(f)\n }\n continue\n }\n for await (const fileContent of this.parseFileNames(p.realPath, p.pathPrefix, regexBasePath, regexpTerms)) {\n fileContents.push(fileContent)\n if (fileContents.length >= limit) {\n return fileContents\n }\n }\n }\n }\n return fileContents\n }\n\n private async *parseFileNames(dir: string, pathPrefix: string, regexBasePath: RegExp, regexpTerms: RegExp): AsyncGenerator<FileContent> {\n try {\n for (const entry of await fs.readdir(dir, { withFileTypes: true })) {\n const realPath = path.join(entry.parentPath, entry.name)\n const fileContent = await this.analyzeFile(realPath, pathPrefix, regexBasePath, regexpTerms)\n if (fileContent !== null) {\n yield fileContent\n }\n if (entry.isDirectory()) {\n yield* this.parseFileNames(realPath, pathPrefix, regexBasePath, regexpTerms)\n }\n }\n } catch (e) {\n this.logger.warn(`${this.parseFileNames.name} - unable to parse: ${dir} (${e})`)\n }\n }\n\n private async analyzeFile(realPath: string, pathPrefix: string, regexBasePath: RegExp, regexpTerms: RegExp): Promise<FileContent> {\n const filePath = realPath.replace(regexBasePath, '')\n if (!regexpTerms.test(filePath)) return null\n const stats: Stats = await fs.stat(realPath)\n return {\n id: stats.ino,\n path: path.join(pathPrefix, dirName(filePath)),\n name: fileName(filePath),\n mime: getMimeType(realPath, stats.isDirectory()),\n size: stats.size,\n mtime: stats.mtime.getTime()\n }\n }\n}\n"],"names":["FilesSearchManager","search","user","spaceIds","shareIds","Promise","all","spacesQueries","id","sharesQueries","isAdmin","fullText","configuration","applications","files","contentIndexing","HttpException","HttpStatus","BAD_REQUEST","searchFullText","content","limit","searchFileNames","userId","indexNames","filesIndexer","existingIndexes","userIndexPrefix","map","spaceIndexPrefix","shareIndexPrefix","length","searchRecords","e","logger","error","name","JSON","stringify","msg","test","message","fileContents","regexpTerms","genRegexPositiveAndNegativeTerms","_id","_type","paths","filesParser","allPaths","p","regexBasePath","RegExp","realPath","isDir","f","analyzeFile","pathPrefix","push","fileContent","parseFileNames","dir","entry","fs","readdir","withFileTypes","path","join","parentPath","isDirectory","warn","filePath","replace","stats","stat","ino","dirName","fileName","mime","getMimeType","size","mtime","getTime","Logger"],"mappings":"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"}
|
|
@@ -12,10 +12,8 @@ Object.defineProperty(exports, "parsePdf", {
|
|
|
12
12
|
return parsePdf;
|
|
13
13
|
}
|
|
14
14
|
});
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
_pdf.GlobalWorkerOptions.workerSrc = require.resolve('pdfjs-dist/legacy/build/pdf.worker.mjs');
|
|
18
|
-
// Type guard to filter true text items
|
|
15
|
+
const _promises = require("node:fs/promises");
|
|
16
|
+
const _unpdf = require("unpdf");
|
|
19
17
|
function isTextItem(item) {
|
|
20
18
|
return typeof item.str === 'string' && Array.isArray(item.transform);
|
|
21
19
|
}
|
|
@@ -25,13 +23,13 @@ const ignorePdfBadFormat = new Set([
|
|
|
25
23
|
]);
|
|
26
24
|
async function parsePdf(filePath, options) {
|
|
27
25
|
let doc;
|
|
26
|
+
const buffer = await (0, _promises.readFile)(filePath);
|
|
28
27
|
try {
|
|
29
28
|
// Load the document, allowing system fonts as fallback
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
const doc = await (0, _unpdf.getDocumentProxy)(new Uint8Array(buffer), {
|
|
30
|
+
disableFontFace: true,
|
|
31
|
+
verbosity: 0
|
|
33
32
|
});
|
|
34
|
-
doc = await loadingTask.promise;
|
|
35
33
|
const fragments = [];
|
|
36
34
|
let lastY = undefined;
|
|
37
35
|
for(let pageNum = 1; pageNum <= doc.numPages; pageNum++){
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../backend/src/applications/files/utils/doc-textify/adapters/pdf.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../../../../../backend/src/applications/files/utils/doc-textify/adapters/pdf.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\nimport { readFile } from 'node:fs/promises'\nimport { getDocumentProxy } from 'unpdf'\nimport type { PDFDocumentProxy } from 'unpdf/pdfjs'\nimport type { DocTextifyOptions } from '../interfaces/doc-textify.interfaces'\n\n// Type guard to filter true text items\ninterface TextItem {\n str: string\n transform: [number, number, number, number, number, number]\n}\nfunction isTextItem(item: any): item is TextItem {\n return typeof item.str === 'string' && Array.isArray(item.transform)\n}\n\nconst ignorePdfBadFormat = new Set([0x0000, 0x0001])\n\n/** Parse PDF files */\nexport async function parsePdf(filePath: string, options: DocTextifyOptions): Promise<string> {\n let doc: PDFDocumentProxy\n const buffer = await readFile(filePath)\n\n try {\n // Load the document, allowing system fonts as fallback\n const doc = await getDocumentProxy(new Uint8Array(buffer), {\n disableFontFace: true,\n verbosity: 0\n })\n const fragments: string[] = []\n let lastY: number | undefined = undefined\n\n for (let pageNum = 1; pageNum <= doc.numPages; pageNum++) {\n const page = await doc.getPage(pageNum)\n const { items } = await page.getTextContent()\n\n for (const item of items) {\n // Skip non-text items\n if (!isTextItem(item)) continue\n\n const currentY = item.transform[5]\n if (lastY !== undefined && currentY !== lastY) {\n fragments.push(options.newlineDelimiter)\n }\n\n fragments.push(item.str)\n lastY = currentY\n }\n page.cleanup()\n }\n\n const content = fragments.join('')\n if (ignorePdfBadFormat.has(content.charCodeAt(0))) {\n return ''\n }\n return content\n } catch (e) {\n if (options.outputErrorToConsole) {\n console.error('Error parsing PDF:', e)\n }\n throw e\n } finally {\n doc?.destroy().catch((e: Error) => console.error(e))\n }\n}\n"],"names":["parsePdf","isTextItem","item","str","Array","isArray","transform","ignorePdfBadFormat","Set","filePath","options","doc","buffer","readFile","getDocumentProxy","Uint8Array","disableFontFace","verbosity","fragments","lastY","undefined","pageNum","numPages","page","getPage","items","getTextContent","currentY","push","newlineDelimiter","cleanup","content","join","has","charCodeAt","e","outputErrorToConsole","console","error","destroy","catch"],"mappings":"AAAA;;;;CAIC;;;;+BAkBqBA;;;eAAAA;;;0BAjBG;uBACQ;AASjC,SAASC,WAAWC,IAAS;IAC3B,OAAO,OAAOA,KAAKC,GAAG,KAAK,YAAYC,MAAMC,OAAO,CAACH,KAAKI,SAAS;AACrE;AAEA,MAAMC,qBAAqB,IAAIC,IAAI;IAAC;IAAQ;CAAO;AAG5C,eAAeR,SAASS,QAAgB,EAAEC,OAA0B;IACzE,IAAIC;IACJ,MAAMC,SAAS,MAAMC,IAAAA,kBAAQ,EAACJ;IAE9B,IAAI;QACF,uDAAuD;QACvD,MAAME,MAAM,MAAMG,IAAAA,uBAAgB,EAAC,IAAIC,WAAWH,SAAS;YACzDI,iBAAiB;YACjBC,WAAW;QACb;QACA,MAAMC,YAAsB,EAAE;QAC9B,IAAIC,QAA4BC;QAEhC,IAAK,IAAIC,UAAU,GAAGA,WAAWV,IAAIW,QAAQ,EAAED,UAAW;YACxD,MAAME,OAAO,MAAMZ,IAAIa,OAAO,CAACH;YAC/B,MAAM,EAAEI,KAAK,EAAE,GAAG,MAAMF,KAAKG,cAAc;YAE3C,KAAK,MAAMxB,QAAQuB,MAAO;gBACxB,sBAAsB;gBACtB,IAAI,CAACxB,WAAWC,OAAO;gBAEvB,MAAMyB,WAAWzB,KAAKI,SAAS,CAAC,EAAE;gBAClC,IAAIa,UAAUC,aAAaO,aAAaR,OAAO;oBAC7CD,UAAUU,IAAI,CAAClB,QAAQmB,gBAAgB;gBACzC;gBAEAX,UAAUU,IAAI,CAAC1B,KAAKC,GAAG;gBACvBgB,QAAQQ;YACV;YACAJ,KAAKO,OAAO;QACd;QAEA,MAAMC,UAAUb,UAAUc,IAAI,CAAC;QAC/B,IAAIzB,mBAAmB0B,GAAG,CAACF,QAAQG,UAAU,CAAC,KAAK;YACjD,OAAO;QACT;QACA,OAAOH;IACT,EAAE,OAAOI,GAAG;QACV,IAAIzB,QAAQ0B,oBAAoB,EAAE;YAChCC,QAAQC,KAAK,CAAC,sBAAsBH;QACtC;QACA,MAAMA;IACR,SAAU;QACRxB,KAAK4B,UAAUC,MAAM,CAACL,IAAaE,QAAQC,KAAK,CAACH;IACnD;AACF"}
|