@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.
Files changed (275) hide show
  1. package/CHANGELOG.md +173 -58
  2. package/environment/environment.dist.yaml +6 -3
  3. package/migrations/0003_giant_luckman.sql +6 -0
  4. package/migrations/meta/0003_snapshot.json +2463 -0
  5. package/migrations/meta/_journal.json +7 -0
  6. package/package.json +19 -17
  7. package/server/app.bootstrap.js +5 -2
  8. package/server/app.bootstrap.js.map +1 -1
  9. package/server/app.constants.js +0 -4
  10. package/server/app.constants.js.map +1 -1
  11. package/server/app.service.js +7 -6
  12. package/server/app.service.js.map +1 -1
  13. package/server/applications/files/constants/only-office.js +12 -0
  14. package/server/applications/files/constants/only-office.js.map +1 -1
  15. package/server/applications/files/files.config.js +5 -0
  16. package/server/applications/files/files.config.js.map +1 -1
  17. package/server/applications/files/files.controller.js +12 -4
  18. package/server/applications/files/files.controller.js.map +1 -1
  19. package/server/applications/files/files.controller.spec.js +18 -4
  20. package/server/applications/files/files.controller.spec.js.map +1 -1
  21. package/server/applications/files/services/files-content-manager.service.js +6 -6
  22. package/server/applications/files/services/files-content-manager.service.js.map +1 -1
  23. package/server/applications/files/services/files-manager.service.js +4 -4
  24. package/server/applications/files/services/files-manager.service.js.map +1 -1
  25. package/server/applications/files/services/files-methods.service.js +4 -7
  26. package/server/applications/files/services/files-methods.service.js.map +1 -1
  27. package/server/applications/files/services/files-only-office-manager.service.js +2 -2
  28. package/server/applications/files/services/files-only-office-manager.service.js.map +1 -1
  29. package/server/applications/files/services/files-parser.service.js +6 -3
  30. package/server/applications/files/services/files-parser.service.js.map +1 -1
  31. package/server/applications/files/services/files-scheduler.service.js +51 -3
  32. package/server/applications/files/services/files-scheduler.service.js.map +1 -1
  33. package/server/applications/files/services/files-search-manager.service.js +4 -0
  34. package/server/applications/files/services/files-search-manager.service.js.map +1 -1
  35. package/server/applications/files/utils/doc-textify/adapters/pdf.js +6 -8
  36. package/server/applications/files/utils/doc-textify/adapters/pdf.js.map +1 -1
  37. package/server/applications/notifications/i18n/de.js +56 -0
  38. package/server/applications/notifications/i18n/de.js.map +1 -0
  39. package/server/applications/notifications/i18n/es.js +52 -0
  40. package/server/applications/notifications/i18n/es.js.map +1 -0
  41. package/server/applications/notifications/i18n/hi.js +52 -0
  42. package/server/applications/notifications/i18n/hi.js.map +1 -0
  43. package/server/applications/notifications/i18n/index.js +73 -8
  44. package/server/applications/notifications/i18n/index.js.map +1 -1
  45. package/server/applications/notifications/i18n/it.js +52 -0
  46. package/server/applications/notifications/i18n/it.js.map +1 -0
  47. package/server/applications/notifications/i18n/ja.js +52 -0
  48. package/server/applications/notifications/i18n/ja.js.map +1 -0
  49. package/server/applications/notifications/i18n/ko.js +52 -0
  50. package/server/applications/notifications/i18n/ko.js.map +1 -0
  51. package/server/applications/notifications/i18n/pl.js +52 -0
  52. package/server/applications/notifications/i18n/pl.js.map +1 -0
  53. package/server/applications/notifications/i18n/pt.js +52 -0
  54. package/server/applications/notifications/i18n/pt.js.map +1 -0
  55. package/server/applications/notifications/i18n/pt_br.js +52 -0
  56. package/server/applications/notifications/i18n/pt_br.js.map +1 -0
  57. package/server/applications/notifications/i18n/ru.js +52 -0
  58. package/server/applications/notifications/i18n/ru.js.map +1 -0
  59. package/server/applications/notifications/i18n/tr.js +52 -0
  60. package/server/applications/notifications/i18n/tr.js.map +1 -0
  61. package/server/applications/notifications/i18n/zh.js +52 -0
  62. package/server/applications/notifications/i18n/zh.js.map +1 -0
  63. package/server/applications/notifications/mails/models.js +6 -7
  64. package/server/applications/notifications/mails/models.js.map +1 -1
  65. package/server/applications/notifications/services/notifications-manager.service.js.map +1 -1
  66. package/server/applications/shares/dto/create-or-update-share.dto.js +11 -0
  67. package/server/applications/shares/dto/create-or-update-share.dto.js.map +1 -1
  68. package/server/applications/shares/interfaces/share-props.interface.js.map +1 -1
  69. package/server/applications/shares/schemas/share.interface.js.map +1 -1
  70. package/server/applications/shares/schemas/shares.schema.js +9 -0
  71. package/server/applications/shares/schemas/shares.schema.js.map +1 -1
  72. package/server/applications/shares/services/shares-manager.service.js +46 -17
  73. package/server/applications/shares/services/shares-manager.service.js.map +1 -1
  74. package/server/applications/shares/services/shares-queries.service.js +24 -5
  75. package/server/applications/shares/services/shares-queries.service.js.map +1 -1
  76. package/server/applications/spaces/constants/cache.js +4 -0
  77. package/server/applications/spaces/constants/cache.js.map +1 -1
  78. package/server/applications/spaces/dto/create-or-update-space.dto.js +5 -0
  79. package/server/applications/spaces/dto/create-or-update-space.dto.js.map +1 -1
  80. package/server/applications/spaces/guards/space.guard.js +3 -3
  81. package/server/applications/spaces/guards/space.guard.js.map +1 -1
  82. package/server/applications/spaces/models/space-props.model.js.map +1 -1
  83. package/server/applications/spaces/models/space.model.js.map +1 -1
  84. package/server/applications/spaces/schemas/space.interface.js.map +1 -1
  85. package/server/applications/spaces/schemas/spaces.schema.js +1 -0
  86. package/server/applications/spaces/schemas/spaces.schema.js.map +1 -1
  87. package/server/applications/spaces/services/spaces-browser.service.js.map +1 -1
  88. package/server/applications/spaces/services/spaces-manager.service.js +34 -31
  89. package/server/applications/spaces/services/spaces-manager.service.js.map +1 -1
  90. package/server/applications/spaces/services/spaces-queries.service.js +23 -7
  91. package/server/applications/spaces/services/spaces-queries.service.js.map +1 -1
  92. package/server/applications/spaces/services/spaces-scheduler.service.js +21 -20
  93. package/server/applications/spaces/services/spaces-scheduler.service.js.map +1 -1
  94. package/server/applications/spaces/spaces.controller.js +4 -2
  95. package/server/applications/spaces/spaces.controller.js.map +1 -1
  96. package/server/applications/spaces/utils/paths.js +14 -16
  97. package/server/applications/spaces/utils/paths.js.map +1 -1
  98. package/server/applications/sync/services/sync-manager.service.js +4 -3
  99. package/server/applications/sync/services/sync-manager.service.js.map +1 -1
  100. package/server/applications/sync/services/sync-paths-manager.service.js +1 -1
  101. package/server/applications/sync/services/sync-paths-manager.service.js.map +1 -1
  102. package/server/applications/sync/services/sync-paths-manager.service.spec.js +1 -1
  103. package/server/applications/sync/services/sync-paths-manager.service.spec.js.map +1 -1
  104. package/server/applications/sync/sync.controller.js +2 -1
  105. package/server/applications/sync/sync.controller.js.map +1 -1
  106. package/server/applications/users/constants/routes.js +5 -0
  107. package/server/applications/users/constants/routes.js.map +1 -1
  108. package/server/applications/users/constants/user.js +0 -16
  109. package/server/applications/users/constants/user.js.map +1 -1
  110. package/server/applications/users/dto/user-properties.dto.js +10 -0
  111. package/server/applications/users/dto/user-properties.dto.js.map +1 -1
  112. package/server/applications/users/models/user.model.js.map +1 -1
  113. package/server/applications/users/schemas/user.interface.js.map +1 -1
  114. package/server/applications/users/schemas/users.schema.js +3 -2
  115. package/server/applications/users/schemas/users.schema.js.map +1 -1
  116. package/server/applications/users/services/admin-users-manager.service.js +1 -0
  117. package/server/applications/users/services/admin-users-manager.service.js.map +1 -1
  118. package/server/applications/users/services/admin-users-manager.service.spec.js +2 -1
  119. package/server/applications/users/services/admin-users-manager.service.spec.js.map +1 -1
  120. package/server/applications/users/services/users-manager.service.js +7 -2
  121. package/server/applications/users/services/users-manager.service.js.map +1 -1
  122. package/server/applications/users/services/users-manager.service.spec.js +1 -0
  123. package/server/applications/users/services/users-manager.service.spec.js.map +1 -1
  124. package/server/applications/users/services/users-queries.service.js +18 -4
  125. package/server/applications/users/services/users-queries.service.js.map +1 -1
  126. package/server/applications/users/users.controller.js +15 -0
  127. package/server/applications/users/users.controller.js.map +1 -1
  128. package/server/applications/users/users.gateway.js +6 -0
  129. package/server/applications/users/users.gateway.js.map +1 -1
  130. package/server/applications/users/utils/test.js +2 -2
  131. package/server/applications/users/utils/test.js.map +1 -1
  132. package/server/applications/webdav/constants/routes.js +2 -2
  133. package/server/applications/webdav/constants/routes.js.map +1 -1
  134. package/server/applications/webdav/constants/webdav.js +2 -2
  135. package/server/applications/webdav/constants/webdav.js.map +1 -1
  136. package/server/applications/webdav/filters/webdav.filter.js +2 -2
  137. package/server/applications/webdav/filters/webdav.filter.js.map +1 -1
  138. package/server/applications/webdav/filters/webdav.filter.spec.js +2 -2
  139. package/server/applications/webdav/filters/webdav.filter.spec.js.map +1 -1
  140. package/server/applications/webdav/services/webdav-methods.service.js +3 -2
  141. package/server/applications/webdav/services/webdav-methods.service.js.map +1 -1
  142. package/server/applications/webdav/utils/webdav.js +1 -2
  143. package/server/applications/webdav/utils/webdav.js.map +1 -1
  144. package/server/authentication/auth.config.js +2 -2
  145. package/server/authentication/auth.config.js.map +1 -1
  146. package/server/authentication/guards/auth-basic.strategy.js +2 -2
  147. package/server/authentication/guards/auth-basic.strategy.js.map +1 -1
  148. package/server/common/i18n.js +52 -0
  149. package/server/common/i18n.js.map +1 -0
  150. package/server/common/image.js +63 -43
  151. package/server/common/image.js.map +1 -1
  152. package/server/common/interfaces.js.map +1 -1
  153. package/server/common/shared.js +5 -2
  154. package/server/common/shared.js.map +1 -1
  155. package/server/configuration/config.validation.js +3 -3
  156. package/server/configuration/config.validation.js.map +1 -1
  157. package/server/infrastructure/cache/adapters/mysql-cache.adapter.js +8 -6
  158. package/server/infrastructure/cache/adapters/mysql-cache.adapter.js.map +1 -1
  159. package/server/infrastructure/cache/adapters/redis-cache.adapter.js +22 -17
  160. package/server/infrastructure/cache/adapters/redis-cache.adapter.js.map +1 -1
  161. package/server/infrastructure/cache/cache.e2e-spec.js +1 -0
  162. package/server/infrastructure/cache/cache.e2e-spec.js.map +1 -1
  163. package/server/infrastructure/cache/cache.module.js +1 -14
  164. package/server/infrastructure/cache/cache.module.js.map +1 -1
  165. package/server/infrastructure/cache/services/cache.service.js.map +1 -1
  166. package/server/infrastructure/database/database.module.js +20 -1
  167. package/server/infrastructure/database/database.module.js.map +1 -1
  168. package/server/infrastructure/database/utils.js +48 -0
  169. package/server/infrastructure/database/utils.js.map +1 -1
  170. package/server/infrastructure/scheduler/scheduler.module.js +1 -1
  171. package/server/infrastructure/scheduler/scheduler.module.js.map +1 -1
  172. package/server/infrastructure/websocket/adapters/cluster.adapter.js +1 -3
  173. package/server/infrastructure/websocket/adapters/cluster.adapter.js.map +1 -1
  174. package/static/3rdpartylicenses.txt +137 -163
  175. package/static/chunk-2KLC4T2Z.js +1 -0
  176. package/static/chunk-2VMSXRCB.js +12 -0
  177. package/static/chunk-3GMLWAFZ.js +1 -0
  178. package/static/chunk-3OHSRRKH.js +4 -0
  179. package/static/chunk-3R4WKOHQ.js +1 -0
  180. package/static/{chunk-7ITZXYYJ.js → chunk-3R74L4UU.js} +1 -1
  181. package/static/chunk-3XVM35O2.js +1 -0
  182. package/static/chunk-3YVRP3VM.js +2 -0
  183. package/static/chunk-5NMSIIQB.js +1 -0
  184. package/static/chunk-5UKZLU5H.js +1 -0
  185. package/static/chunk-AF24EYXU.js +1 -0
  186. package/static/chunk-AKQVEHO6.js +2 -0
  187. package/static/chunk-BCVX464U.js +2 -0
  188. package/static/chunk-BQV4FRM6.js +1 -0
  189. package/static/{chunk-EVIE5F2U.js → chunk-CETH7UYS.js} +1 -1
  190. package/static/chunk-CHJ64RJM.js +1 -0
  191. package/static/chunk-DIT6W7VM.js +562 -0
  192. package/static/chunk-DKSEQTMX.js +1 -0
  193. package/static/chunk-DM4NXKEP.js +1 -0
  194. package/static/chunk-DPUVSXRB.js +1 -0
  195. package/static/chunk-DSWEWLXJ.js +1 -0
  196. package/static/chunk-FJE6BOFL.js +1 -0
  197. package/static/chunk-FZ3JPGYZ.js +1 -0
  198. package/static/chunk-IQSKQXC3.js +1 -0
  199. package/static/chunk-ITUFI2BJ.js +1 -0
  200. package/static/chunk-JPT5WEAT.js +1 -0
  201. package/static/chunk-LCTZJ537.js +1 -0
  202. package/static/chunk-LK2UCQJ6.js +1 -0
  203. package/static/chunk-LNTUR3GU.js +1 -0
  204. package/static/chunk-LP5TBXEN.js +7 -0
  205. package/static/{chunk-IPAC4VAF.js → chunk-LVSNIS5P.js} +1 -1
  206. package/static/{chunk-SIPE37PA.js → chunk-MTVSJTIW.js} +1 -1
  207. package/static/chunk-N3U6637P.js +1 -0
  208. package/static/chunk-NNV4OXSB.js +1 -0
  209. package/static/chunk-O6FYXVHI.js +1 -0
  210. package/static/chunk-OOGP4WSH.js +2 -0
  211. package/static/chunk-PB4AIT7O.js +1 -0
  212. package/static/chunk-PCWDQPOM.js +2 -0
  213. package/static/chunk-PNR6M34W.js +1 -0
  214. package/static/chunk-PVDHBQRM.js +1 -0
  215. package/static/chunk-Q5KM7LTX.js +1 -0
  216. package/static/chunk-QHC6ZPQ4.js +1 -0
  217. package/static/chunk-QMRBZHE4.js +1 -0
  218. package/static/chunk-QO6BTONN.js +1 -0
  219. package/static/chunk-QSJRY3TF.js +1 -0
  220. package/static/chunk-QUUIRSYT.js +1 -0
  221. package/static/chunk-RFH46UW3.js +1 -0
  222. package/static/{chunk-PTGDOWV3.js → chunk-RSXHRKM5.js} +1 -1
  223. package/static/chunk-RV3VZJPZ.js +1 -0
  224. package/static/{chunk-QNJFQVYI.js → chunk-S7HNXVRB.js} +1 -1
  225. package/static/chunk-SBZ572Q4.js +2 -0
  226. package/static/chunk-SJR5R3Y4.js +1 -0
  227. package/static/chunk-SLHTEGRU.js +1 -0
  228. package/static/{chunk-SH5EVL4E.js → chunk-SSFF27P2.js} +1 -1
  229. package/static/chunk-UNCPXHHT.js +1 -0
  230. package/static/chunk-URHTCJ7G.js +1 -0
  231. package/static/chunk-V3LHHZYN.js +1 -0
  232. package/static/{chunk-DJYJ66UF.js → chunk-VJTXJ43D.js} +1 -1
  233. package/static/chunk-VQQKMY2C.js +1 -0
  234. package/static/{chunk-IQOALFYU.js → chunk-WSSU2HXE.js} +1 -1
  235. package/static/chunk-XDZGW64M.js +3 -0
  236. package/static/chunk-XTRDKGKG.js +1 -0
  237. package/static/chunk-YLWTEC3X.js +1 -0
  238. package/static/chunk-Z5J5F5SX.js +1 -0
  239. package/static/chunk-ZIJQRARU.js +1 -0
  240. package/static/index.html +2 -2
  241. package/static/main-4H5BJY3J.js +9 -0
  242. package/static/scripts-WRDOQIU5.js +24 -0
  243. package/static/{styles-A5VYX3CE.css → styles-2C2UNCNB.css} +1 -1
  244. package/server/applications/spaces/interfaces/space-quota.interface.js +0 -10
  245. package/server/applications/spaces/interfaces/space-quota.interface.js.map +0 -1
  246. package/static/chunk-22EANI6R.js +0 -1
  247. package/static/chunk-3GFGJYMK.js +0 -1
  248. package/static/chunk-4YGJGZZZ.js +0 -1
  249. package/static/chunk-5K7HEX3C.js +0 -27
  250. package/static/chunk-5KLMS6A4.js +0 -1
  251. package/static/chunk-ATP3BFHV.js +0 -562
  252. package/static/chunk-BB4G55KE.js +0 -1
  253. package/static/chunk-EWKSX76T.js +0 -1
  254. package/static/chunk-FHLACA7V.js +0 -1
  255. package/static/chunk-GCATNU55.js +0 -1
  256. package/static/chunk-GYODPCIE.js +0 -1
  257. package/static/chunk-HZTFYLM5.js +0 -1
  258. package/static/chunk-JSUKJT6Z.js +0 -1
  259. package/static/chunk-JXZCNFW7.js +0 -1
  260. package/static/chunk-LTGFCQR7.js +0 -1
  261. package/static/chunk-LV3PYKWO.js +0 -1
  262. package/static/chunk-N2WFNW6M.js +0 -7
  263. package/static/chunk-ORMRCEGT.js +0 -1
  264. package/static/chunk-OUTBJSMW.js +0 -1
  265. package/static/chunk-RS2PX32L.js +0 -1
  266. package/static/chunk-RSSWH3S2.js +0 -1
  267. package/static/chunk-RTRJ3KFH.js +0 -1
  268. package/static/chunk-TKTCBDOG.js +0 -1
  269. package/static/chunk-V6K2N46L.js +0 -1
  270. package/static/chunk-XLCCZSQL.js +0 -4
  271. package/static/chunk-YPEH66GG.js +0 -1
  272. package/static/chunk-YPOIUQ57.js +0 -1
  273. package/static/chunk-ZKCFO2OA.js +0 -4
  274. package/static/main-MZ7HWZXO.js +0 -9
  275. 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: `${_appconstants.SERVER_NAME} - ${user.fullName} (${user.email})`
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":"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"}
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)(// if the child share is from a share with an external path, the child share should have an external path and a fileId
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":"AAAA;;;;CAIC;;;;+BAsBYA;;;eAAAA;;;wBApB8B;4BACuB;iEACjD;2BACiB;mCACT;uBACoB;wBAClB;8BACJ;wBACuB;4BACnB;mCACC;8BACL;sBACG;2BACA;6BACJ;6BAEa;uBACN;;;;;;;;;;;;;;;;;;;;AAGtB,IAAA,AAAMA,cAAN,MAAMA;IAKX,OAAOC,SAASC,MAAe,EAAEC,QAAmB,EAAEC,QAAmB,EAA+D;QACtI,OAAO,IAAI,CAACC,SAAS,CAACH;QACtB,OAAO,IAAI,CAACI,UAAU,CAACH;QACvB,OAAO,IAAI,CAACI,UAAU,CAACH;IACzB;IAEA,OAAOC,UAAUH,MAAe,EAA+D;QAC7F,KAAK,MAAMM,QAAQ,CAAA,MAAM,IAAI,CAACC,EAAE,CAC7BC,MAAM,CAAC;YACNC,IAAIC,kBAAK,CAACD,EAAE;YACZE,OAAOD,kBAAK,CAACC,KAAK;QACpB,GACCC,IAAI,CAACF,kBAAK,EACVG,KAAK,CAACC,IAAAA,eAAG,KAAI;YAACC,IAAAA,cAAE,EAACL,kBAAK,CAACM,eAAe,EAAE;YAAOC,IAAAA,eAAG,EAACP,kBAAK,CAACQ,IAAI,EAAEC,eAAS,CAACC,IAAI;eAAOpB,SAAS;gBAACe,IAAAA,cAAE,EAACL,kBAAK,CAACD,EAAE,EAAET;aAAQ,GAAG,EAAE;SAAE,EAAC,EAAG;YAC/H,MAAMqB,gBAAgBC,oBAAS,CAACC,YAAY,CAACjB,KAAKK,KAAK;YACvD,IAAI,CAAE,MAAMa,IAAAA,mBAAY,EAACH,gBAAiB;gBACxC,IAAI,CAACI,MAAM,CAACC,IAAI,CAAC,GAAG,IAAI,CAACvB,SAAS,CAACwB,IAAI,CAAC,8BAA8B,EAAEN,eAAe;gBACvF;YACF;YACA,MAAM;gBAACf,KAAKG,EAAE;gBAAE;gBAAQ;oBAAC;wBAAEmB,UAAUP;wBAAeQ,YAAY,GAAGC,wBAAgB,CAACC,KAAK,CAAC,CAAC,EAAEC,mBAAW,CAACC,QAAQ,EAAE;wBAAEC,OAAO;oBAAK;iBAAE;aAAC;QACtI;IACF;IAEA,OAAO9B,WAAWH,QAAmB,EAA+D;QAClG,KAAK,MAAMkC,SAAS,CAAA,MAAM,IAAI,CAAC5B,EAAE,CAC9BC,MAAM,CAAC;YACNC,IAAI2B,oBAAM,CAAC3B,EAAE;YACb4B,OAAOD,oBAAM,CAACC,KAAK;YACnBC,OAAOC,IAAAA,mCAA4B,EAACC,8BAAW,CAACH,KAAK,EAAE;gBACrDA,OAAOG,8BAAW,CAACH,KAAK;gBACxBI,cAAcD,8BAAW,CAACC,YAAY;gBACtCP,OAAOQ,IAAAA,eAAG,CAAS,CAAC,IAAI,EAAEF,8BAAW,CAACC,YAAY,CAAC,iBAAiB,EAAEE,kBAAK,CAACT,KAAK,CAAC,CAAC,CAAC;gBACpFU,MAAM;oBACJC,MAAMC,IAAAA,wBAAW,EAACH,kBAAK;oBACvBI,WAAWrC,kBAAK,CAACC,KAAK;gBACxB;YACF;QACF,GACCC,IAAI,CAACwB,oBAAM,EACXY,QAAQ,CAACR,8BAAW,EAAEzB,IAAAA,cAAE,EAACyB,8BAAW,CAACS,OAAO,EAAEb,oBAAM,CAAC3B,EAAE,GACvDuC,QAAQ,CAACL,kBAAK,EAAE5B,IAAAA,cAAE,EAAC4B,kBAAK,CAAClC,EAAE,EAAE+B,8BAAW,CAACU,MAAM,GAC/CF,QAAQ,CAACtC,kBAAK,EAAEK,IAAAA,cAAE,EAACL,kBAAK,CAACD,EAAE,EAAEkC,kBAAK,CAACQ,OAAO,GAC1CtC,KAAK,CAACC,IAAAA,eAAG,EAACC,IAAAA,cAAE,EAACqB,oBAAM,CAACpB,eAAe,EAAE,UAAWf,WAAW;YAACmD,IAAAA,mBAAO,EAAChB,oBAAM,CAAC3B,EAAE,EAAER;SAAU,GAAG,EAAE,GAC9FoD,OAAO,CAACjB,oBAAM,CAAC3B,EAAE,CAAA,EAAG;YACrB,MAAM6C,iBAAiBC,sBAAU,CAAChC,YAAY,CAACY,MAAME,KAAK;YAC1D,IAAI,CAAE,MAAMb,IAAAA,mBAAY,EAAC8B,iBAAkB;gBACzC,IAAI,CAAC7B,MAAM,CAACC,IAAI,CAAC,GAAG,IAAI,CAACtB,UAAU,CAACuB,IAAI,CAAC,+BAA+B,EAAE2B,gBAAgB;gBAC1F;YACF;YACA,MAAME,YAAY;gBAAC;oBAAE5B,UAAU0B;oBAAgBzB,YAAY,GAAGC,wBAAgB,CAACC,KAAK,CAAC,CAAC,EAAEI,MAAME,KAAK,EAAE;oBAAEH,OAAO;gBAAK;aAAE;YACrH,MAAMuB,YAAYtB,MAAMG,KAAK,CAACoB,GAAG,CAC/B,CAACC,IACCA,EAAElB,YAAY,GACV;oBACEb,UAAU+B,EAAElB,YAAY;oBACxBZ,YAAY,GAAGC,wBAAgB,CAACC,KAAK,CAAC,CAAC,EAAEI,MAAME,KAAK,CAAC,CAAC,EAAEsB,EAAEtB,KAAK,EAAE;oBACjEH,OAAOyB,EAAEzB,KAAK;gBAChB,IACA;oBACEN,UAAUiB,iBAAI,CAACe,IAAI,CAACtC,oBAAS,CAACC,YAAY,CAACoC,EAAEf,IAAI,CAACG,SAAS,GAAGY,EAAEf,IAAI,CAACC,IAAI;oBACzEhB,YAAY,GAAGC,wBAAgB,CAACC,KAAK,CAAC,CAAC,EAAEI,MAAME,KAAK,CAAC,CAAC,EAAEsB,EAAEtB,KAAK,EAAE;oBACjEH,OAAOyB,EAAEzB,KAAK;gBAChB;YAER,MAAM;gBAACC,MAAM1B,EAAE;gBAAE;gBAAS;uBAAI+C;uBAAcC;iBAAU;aAAC;QACzD;IACF;IAEA,OAAOpD,WAAWH,QAAmB,EAA+D;QAClG,KAAK,MAAM2D,SAAS,CAAA,MAAM,IAAI,CAACtD,EAAE,CAC9BC,MAAM,CAAC;YACNC,IAAIqD,oBAAM,CAACrD,EAAE;YACb4B,OAAOyB,oBAAM,CAACzB,KAAK;YACnBI,cAAcC,IAAAA,eAAG,CAAQ,CAAC,IAAI,EAAEoB,oBAAM,CAACrB,YAAY,CAAC,cAAc,EAAEqB,oBAAM,CAACrB,YAAY,CAAC,EAAE,EAAED,8BAAW,CAACC,YAAY,CAAC,CAAC,CAAC;YACvHP,OAAOQ,IAAAA,eAAG,CAAS,CAAC,IAAI,EAAEoB,oBAAM,CAACrB,YAAY,CAAC,iBAAiB,EAAEE,kBAAK,CAACT,KAAK,CAAC,CAAC,CAAC;YAC/EU,MAAM;gBAAEC,MAAMH,IAAAA,eAAG,CAAQ,CAAC,IAAI,EAAEC,kBAAK,CAAClC,EAAE,CAAC,cAAc,EAAEqC,IAAAA,wBAAW,EAACH,kBAAK,EAAE,MAAM,CAAC;gBAAEI,WAAWrC,kBAAK,CAACC,KAAK;gBAAEoD,WAAW3B,oBAAM,CAACC,KAAK;YAAC;QACvI,GACCzB,IAAI,CAACkD,oBAAM,EACXd,QAAQ,CAACR,8BAAW,EAAEzB,IAAAA,cAAE,EAACyB,8BAAW,CAAC/B,EAAE,EAAEqD,oBAAM,CAACE,WAAW,GAC3DhB,QAAQ,CACPL,kBAAK,EACLsB,IAAAA,cAAE,EACA,sHAAsH;QACtHnD,IAAAA,eAAG,EAACoD,IAAAA,qBAAS,EAACJ,oBAAM,CAACZ,MAAM,GAAGnC,IAAAA,cAAE,EAAC4B,kBAAK,CAAClC,EAAE,EAAEqD,oBAAM,CAACZ,MAAM,IACxDpC,IAAAA,eAAG,EAACqD,IAAAA,kBAAM,EAACL,oBAAM,CAACrB,YAAY,GAAG0B,IAAAA,kBAAM,EAACL,oBAAM,CAACZ,MAAM,GAAGgB,IAAAA,qBAAS,EAAC1B,8BAAW,CAACU,MAAM,GAAGnC,IAAAA,cAAE,EAAC4B,kBAAK,CAAClC,EAAE,EAAE+B,8BAAW,CAACU,MAAM,KAGzHF,QAAQ,CAACZ,oBAAM,EAAEtB,IAAAA,eAAG,EAACqD,IAAAA,kBAAM,EAACL,oBAAM,CAACrB,YAAY,GAAGyB,IAAAA,qBAAS,EAACvB,kBAAK,CAACM,OAAO,GAAGlC,IAAAA,cAAE,EAACqB,oBAAM,CAAC3B,EAAE,EAAEkC,kBAAK,CAACM,OAAO,GAAGlC,IAAAA,cAAE,EAACqB,oBAAM,CAACpB,eAAe,EAAE,QACrIgC,QAAQ,CAACtC,kBAAK,EAAEI,IAAAA,eAAG,EAACC,IAAAA,cAAE,EAACL,kBAAK,CAACD,EAAE,EAAEkC,kBAAK,CAACQ,OAAO,GAAGpC,IAAAA,cAAE,EAACL,kBAAK,CAACM,eAAe,EAAE,QAC3EH,KAAK,CAACC,IAAAA,eAAG,EAACC,IAAAA,cAAE,EAAC+C,oBAAM,CAAC9C,eAAe,EAAE,UAAU;YAACD,IAAAA,cAAE,EAAC+C,oBAAM,CAACM,IAAI,EAAEC,kBAAU,CAACC,MAAM;eAAOpE,WAAW;gBAACkD,IAAAA,mBAAO,EAACU,oBAAM,CAACrD,EAAE,EAAEP;aAAU,GAAG,EAAE;SAAE,GACxImD,OAAO,CAACS,oBAAM,CAACrD,EAAE,CAAA,EAAG;YACrB,IAAI8D;YACJ,IAAIV,MAAMpB,YAAY,EAAE;gBACtB8B,iBAAiB1B,iBAAI,CAACe,IAAI,CAACC,MAAMpB,YAAY,EAAEoB,MAAMjB,IAAI,CAACC,IAAI;YAChE,OAAO,IAAIgB,MAAMjB,IAAI,CAACG,SAAS,EAAE;gBAC/BwB,iBAAiB1B,iBAAI,CAACe,IAAI,CAACtC,oBAAS,CAACC,YAAY,CAACsC,MAAMjB,IAAI,CAACG,SAAS,GAAGc,MAAMjB,IAAI,CAACC,IAAI;YAC1F,OAAO,IAAIgB,MAAMjB,IAAI,CAACmB,SAAS,EAAE;gBAC/BQ,iBAAiB1B,iBAAI,CAACe,IAAI,CAACL,sBAAU,CAAChC,YAAY,CAACsC,MAAMjB,IAAI,CAACmB,SAAS,GAAGF,MAAMjB,IAAI,CAACC,IAAI;YAC3F,OAAO;gBAEL;YACF;YACA,IAAI,CAAE,MAAMrB,IAAAA,mBAAY,EAAC+C,iBAAkB;gBACzC,IAAI,CAAC9C,MAAM,CAACC,IAAI,CAAC,GAAG,IAAI,CAACrB,UAAU,CAACsB,IAAI,CAAC,+BAA+B,EAAE4C,gBAAgB;gBAC1F;YACF;YACA,MAAM;gBAACV,MAAMpD,EAAE;gBAAE;gBAAS;oBAAC;wBAAEmB,UAAU2C;wBAAgB1C,YAAY,GAAGC,wBAAgB,CAAC0C,MAAM,CAAC,CAAC,EAAEX,MAAMxB,KAAK,EAAE;wBAAEH,OAAO2B,MAAM3B,KAAK;oBAAC;iBAAE;aAAC;QACxI;IACF;IA9GA,YAAY,AAA4C3B,EAAY,CAAE;aAAdA,KAAAA;aAFvCkB,SAAS,IAAIgD,cAAM,CAAC3E,YAAY6B,IAAI;IAEkB;AA+GzE"}
@@ -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.EVERY_DAY_AT_MIDNIGHT),
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)(60000),
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":"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"}
@@ -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","HttpException","HttpStatus","BAD_REQUEST","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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"}
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 _pdf = require("pdfjs-dist/legacy/build/pdf.mjs");
16
- // Enable parallel PDF parsing via Node.js worker threads
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 loadingTask = (0, _pdf.getDocument)({
31
- url: filePath,
32
- useSystemFonts: true
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 { getDocument, GlobalWorkerOptions, PDFDocumentProxy } from 'pdfjs-dist/legacy/build/pdf.mjs'\nimport { DocTextifyOptions } from '../interfaces/doc-textify.interfaces'\n\n// Enable parallel PDF parsing via Node.js worker threads\nGlobalWorkerOptions.workerSrc = require.resolve('pdfjs-dist/legacy/build/pdf.worker.mjs')\n\n// Type guard to filter true text items\nfunction isTextItem(item: any): item is { str: string; transform: [number, number, number, number, number, number] } {\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\n try {\n // Load the document, allowing system fonts as fallback\n const loadingTask = getDocument({ url: filePath, useSystemFonts: true })\n doc = await loadingTask.promise\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","GlobalWorkerOptions","workerSrc","require","resolve","isTextItem","item","str","Array","isArray","transform","ignorePdfBadFormat","Set","filePath","options","doc","loadingTask","getDocument","url","useSystemFonts","promise","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;;;;+BAeqBA;;;eAAAA;;;qBAd6C;AAGnE,yDAAyD;AACzDC,wBAAmB,CAACC,SAAS,GAAGC,QAAQC,OAAO,CAAC;AAEhD,uCAAuC;AACvC,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,eAAeZ,SAASa,QAAgB,EAAEC,OAA0B;IACzE,IAAIC;IAEJ,IAAI;QACF,uDAAuD;QACvD,MAAMC,cAAcC,IAAAA,gBAAW,EAAC;YAAEC,KAAKL;YAAUM,gBAAgB;QAAK;QACtEJ,MAAM,MAAMC,YAAYI,OAAO;QAC/B,MAAMC,YAAsB,EAAE;QAC9B,IAAIC,QAA4BC;QAEhC,IAAK,IAAIC,UAAU,GAAGA,WAAWT,IAAIU,QAAQ,EAAED,UAAW;YACxD,MAAME,OAAO,MAAMX,IAAIY,OAAO,CAACH;YAC/B,MAAM,EAAEI,KAAK,EAAE,GAAG,MAAMF,KAAKG,cAAc;YAE3C,KAAK,MAAMvB,QAAQsB,MAAO;gBACxB,sBAAsB;gBACtB,IAAI,CAACvB,WAAWC,OAAO;gBAEvB,MAAMwB,WAAWxB,KAAKI,SAAS,CAAC,EAAE;gBAClC,IAAIY,UAAUC,aAAaO,aAAaR,OAAO;oBAC7CD,UAAUU,IAAI,CAACjB,QAAQkB,gBAAgB;gBACzC;gBAEAX,UAAUU,IAAI,CAACzB,KAAKC,GAAG;gBACvBe,QAAQQ;YACV;YACAJ,KAAKO,OAAO;QACd;QAEA,MAAMC,UAAUb,UAAUc,IAAI,CAAC;QAC/B,IAAIxB,mBAAmByB,GAAG,CAACF,QAAQG,UAAU,CAAC,KAAK;YACjD,OAAO;QACT;QACA,OAAOH;IACT,EAAE,OAAOI,GAAG;QACV,IAAIxB,QAAQyB,oBAAoB,EAAE;YAChCC,QAAQC,KAAK,CAAC,sBAAsBH;QACtC;QACA,MAAMA;IACR,SAAU;QACRvB,KAAK2B,UAAUC,MAAM,CAACL,IAAaE,QAAQC,KAAK,CAACH;IACnD;AACF"}
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"}