@sync-in/server 1.9.3 → 1.10.0

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 (349) hide show
  1. package/CHANGELOG.md +41 -4
  2. package/environment/environment.dist.yaml +15 -5
  3. package/package.json +18 -19
  4. package/server/app.bootstrap.js +1 -1
  5. package/server/app.bootstrap.js.map +1 -1
  6. package/server/app.constants.js +3 -2
  7. package/server/app.constants.js.map +1 -1
  8. package/server/applications/files/constants/cache.js +2 -5
  9. package/server/applications/files/constants/cache.js.map +1 -1
  10. package/server/applications/files/constants/files.js +4 -0
  11. package/server/applications/files/constants/files.js.map +1 -1
  12. package/server/applications/files/constants/operations.js +4 -0
  13. package/server/applications/files/constants/operations.js.map +1 -1
  14. package/server/applications/files/constants/routes.js +1 -26
  15. package/server/applications/files/constants/routes.js.map +1 -1
  16. package/server/applications/files/files.config.js +15 -39
  17. package/server/applications/files/files.config.js.map +1 -1
  18. package/server/applications/files/files.controller.js +4 -4
  19. package/server/applications/files/files.controller.js.map +1 -1
  20. package/server/applications/files/files.module.js +12 -9
  21. package/server/applications/files/files.module.js.map +1 -1
  22. package/server/applications/files/interfaces/file-lock.interface.js.map +1 -1
  23. package/server/applications/files/interfaces/file-props.interface.js.map +1 -1
  24. package/server/applications/files/modules/collabora-online/collabora-online-environment.decorator.js +32 -0
  25. package/server/applications/files/modules/collabora-online/collabora-online-environment.decorator.js.map +1 -0
  26. package/server/applications/files/modules/collabora-online/collabora-online-manager.service.js +280 -0
  27. package/server/applications/files/modules/collabora-online/collabora-online-manager.service.js.map +1 -0
  28. package/server/applications/files/modules/collabora-online/collabora-online-manager.service.spec.js +552 -0
  29. package/server/applications/files/modules/collabora-online/collabora-online-manager.service.spec.js.map +1 -0
  30. package/server/applications/files/modules/collabora-online/collabora-online.config.js +40 -0
  31. package/server/applications/files/modules/collabora-online/collabora-online.config.js.map +1 -0
  32. package/server/applications/files/modules/collabora-online/collabora-online.constants.js +110 -0
  33. package/server/applications/files/modules/collabora-online/collabora-online.constants.js.map +1 -0
  34. package/server/applications/files/modules/collabora-online/collabora-online.controller.js +128 -0
  35. package/server/applications/files/modules/collabora-online/collabora-online.controller.js.map +1 -0
  36. package/server/applications/files/modules/collabora-online/collabora-online.controller.spec.js +47 -0
  37. package/server/applications/files/modules/collabora-online/collabora-online.controller.spec.js.map +1 -0
  38. package/server/applications/files/{interfaces/only-office-config.interface.js → modules/collabora-online/collabora-online.dtos.js} +1 -1
  39. package/server/applications/files/modules/collabora-online/collabora-online.dtos.js.map +1 -0
  40. package/server/applications/files/{guards/files-only-office.guard.js → modules/collabora-online/collabora-online.guard.js} +7 -21
  41. package/server/applications/files/modules/collabora-online/collabora-online.guard.js.map +1 -0
  42. package/server/applications/files/modules/collabora-online/collabora-online.guard.spec.js +86 -0
  43. package/server/applications/files/modules/collabora-online/collabora-online.guard.spec.js.map +1 -0
  44. package/server/applications/files/modules/collabora-online/collabora-online.interface.js +10 -0
  45. package/server/applications/files/modules/collabora-online/collabora-online.interface.js.map +1 -0
  46. package/server/applications/files/modules/collabora-online/collabora-online.module.js +41 -0
  47. package/server/applications/files/modules/collabora-online/collabora-online.module.js.map +1 -0
  48. package/server/applications/files/modules/collabora-online/collabora-online.routes.js +35 -0
  49. package/server/applications/files/modules/collabora-online/collabora-online.routes.js.map +1 -0
  50. package/server/applications/files/modules/collabora-online/collabora-online.strategy.js +59 -0
  51. package/server/applications/files/modules/collabora-online/collabora-online.strategy.js.map +1 -0
  52. package/server/applications/files/modules/collabora-online/collabora-online.utils.js +28 -0
  53. package/server/applications/files/modules/collabora-online/collabora-online.utils.js.map +1 -0
  54. package/server/applications/files/{decorators → modules/only-office}/only-office-environment.decorator.js +5 -5
  55. package/server/applications/files/modules/only-office/only-office-environment.decorator.js.map +1 -0
  56. package/server/applications/files/{services/files-only-office-manager.service.js → modules/only-office/only-office-manager.service.js} +101 -97
  57. package/server/applications/files/modules/only-office/only-office-manager.service.js.map +1 -0
  58. package/server/applications/files/modules/only-office/only-office-manager.service.spec.js +477 -0
  59. package/server/applications/files/modules/only-office/only-office-manager.service.spec.js.map +1 -0
  60. package/server/applications/files/modules/only-office/only-office.config.js +51 -0
  61. package/server/applications/files/modules/only-office/only-office.config.js.map +1 -0
  62. package/server/applications/files/modules/only-office/only-office.constants.js +417 -0
  63. package/server/applications/files/modules/only-office/only-office.constants.js.map +1 -0
  64. package/server/applications/files/{files-only-office.controller.js → modules/only-office/only-office.controller.js} +35 -52
  65. package/server/applications/files/modules/only-office/only-office.controller.js.map +1 -0
  66. package/server/applications/files/{files-only-office.controller.spec.js → modules/only-office/only-office.controller.spec.js} +24 -21
  67. package/server/applications/files/modules/only-office/only-office.controller.spec.js.map +1 -0
  68. package/server/applications/files/modules/only-office/only-office.dtos.js +10 -0
  69. package/server/applications/files/modules/only-office/only-office.dtos.js.map +1 -0
  70. package/server/applications/files/modules/only-office/only-office.guard.js +40 -0
  71. package/server/applications/files/modules/only-office/only-office.guard.js.map +1 -0
  72. package/server/applications/files/{guards/files-only-office.guard.spec.js → modules/only-office/only-office.guard.spec.js} +15 -21
  73. package/server/applications/files/modules/only-office/only-office.guard.spec.js.map +1 -0
  74. package/server/applications/files/modules/only-office/only-office.interface.js +10 -0
  75. package/server/applications/files/modules/only-office/only-office.interface.js.map +1 -0
  76. package/server/applications/files/modules/only-office/only-office.module.js +41 -0
  77. package/server/applications/files/modules/only-office/only-office.module.js.map +1 -0
  78. package/server/applications/files/modules/only-office/only-office.routes.js +45 -0
  79. package/server/applications/files/modules/only-office/only-office.routes.js.map +1 -0
  80. package/server/applications/files/{guards/files-only-office.strategy.js → modules/only-office/only-office.strategy.js} +11 -11
  81. package/server/applications/files/modules/only-office/only-office.strategy.js.map +1 -0
  82. package/server/applications/files/services/files-lock-manager.service.js +25 -33
  83. package/server/applications/files/services/files-lock-manager.service.js.map +1 -1
  84. package/server/applications/files/services/files-manager.service.js +17 -16
  85. package/server/applications/files/services/files-manager.service.js.map +1 -1
  86. package/server/applications/files/services/files-methods.service.js +2 -2
  87. package/server/applications/files/services/files-methods.service.js.map +1 -1
  88. package/server/applications/files/services/files-methods.service.spec.js +5 -5
  89. package/server/applications/files/services/files-methods.service.spec.js.map +1 -1
  90. package/server/applications/files/services/files-recents.service.js +4 -0
  91. package/server/applications/files/services/files-recents.service.js.map +1 -1
  92. package/server/applications/files/services/files-scheduler.service.js +24 -5
  93. package/server/applications/files/services/files-scheduler.service.js.map +1 -1
  94. package/server/applications/files/utils/files.js +10 -2
  95. package/server/applications/files/utils/files.js.map +1 -1
  96. package/server/applications/links/constants/routes.js +5 -0
  97. package/server/applications/links/constants/routes.js.map +1 -1
  98. package/server/applications/links/interfaces/link-space.interface.js.map +1 -1
  99. package/server/applications/links/links.controller.js +25 -5
  100. package/server/applications/links/links.controller.js.map +1 -1
  101. package/server/applications/links/services/links-manager.service.js +43 -21
  102. package/server/applications/links/services/links-manager.service.js.map +1 -1
  103. package/server/applications/links/services/links-manager.service.spec.js +4 -3
  104. package/server/applications/links/services/links-manager.service.spec.js.map +1 -1
  105. package/server/applications/links/services/links-queries.service.js +9 -2
  106. package/server/applications/links/services/links-queries.service.js.map +1 -1
  107. package/server/applications/shares/interfaces/share-link.interface.js.map +1 -1
  108. package/server/applications/shares/services/shares-manager.service.js +3 -0
  109. package/server/applications/shares/services/shares-manager.service.js.map +1 -1
  110. package/server/applications/shares/services/shares-manager.service.spec.js +2 -1
  111. package/server/applications/shares/services/shares-manager.service.spec.js.map +1 -1
  112. package/server/applications/shares/services/shares-queries.service.js +1 -0
  113. package/server/applications/shares/services/shares-queries.service.js.map +1 -1
  114. package/server/applications/spaces/constants/spaces.js +2 -2
  115. package/server/applications/spaces/constants/spaces.js.map +1 -1
  116. package/server/applications/spaces/decorators/space-override-permission.decorator.js +18 -0
  117. package/server/applications/spaces/decorators/space-override-permission.decorator.js.map +1 -0
  118. package/server/applications/spaces/guards/space.guard.js +40 -33
  119. package/server/applications/spaces/guards/space.guard.js.map +1 -1
  120. package/server/applications/spaces/guards/space.guard.spec.js +10 -15
  121. package/server/applications/spaces/guards/space.guard.spec.js.map +1 -1
  122. package/server/applications/spaces/services/spaces-scheduler.service.js +9 -1
  123. package/server/applications/spaces/services/spaces-scheduler.service.js.map +1 -1
  124. package/server/applications/webdav/constants/webdav.js +4 -0
  125. package/server/applications/webdav/constants/webdav.js.map +1 -1
  126. package/server/applications/webdav/guards/webdav-protocol.guard.js +9 -8
  127. package/server/applications/webdav/guards/webdav-protocol.guard.js.map +1 -1
  128. package/server/applications/webdav/guards/webdav-protocol.guard.spec.js +1 -1
  129. package/server/applications/webdav/guards/webdav-protocol.guard.spec.js.map +1 -1
  130. package/server/applications/webdav/interfaces/webdav.interface.js.map +1 -1
  131. package/server/applications/webdav/services/webdav-methods.service.js +40 -17
  132. package/server/applications/webdav/services/webdav-methods.service.js.map +1 -1
  133. package/server/applications/webdav/services/webdav-methods.service.spec.js +2157 -1289
  134. package/server/applications/webdav/services/webdav-methods.service.spec.js.map +1 -1
  135. package/server/applications/webdav/utils/webdav.js +8 -4
  136. package/server/applications/webdav/utils/webdav.js.map +1 -1
  137. package/server/applications/webdav/webdav.controller.js +4 -4
  138. package/server/applications/webdav/webdav.controller.js.map +1 -1
  139. package/server/authentication/guards/auth-token-access.guard.js +8 -3
  140. package/server/authentication/guards/auth-token-access.guard.js.map +1 -1
  141. package/server/authentication/services/auth-methods/auth-method-two-fa.service.js +1 -1
  142. package/server/authentication/services/auth-methods/auth-method-two-fa.service.js.map +1 -1
  143. package/server/authentication/services/auth-methods/auth-method-two-fa.service.spec.js +350 -4
  144. package/server/authentication/services/auth-methods/auth-method-two-fa.service.spec.js.map +1 -1
  145. package/server/configuration/config.environment.js +5 -1
  146. package/server/configuration/config.environment.js.map +1 -1
  147. package/server/configuration/config.interfaces.js.map +1 -1
  148. package/static/3rdpartylicenses.txt +507 -507
  149. package/static/assets/pdfjs/build/pdf.mjs +93 -33
  150. package/static/assets/pdfjs/build/pdf.mjs.map +1 -1
  151. package/static/assets/pdfjs/build/pdf.sandbox.mjs +3 -3
  152. package/static/assets/pdfjs/build/pdf.sandbox.mjs.map +1 -1
  153. package/static/assets/pdfjs/build/pdf.worker.mjs +166 -54
  154. package/static/assets/pdfjs/build/pdf.worker.mjs.map +1 -1
  155. package/static/assets/pdfjs/version +1 -1
  156. package/static/assets/pdfjs/web/images/checkmark.svg +5 -0
  157. package/static/assets/pdfjs/web/images/pages_closeButton.svg +3 -0
  158. package/static/assets/pdfjs/web/images/pages_selected.svg +7 -0
  159. package/static/assets/pdfjs/web/images/pages_viewArrow.svg +3 -0
  160. package/static/assets/pdfjs/web/images/pages_viewButton.svg +3 -0
  161. package/static/assets/pdfjs/web/locale/be/viewer.ftl +0 -2
  162. package/static/assets/pdfjs/web/locale/bs/viewer.ftl +0 -5
  163. package/static/assets/pdfjs/web/locale/cs/viewer.ftl +4 -6
  164. package/static/assets/pdfjs/web/locale/cy/viewer.ftl +0 -2
  165. package/static/assets/pdfjs/web/locale/da/viewer.ftl +0 -2
  166. package/static/assets/pdfjs/web/locale/de/viewer.ftl +0 -2
  167. package/static/assets/pdfjs/web/locale/dsb/viewer.ftl +0 -2
  168. package/static/assets/pdfjs/web/locale/el/viewer.ftl +0 -2
  169. package/static/assets/pdfjs/web/locale/en-CA/viewer.ftl +6 -2
  170. package/static/assets/pdfjs/web/locale/en-GB/viewer.ftl +0 -2
  171. package/static/assets/pdfjs/web/locale/en-US/viewer.ftl +82 -17
  172. package/static/assets/pdfjs/web/locale/eo/viewer.ftl +0 -2
  173. package/static/assets/pdfjs/web/locale/es-AR/viewer.ftl +0 -2
  174. package/static/assets/pdfjs/web/locale/es-CL/viewer.ftl +0 -2
  175. package/static/assets/pdfjs/web/locale/es-ES/viewer.ftl +0 -2
  176. package/static/assets/pdfjs/web/locale/es-MX/viewer.ftl +0 -2
  177. package/static/assets/pdfjs/web/locale/eu/viewer.ftl +0 -2
  178. package/static/assets/pdfjs/web/locale/fi/viewer.ftl +0 -2
  179. package/static/assets/pdfjs/web/locale/fr/viewer.ftl +0 -2
  180. package/static/assets/pdfjs/web/locale/fur/viewer.ftl +0 -5
  181. package/static/assets/pdfjs/web/locale/fy-NL/viewer.ftl +3 -5
  182. package/static/assets/pdfjs/web/locale/gn/viewer.ftl +0 -2
  183. package/static/assets/pdfjs/web/locale/he/viewer.ftl +0 -2
  184. package/static/assets/pdfjs/web/locale/hr/viewer.ftl +66 -0
  185. package/static/assets/pdfjs/web/locale/hsb/viewer.ftl +0 -2
  186. package/static/assets/pdfjs/web/locale/hu/viewer.ftl +0 -2
  187. package/static/assets/pdfjs/web/locale/hy-AM/viewer.ftl +3 -8
  188. package/static/assets/pdfjs/web/locale/ia/viewer.ftl +0 -2
  189. package/static/assets/pdfjs/web/locale/id/viewer.ftl +0 -5
  190. package/static/assets/pdfjs/web/locale/is/viewer.ftl +0 -5
  191. package/static/assets/pdfjs/web/locale/it/viewer.ftl +0 -2
  192. package/static/assets/pdfjs/web/locale/ja/viewer.ftl +0 -14
  193. package/static/assets/pdfjs/web/locale/ka/viewer.ftl +4 -6
  194. package/static/assets/pdfjs/web/locale/kab/viewer.ftl +0 -5
  195. package/static/assets/pdfjs/web/locale/kk/viewer.ftl +0 -2
  196. package/static/assets/pdfjs/web/locale/ko/viewer.ftl +0 -2
  197. package/static/assets/pdfjs/web/locale/nb-NO/viewer.ftl +1 -3
  198. package/static/assets/pdfjs/web/locale/nl/viewer.ftl +0 -2
  199. package/static/assets/pdfjs/web/locale/nn-NO/viewer.ftl +4 -2
  200. package/static/assets/pdfjs/web/locale/pa-IN/viewer.ftl +0 -2
  201. package/static/assets/pdfjs/web/locale/pl/viewer.ftl +0 -2
  202. package/static/assets/pdfjs/web/locale/pt-BR/viewer.ftl +0 -2
  203. package/static/assets/pdfjs/web/locale/pt-PT/viewer.ftl +35 -0
  204. package/static/assets/pdfjs/web/locale/rm/viewer.ftl +0 -5
  205. package/static/assets/pdfjs/web/locale/ro/viewer.ftl +4 -6
  206. package/static/assets/pdfjs/web/locale/ru/viewer.ftl +3 -5
  207. package/static/assets/pdfjs/web/locale/sk/viewer.ftl +0 -2
  208. package/static/assets/pdfjs/web/locale/sl/viewer.ftl +0 -2
  209. package/static/assets/pdfjs/web/locale/sq/viewer.ftl +0 -2
  210. package/static/assets/pdfjs/web/locale/sv-SE/viewer.ftl +0 -2
  211. package/static/assets/pdfjs/web/locale/tg/viewer.ftl +0 -2
  212. package/static/assets/pdfjs/web/locale/th/viewer.ftl +2 -2
  213. package/static/assets/pdfjs/web/locale/tr/viewer.ftl +0 -2
  214. package/static/assets/pdfjs/web/locale/vi/viewer.ftl +0 -2
  215. package/static/assets/pdfjs/web/locale/zh-CN/viewer.ftl +0 -2
  216. package/static/assets/pdfjs/web/locale/zh-TW/viewer.ftl +0 -2
  217. package/static/assets/pdfjs/web/viewer.css +1778 -835
  218. package/static/assets/pdfjs/web/viewer.html +167 -86
  219. package/static/assets/pdfjs/web/viewer.mjs +1106 -801
  220. package/static/assets/pdfjs/web/viewer.mjs.map +1 -1
  221. package/static/chunk-27V66YJV.js +2 -0
  222. package/static/{chunk-WJYVS27M.js → chunk-27Z3SYRL.js} +1 -1
  223. package/static/{chunk-NFIES7BC.js → chunk-2RWLNKZH.js} +1 -1
  224. package/static/chunk-2YQ4SX3A.js +13 -0
  225. package/static/{chunk-GENTF6JM.js → chunk-3JYMJQYT.js} +1 -1
  226. package/static/chunk-3QTROEHV.js +1 -0
  227. package/static/{chunk-ZPI7RQ2S.js → chunk-3RPUQ22U.js} +1 -1
  228. package/static/{chunk-R6VB3INJ.js → chunk-3WZ6F3LC.js} +1 -1
  229. package/static/chunk-3ZLBVUCX.js +2 -0
  230. package/static/{chunk-5HCVWZMA.js → chunk-45AZ6ZML.js} +1 -1
  231. package/static/chunk-46TJLPJY.js +1 -0
  232. package/static/chunk-4NIYCYRS.js +2 -0
  233. package/static/{chunk-XXYMVRSH.js → chunk-4TPFERL6.js} +1 -1
  234. package/static/{chunk-CAZSNVMS.js → chunk-5O66CLTD.js} +1 -1
  235. package/static/chunk-6OEOADR6.js +1 -0
  236. package/static/chunk-6WMXMIE4.js +1 -0
  237. package/static/{chunk-NK2NMAJI.js → chunk-7VRYTDX4.js} +1 -1
  238. package/static/{chunk-ASBPYTLT.js → chunk-ARS47O5X.js} +1 -1
  239. package/static/chunk-B6HQYQYG.js +1 -0
  240. package/static/chunk-BCN4T5DO.js +2 -0
  241. package/static/{chunk-PKU4IIIR.js → chunk-CCZWPM7Q.js} +1 -1
  242. package/static/{chunk-QUSS6SUC.js → chunk-CMNMPG6Z.js} +1 -1
  243. package/static/{chunk-GDPJRUVU.js → chunk-CSVPAZHK.js} +1 -1
  244. package/static/{chunk-BJARRIS6.js → chunk-D55YR5X7.js} +4 -4
  245. package/static/{chunk-Z6RJZIDG.js → chunk-D5FQ72R4.js} +1 -1
  246. package/static/{chunk-4DF2SQD4.js → chunk-DGCVA6BM.js} +1 -1
  247. package/static/{chunk-TVJQXN73.js → chunk-DVCN3P7Q.js} +1 -1
  248. package/static/chunk-E32J777S.js +5 -0
  249. package/static/{chunk-5NHB7SV3.js → chunk-FIUF2JM4.js} +1 -1
  250. package/static/{chunk-RJOHDAPM.js → chunk-G3PL6YX3.js} +1 -1
  251. package/static/chunk-G7RZN7HN.js +1 -0
  252. package/static/{chunk-DDRGLHOP.js → chunk-GQHXYX6Z.js} +1 -1
  253. package/static/{chunk-5HYSNQR4.js → chunk-GWRAGN3M.js} +1 -1
  254. package/static/{chunk-ZC5ZDCDC.js → chunk-GXWGB7WO.js} +1 -1
  255. package/static/{chunk-25PWAXTJ.js → chunk-HGODIZTV.js} +1 -1
  256. package/static/{chunk-4KXJ6C4N.js → chunk-HZAB6F4Q.js} +1 -1
  257. package/static/chunk-I3FR3A45.js +1 -0
  258. package/static/{chunk-A6J6SOM6.js → chunk-I5SPA4G2.js} +1 -1
  259. package/static/{chunk-TGHBDJZA.js → chunk-IMFO2MI7.js} +1 -1
  260. package/static/{chunk-CURVLK7L.js → chunk-JNTNMIUH.js} +1 -1
  261. package/static/chunk-JRXG43AA.js +2 -0
  262. package/static/{chunk-XAIOGRBO.js → chunk-KAUCN24H.js} +1 -1
  263. package/static/chunk-KDUAB76O.js +1 -0
  264. package/static/chunk-KPOQLDWF.js +1 -0
  265. package/static/{chunk-HE6EDXWI.js → chunk-KWFELZTM.js} +1 -1
  266. package/static/{chunk-2CAAJBRO.js → chunk-L3BIP4AA.js} +1 -1
  267. package/static/{chunk-U75PLYIJ.js → chunk-LGIVVJDD.js} +1 -1
  268. package/static/{chunk-JEVBUJQ4.js → chunk-LNLBIJZD.js} +1 -1
  269. package/static/chunk-LTJNLOX2.js +1 -0
  270. package/static/{chunk-SDR3UG2F.js → chunk-LZUHREOF.js} +1 -1
  271. package/static/{chunk-VO4WVT6K.js → chunk-NIR4YE2E.js} +1 -1
  272. package/static/{chunk-S6YKBWJE.js → chunk-NJJURHX4.js} +1 -1
  273. package/static/chunk-NNZWSNAW.js +1 -0
  274. package/static/chunk-NWKBB7J4.js +1 -0
  275. package/static/chunk-O3YLAEVE.js +3 -0
  276. package/static/chunk-OUHCDDT6.js +1 -0
  277. package/static/{chunk-ZRBLCAOK.js → chunk-PDG7DOEF.js} +1 -1
  278. package/static/chunk-POUWUMC4.js +1 -0
  279. package/static/{chunk-YTBSB2GE.js → chunk-PPJCVBJH.js} +1 -1
  280. package/static/{chunk-K3MOXDU5.js → chunk-PQZLR4P3.js} +1 -1
  281. package/static/chunk-PVYVY3GD.js +1 -0
  282. package/static/chunk-Q5X5TPAG.js +1 -0
  283. package/static/{chunk-LFAQLJZK.js → chunk-QHJT5H4M.js} +1 -1
  284. package/static/{chunk-A7DSX7VP.js → chunk-R4VMWCM5.js} +1 -1
  285. package/static/{chunk-27XEAHMV.js → chunk-R7PLNX75.js} +1 -1
  286. package/static/chunk-RJULB733.js +1 -0
  287. package/static/{chunk-MBFMTBVJ.js → chunk-RNVPQQKT.js} +5 -5
  288. package/static/chunk-RTNEBRKJ.js +1 -0
  289. package/static/{chunk-FXM7XXWA.js → chunk-S3TTWPQA.js} +1 -1
  290. package/static/{chunk-6VJI4X2A.js → chunk-SDJNZULP.js} +1 -1
  291. package/static/chunk-SNOOCDJD.js +1 -0
  292. package/static/chunk-T42BV6TR.js +1 -0
  293. package/static/{chunk-4OV3SAUS.js → chunk-TNCKNU6I.js} +1 -1
  294. package/static/{chunk-2LHHXDD5.js → chunk-ULSPQ3HP.js} +1 -1
  295. package/static/{chunk-4EUHBTWV.js → chunk-UOK3LKSX.js} +1 -1
  296. package/static/{chunk-7NI353LS.js → chunk-VD5JHSDS.js} +1 -1
  297. package/static/{chunk-YXWF2DGF.js → chunk-XBKCQCBI.js} +1 -1
  298. package/static/{chunk-KBWK65KM.js → chunk-XEWLBWFF.js} +1 -1
  299. package/static/{chunk-FLPZB3OX.js → chunk-XTVNHFKX.js} +1 -1
  300. package/static/chunk-ZCSHU3D7.js +1 -0
  301. package/static/{chunk-FRBTL2ER.js → chunk-ZEJLIGAY.js} +1 -1
  302. package/static/{chunk-7H5O4BLV.js → chunk-ZHOE5VEY.js} +1 -1
  303. package/static/chunk-ZOMRIN3G.js +2 -0
  304. package/static/index.html +2 -2
  305. package/static/main-YKDNJ7LK.js +11 -0
  306. package/static/{styles-S5HVK4H5.css → styles-XLLEY5Y3.css} +1 -1
  307. package/server/applications/files/constants/only-office.js +0 -531
  308. package/server/applications/files/constants/only-office.js.map +0 -1
  309. package/server/applications/files/decorators/only-office-environment.decorator.js.map +0 -1
  310. package/server/applications/files/files-only-office.controller.js.map +0 -1
  311. package/server/applications/files/files-only-office.controller.spec.js.map +0 -1
  312. package/server/applications/files/guards/files-only-office.guard.js.map +0 -1
  313. package/server/applications/files/guards/files-only-office.guard.spec.js.map +0 -1
  314. package/server/applications/files/guards/files-only-office.strategy.js.map +0 -1
  315. package/server/applications/files/interfaces/only-office-config.interface.js.map +0 -1
  316. package/server/applications/files/services/files-only-office-manager.service.js.map +0 -1
  317. package/server/applications/files/services/files-only-office-manager.service.spec.js +0 -58
  318. package/server/applications/files/services/files-only-office-manager.service.spec.js.map +0 -1
  319. package/static/chunk-2XY4PMI5.js +0 -1
  320. package/static/chunk-33WFRCUP.js +0 -1
  321. package/static/chunk-3LVFDMTN.js +0 -1
  322. package/static/chunk-42L6C5MT.js +0 -1
  323. package/static/chunk-5WCQBTXW.js +0 -1
  324. package/static/chunk-A7R246NW.js +0 -1
  325. package/static/chunk-BSB4VROD.js +0 -2
  326. package/static/chunk-DHFQIFOF.js +0 -1
  327. package/static/chunk-DRHPEERW.js +0 -2
  328. package/static/chunk-FCGTI42I.js +0 -1
  329. package/static/chunk-H4RLHI3Y.js +0 -1
  330. package/static/chunk-ITVA26X2.js +0 -2
  331. package/static/chunk-IUJ4IK26.js +0 -1
  332. package/static/chunk-L3PDWJZ3.js +0 -3
  333. package/static/chunk-LBXOAKBD.js +0 -1
  334. package/static/chunk-LZKI5P5T.js +0 -1
  335. package/static/chunk-MYM43ENO.js +0 -1
  336. package/static/chunk-MZBO5PAR.js +0 -1
  337. package/static/chunk-NAH4V2R6.js +0 -2
  338. package/static/chunk-O7UXVNR2.js +0 -1
  339. package/static/chunk-PCFH5HCI.js +0 -2
  340. package/static/chunk-SRBOO7AO.js +0 -1
  341. package/static/chunk-UUX3M6DC.js +0 -1
  342. package/static/chunk-VJ2HWQRJ.js +0 -5
  343. package/static/chunk-VZPCXSRG.js +0 -2
  344. package/static/chunk-W72JYHOH.js +0 -1
  345. package/static/chunk-XHQEF2IX.js +0 -1
  346. package/static/chunk-XKEBQNQJ.js +0 -1
  347. package/static/chunk-ZERBTNFW.js +0 -13
  348. package/static/main-FE6GWZXU.js +0 -11
  349. /package/static/assets/pdfjs/web/images/{toolbarButton-sidebarToggle.svg → toolbarButton-viewsManagerToggle.svg} +0 -0
@@ -0,0 +1,552 @@
1
+ /*
2
+ * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>
3
+ * This file is part of Sync-in | The open source file sync and share solution
4
+ * See the LICENSE file for licensing details
5
+ */ "use strict";
6
+ Object.defineProperty(exports, "__esModule", {
7
+ value: true
8
+ });
9
+ const _common = require("@nestjs/common");
10
+ const _jwt = require("@nestjs/jwt");
11
+ const _testing = require("@nestjs/testing");
12
+ const _promises = /*#__PURE__*/ _interop_require_default(require("node:fs/promises"));
13
+ const _nodestream = require("node:stream");
14
+ const _contextmanagerservice = require("../../../../infrastructure/context/services/context-manager.service");
15
+ const _spaces = require("../../../spaces/constants/spaces");
16
+ const _webdav = require("../../../webdav/constants/webdav");
17
+ const _operations = require("../../constants/operations");
18
+ const _filelockerror = require("../../models/file-lock-error");
19
+ const _fileslockmanagerservice = require("../../services/files-lock-manager.service");
20
+ const _files = /*#__PURE__*/ _interop_require_wildcard(require("../../utils/files"));
21
+ const _collaboraonlinemanagerservice = require("./collabora-online-manager.service");
22
+ const _collaboraonlineconstants = require("./collabora-online.constants");
23
+ function _interop_require_default(obj) {
24
+ return obj && obj.__esModule ? obj : {
25
+ default: obj
26
+ };
27
+ }
28
+ function _getRequireWildcardCache(nodeInterop) {
29
+ if (typeof WeakMap !== "function") return null;
30
+ var cacheBabelInterop = new WeakMap();
31
+ var cacheNodeInterop = new WeakMap();
32
+ return (_getRequireWildcardCache = function(nodeInterop) {
33
+ return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
34
+ })(nodeInterop);
35
+ }
36
+ function _interop_require_wildcard(obj, nodeInterop) {
37
+ if (!nodeInterop && obj && obj.__esModule) {
38
+ return obj;
39
+ }
40
+ if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
41
+ return {
42
+ default: obj
43
+ };
44
+ }
45
+ var cache = _getRequireWildcardCache(nodeInterop);
46
+ if (cache && cache.has(obj)) {
47
+ return cache.get(obj);
48
+ }
49
+ var newObj = {
50
+ __proto__: null
51
+ };
52
+ var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
53
+ for(var key in obj){
54
+ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
55
+ var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
56
+ if (desc && (desc.get || desc.set)) {
57
+ Object.defineProperty(newObj, key, desc);
58
+ } else {
59
+ newObj[key] = obj[key];
60
+ }
61
+ }
62
+ }
63
+ newObj.default = obj;
64
+ if (cache) {
65
+ cache.set(obj, newObj);
66
+ }
67
+ return newObj;
68
+ }
69
+ jest.mock('../../utils/files');
70
+ jest.mock('node:fs/promises');
71
+ jest.mock('../../../users/utils/avatar');
72
+ describe(_collaboraonlinemanagerservice.CollaboraOnlineManager.name, ()=>{
73
+ let service;
74
+ let filesLockManager;
75
+ const mockUser = {
76
+ id: 1,
77
+ login: 'testuser',
78
+ email: 'test@example.com',
79
+ fullName: 'Test User',
80
+ language: 'en',
81
+ role: 1,
82
+ applications: []
83
+ };
84
+ const mockSpace = {
85
+ realPath: '/path/to/document.docx',
86
+ url: '/files/document.docx',
87
+ envPermissions: '',
88
+ dbFile: {
89
+ id: 1,
90
+ path: '/document.docx',
91
+ ownerId: 1,
92
+ name: 'document.docx'
93
+ }
94
+ };
95
+ beforeEach(async ()=>{
96
+ const module = await _testing.Test.createTestingModule({
97
+ providers: [
98
+ _collaboraonlinemanagerservice.CollaboraOnlineManager,
99
+ {
100
+ provide: _contextmanagerservice.ContextManager,
101
+ useValue: {
102
+ headerOriginUrl: jest.fn().mockReturnValue('https://domain.com')
103
+ }
104
+ },
105
+ {
106
+ provide: _jwt.JwtService,
107
+ useValue: {
108
+ signAsync: jest.fn().mockResolvedValue('mock-jwt-token')
109
+ }
110
+ },
111
+ {
112
+ provide: _fileslockmanagerservice.FilesLockManager,
113
+ useValue: {
114
+ checkConflicts: jest.fn(),
115
+ convertLockToFileLockProps: jest.fn(),
116
+ create: jest.fn(),
117
+ removeLock: jest.fn(),
118
+ getLocksByPath: jest.fn(),
119
+ isLockedWithToken: jest.fn(),
120
+ refreshLockTimeout: jest.fn()
121
+ }
122
+ }
123
+ ]
124
+ }).compile();
125
+ module.useLogger([
126
+ 'fatal'
127
+ ]);
128
+ service = module.get(_collaboraonlinemanagerservice.CollaboraOnlineManager);
129
+ filesLockManager = module.get(_fileslockmanagerservice.FilesLockManager);
130
+ });
131
+ afterEach(()=>{
132
+ jest.clearAllMocks();
133
+ });
134
+ it('should be defined', ()=>{
135
+ expect(service).toBeDefined();
136
+ });
137
+ describe('getSettings', ()=>{
138
+ beforeEach(()=>{
139
+ jest.spyOn(_files, 'isPathExists').mockResolvedValue(true);
140
+ jest.spyOn(_files, 'isPathIsDir').mockResolvedValue(false);
141
+ jest.spyOn(_files, 'genUniqHashFromFileDBProps').mockReturnValue('file-hash-123');
142
+ });
143
+ it('should return settings with edit mode when user has modify permissions and no lock conflicts', async ()=>{
144
+ const spaceWithPermissions = {
145
+ ...mockSpace,
146
+ envPermissions: _spaces.SPACE_OPERATION.MODIFY
147
+ };
148
+ jest.spyOn(filesLockManager, 'checkConflicts').mockResolvedValue(undefined);
149
+ const result = await service.getSettings(mockUser, spaceWithPermissions);
150
+ expect(result).toEqual({
151
+ documentServerUrl: expect.stringContaining('file-hash-123'),
152
+ mode: _operations.FILE_MODE.EDIT,
153
+ hasLock: false
154
+ });
155
+ expect(filesLockManager.checkConflicts).toHaveBeenCalledWith(mockSpace.dbFile, _webdav.DEPTH.RESOURCE, expect.objectContaining({
156
+ userId: mockUser.id,
157
+ app: _collaboraonlineconstants.COLLABORA_APP_LOCK,
158
+ lockScope: _webdav.LOCK_SCOPE.SHARED
159
+ }));
160
+ });
161
+ it('should return settings with view mode when lock conflict exists', async ()=>{
162
+ const spaceWithPermissions = {
163
+ ...mockSpace,
164
+ envPermissions: _spaces.SPACE_OPERATION.MODIFY
165
+ };
166
+ const mockLock = {
167
+ userId: 2,
168
+ options: {
169
+ lockToken: 'token-123'
170
+ }
171
+ };
172
+ const mockFileLockProps = {
173
+ owner: {
174
+ id: 2,
175
+ login: 'otheruser',
176
+ email: 'other@example.com',
177
+ fullName: 'Other User'
178
+ },
179
+ app: _collaboraonlineconstants.COLLABORA_APP_LOCK,
180
+ isExclusive: false
181
+ };
182
+ jest.spyOn(filesLockManager, 'checkConflicts').mockRejectedValue(new _filelockerror.LockConflict(mockLock, 'conflict'));
183
+ jest.spyOn(filesLockManager, 'convertLockToFileLockProps').mockReturnValue(mockFileLockProps);
184
+ const result = await service.getSettings(mockUser, spaceWithPermissions);
185
+ expect(result.mode).toBe(_operations.FILE_MODE.VIEW);
186
+ expect(result.hasLock).toEqual(mockFileLockProps);
187
+ });
188
+ it('should return view mode when user does not have modify permissions', async ()=>{
189
+ const spaceWithoutPermissions = {
190
+ ...mockSpace,
191
+ envPermissions: ''
192
+ };
193
+ const result = await service.getSettings(mockUser, spaceWithoutPermissions);
194
+ expect(result.mode).toBe(_operations.FILE_MODE.VIEW);
195
+ expect(filesLockManager.checkConflicts).not.toHaveBeenCalled();
196
+ });
197
+ it('should throw error when document extension is not supported', async ()=>{
198
+ const spaceWithUnsupportedFile = {
199
+ ...mockSpace,
200
+ realPath: '/path/to/document.xyz'
201
+ };
202
+ jest.spyOn(_files, 'isPathExists').mockResolvedValue(true);
203
+ jest.spyOn(_files, 'isPathIsDir').mockResolvedValue(false);
204
+ await expect(service.getSettings(mockUser, spaceWithUnsupportedFile)).rejects.toThrow(new _common.HttpException('Document not supported', _common.HttpStatus.BAD_REQUEST));
205
+ });
206
+ it('should throw error when document does not exist', async ()=>{
207
+ jest.spyOn(_files, 'isPathExists').mockResolvedValue(false);
208
+ await expect(service.getSettings(mockUser, mockSpace)).rejects.toThrow(new _common.HttpException('Document not found', _common.HttpStatus.NOT_FOUND));
209
+ });
210
+ it('should throw error when path is a directory', async ()=>{
211
+ jest.spyOn(_files, 'isPathExists').mockResolvedValue(true);
212
+ jest.spyOn(_files, 'isPathIsDir').mockResolvedValue(true);
213
+ await expect(service.getSettings(mockUser, mockSpace)).rejects.toThrow(new _common.HttpException('Document must be a file', _common.HttpStatus.BAD_REQUEST));
214
+ });
215
+ });
216
+ describe('checkFileInfo', ()=>{
217
+ it('should return file information', async ()=>{
218
+ const mockStats = {
219
+ size: 1024,
220
+ mtime: new Date('2024-01-01T10:00:00Z')
221
+ };
222
+ const mockRequest = {
223
+ user: mockUser,
224
+ space: {
225
+ ...mockSpace,
226
+ envPermissions: _spaces.SPACE_OPERATION.MODIFY
227
+ }
228
+ };
229
+ jest.spyOn(_promises.default, 'stat').mockResolvedValue(mockStats);
230
+ jest.spyOn(_files, 'fileName').mockReturnValue('document.docx');
231
+ jest.spyOn(_files, 'genEtag').mockReturnValue('etag-123');
232
+ const { getAvatarBase64 } = await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("../../../users/utils/avatar")));
233
+ getAvatarBase64.mockResolvedValue('base64-avatar');
234
+ const result = await service.checkFileInfo(mockRequest);
235
+ expect(result).toEqual({
236
+ BaseFileName: 'document.docx',
237
+ Version: 'etag-123',
238
+ OwnerId: '1',
239
+ Size: 1024,
240
+ LastModifiedTime: '2024-01-01T10:00:00.000Z',
241
+ UserId: '1',
242
+ UserFriendlyName: 'Test User (test@example.com)',
243
+ ReadOnly: false,
244
+ UserExtraInfo: {
245
+ avatar: 'base64-avatar'
246
+ },
247
+ UserCanNotWriteRelative: true,
248
+ UserCanWrite: true,
249
+ UserCanRename: false,
250
+ SupportsUpdate: true,
251
+ SupportsRename: false,
252
+ SupportsExport: true,
253
+ SupportsCoauth: true,
254
+ SupportsLocks: true,
255
+ SupportsGetLock: true
256
+ });
257
+ });
258
+ it('should set UserCanWrite to false when user does not have modify permissions', async ()=>{
259
+ const mockStats = {
260
+ size: 1024,
261
+ mtime: new Date('2024-01-01T10:00:00Z')
262
+ };
263
+ const mockRequest = {
264
+ user: mockUser,
265
+ space: {
266
+ ...mockSpace,
267
+ envPermissions: ''
268
+ }
269
+ };
270
+ jest.spyOn(_promises.default, 'stat').mockResolvedValue(mockStats);
271
+ jest.spyOn(_files, 'fileName').mockReturnValue('document.docx');
272
+ jest.spyOn(_files, 'genEtag').mockReturnValue('etag-123');
273
+ const { getAvatarBase64 } = await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("../../../users/utils/avatar")));
274
+ getAvatarBase64.mockResolvedValue('base64-avatar');
275
+ const result = await service.checkFileInfo(mockRequest);
276
+ expect(result.UserCanWrite).toBe(false);
277
+ });
278
+ });
279
+ describe('saveDocument', ()=>{
280
+ beforeEach(()=>{
281
+ jest.spyOn(_files, 'isPathExists').mockResolvedValue(true);
282
+ jest.spyOn(_files, 'isPathIsDir').mockResolvedValue(false);
283
+ jest.spyOn(_files, 'fileName').mockReturnValue('document.docx');
284
+ jest.spyOn(_files, 'uniqueFilePathFromDir').mockResolvedValue('/tmp/document-unique.docx');
285
+ jest.spyOn(_files, 'writeFromStream').mockResolvedValue(undefined);
286
+ jest.spyOn(_files, 'copyFileContent').mockResolvedValue(undefined);
287
+ jest.spyOn(_files, 'removeFiles').mockResolvedValue(undefined);
288
+ });
289
+ it('should save document successfully', async ()=>{
290
+ const mockStats = {
291
+ mtime: new Date('2024-01-01T11:00:00Z')
292
+ };
293
+ const mockRequest = {
294
+ user: mockUser,
295
+ space: mockSpace,
296
+ headers: {
297
+ 'content-length': '1024'
298
+ },
299
+ raw: new _nodestream.Readable()
300
+ };
301
+ jest.spyOn(_promises.default, 'stat').mockResolvedValue(mockStats);
302
+ jest.spyOn(_files, 'fileSize').mockResolvedValue(1024);
303
+ const result = await service.saveDocument(mockRequest);
304
+ expect(result).toEqual({
305
+ LastModifiedTime: '2024-01-01T11:00:00.000Z'
306
+ });
307
+ expect(_files.writeFromStream).toHaveBeenCalledWith('/tmp/document-unique.docx', mockRequest.raw);
308
+ expect(_files.copyFileContent).toHaveBeenCalledWith('/tmp/document-unique.docx', mockSpace.realPath);
309
+ expect(_files.removeFiles).toHaveBeenCalledWith('/tmp/document-unique.docx');
310
+ });
311
+ it('should throw error when document size mismatch', async ()=>{
312
+ const mockRequest = {
313
+ user: mockUser,
314
+ space: mockSpace,
315
+ headers: {
316
+ 'content-length': '1024',
317
+ [_collaboraonlineconstants.COLLABORA_HEADERS.Timestamp]: '2024-01-01T10:00:00.000Z'
318
+ },
319
+ raw: new _nodestream.Readable()
320
+ };
321
+ jest.spyOn(_files, 'fileSize').mockResolvedValue(512);
322
+ jest.spyOn(_promises.default, 'stat').mockResolvedValue({
323
+ mtime: new Date('2024-01-01T10:00:00.000Z')
324
+ });
325
+ await expect(service.saveDocument(mockRequest)).rejects.toThrow(new _common.HttpException('Size Mismatch', _common.HttpStatus.BAD_REQUEST));
326
+ });
327
+ it('should throw error when timestamp mismatch', async ()=>{
328
+ const mockRequest = {
329
+ user: mockUser,
330
+ space: mockSpace,
331
+ headers: {
332
+ 'content-length': '1024',
333
+ [_collaboraonlineconstants.COLLABORA_HEADERS.Timestamp]: '2024-01-01T10:00:00.000Z'
334
+ },
335
+ raw: new _nodestream.Readable()
336
+ };
337
+ jest.spyOn(_promises.default, 'stat').mockResolvedValue({
338
+ mtime: new Date('2024-01-01T11:00:00.000Z')
339
+ });
340
+ await expect(service.saveDocument(mockRequest)).rejects.toThrow(new _common.HttpException({
341
+ LOOLStatusCode: 1010
342
+ }, _common.HttpStatus.CONFLICT));
343
+ });
344
+ it('should throw error when document does not exist', async ()=>{
345
+ const mockRequest = {
346
+ user: mockUser,
347
+ space: mockSpace,
348
+ headers: {
349
+ 'content-length': '1024'
350
+ },
351
+ raw: new _nodestream.Readable()
352
+ };
353
+ jest.spyOn(_files, 'isPathExists').mockResolvedValue(false);
354
+ await expect(service.saveDocument(mockRequest)).rejects.toThrow(new _common.HttpException('Document not found', _common.HttpStatus.NOT_FOUND));
355
+ });
356
+ });
357
+ describe('manageLock', ()=>{
358
+ const mockReply = {
359
+ header: jest.fn().mockReturnThis()
360
+ };
361
+ describe('LOCK action', ()=>{
362
+ it('should create a new lock', async ()=>{
363
+ const mockRequest = {
364
+ user: mockUser,
365
+ space: mockSpace,
366
+ headers: {
367
+ [_collaboraonlineconstants.COLLABORA_HEADERS.Action]: _collaboraonlineconstants.COLLABORA_LOCK_ACTION.LOCK,
368
+ [_collaboraonlineconstants.COLLABORA_HEADERS.LockToken]: 'new-lock-token'
369
+ }
370
+ };
371
+ jest.spyOn(filesLockManager, 'isLockedWithToken').mockResolvedValue(null);
372
+ jest.spyOn(filesLockManager, 'create').mockResolvedValue([
373
+ true,
374
+ {}
375
+ ]);
376
+ await service.manageLock(mockRequest, mockReply);
377
+ expect(filesLockManager.create).toHaveBeenCalledWith(mockUser, mockSpace.dbFile, _collaboraonlineconstants.COLLABORA_APP_LOCK, _webdav.DEPTH.RESOURCE, expect.objectContaining({
378
+ lockToken: 'new-lock-token',
379
+ lockScope: _webdav.LOCK_SCOPE.SHARED
380
+ }), expect.any(Number));
381
+ });
382
+ it('should refresh existing lock', async ()=>{
383
+ const mockRequest = {
384
+ user: mockUser,
385
+ space: mockSpace,
386
+ headers: {
387
+ [_collaboraonlineconstants.COLLABORA_HEADERS.Action]: _collaboraonlineconstants.COLLABORA_LOCK_ACTION.LOCK,
388
+ [_collaboraonlineconstants.COLLABORA_HEADERS.LockToken]: 'existing-lock-token'
389
+ }
390
+ };
391
+ const existingLock = {
392
+ key: 'lock-key',
393
+ options: {
394
+ lockToken: 'existing-lock-token'
395
+ }
396
+ };
397
+ jest.spyOn(filesLockManager, 'isLockedWithToken').mockResolvedValue(existingLock);
398
+ jest.spyOn(filesLockManager, 'refreshLockTimeout').mockResolvedValue(undefined);
399
+ await service.manageLock(mockRequest, mockReply);
400
+ expect(filesLockManager.refreshLockTimeout).toHaveBeenCalledWith(existingLock, expect.any(Number));
401
+ expect(filesLockManager.create).not.toHaveBeenCalled();
402
+ });
403
+ it('should throw conflict when lock creation fails', async ()=>{
404
+ const mockRequest = {
405
+ user: mockUser,
406
+ space: mockSpace,
407
+ headers: {
408
+ [_collaboraonlineconstants.COLLABORA_HEADERS.Action]: _collaboraonlineconstants.COLLABORA_LOCK_ACTION.LOCK,
409
+ [_collaboraonlineconstants.COLLABORA_HEADERS.LockToken]: 'new-lock-token'
410
+ }
411
+ };
412
+ const conflictingLock = {
413
+ options: {
414
+ lockToken: 'conflicting-token'
415
+ }
416
+ };
417
+ jest.spyOn(filesLockManager, 'isLockedWithToken').mockResolvedValue(null);
418
+ jest.spyOn(filesLockManager, 'create').mockResolvedValue([
419
+ false,
420
+ conflictingLock
421
+ ]);
422
+ await expect(service.manageLock(mockRequest, mockReply)).rejects.toThrow(new _common.HttpException('The file is locked', _common.HttpStatus.CONFLICT));
423
+ expect(mockReply.header).toHaveBeenCalledWith(_collaboraonlineconstants.COLLABORA_HEADERS.LockToken, 'conflicting-token');
424
+ });
425
+ it('should throw error when lock token is missing', async ()=>{
426
+ const mockRequest = {
427
+ user: mockUser,
428
+ space: mockSpace,
429
+ headers: {
430
+ [_collaboraonlineconstants.COLLABORA_HEADERS.Action]: _collaboraonlineconstants.COLLABORA_LOCK_ACTION.LOCK
431
+ }
432
+ };
433
+ await expect(service.manageLock(mockRequest, mockReply)).rejects.toThrow(new _common.HttpException('Lock token is required', _common.HttpStatus.CONFLICT));
434
+ });
435
+ });
436
+ describe('UNLOCK action', ()=>{
437
+ it('should remove existing lock', async ()=>{
438
+ const mockRequest = {
439
+ user: mockUser,
440
+ space: mockSpace,
441
+ headers: {
442
+ [_collaboraonlineconstants.COLLABORA_HEADERS.Action]: _collaboraonlineconstants.COLLABORA_LOCK_ACTION.UNLOCK,
443
+ [_collaboraonlineconstants.COLLABORA_HEADERS.LockToken]: 'lock-token-to-remove'
444
+ }
445
+ };
446
+ const existingLock = {
447
+ key: 'lock-key',
448
+ options: {
449
+ lockToken: 'lock-token-to-remove'
450
+ }
451
+ };
452
+ jest.spyOn(filesLockManager, 'isLockedWithToken').mockResolvedValue(existingLock);
453
+ jest.spyOn(filesLockManager, 'removeLock').mockResolvedValue(undefined);
454
+ await service.manageLock(mockRequest, mockReply);
455
+ expect(filesLockManager.removeLock).toHaveBeenCalledWith('lock-key');
456
+ });
457
+ it('should throw error when lock does not exist', async ()=>{
458
+ const mockRequest = {
459
+ user: mockUser,
460
+ space: mockSpace,
461
+ headers: {
462
+ [_collaboraonlineconstants.COLLABORA_HEADERS.Action]: _collaboraonlineconstants.COLLABORA_LOCK_ACTION.UNLOCK,
463
+ [_collaboraonlineconstants.COLLABORA_HEADERS.LockToken]: 'non-existent-token'
464
+ }
465
+ };
466
+ jest.spyOn(filesLockManager, 'isLockedWithToken').mockResolvedValue(null);
467
+ await expect(service.manageLock(mockRequest, mockReply)).rejects.toThrow(new _common.HttpException('Lock not found', _common.HttpStatus.CONFLICT));
468
+ });
469
+ });
470
+ describe('GET_LOCK action', ()=>{
471
+ it('should return lock token when lock exists', async ()=>{
472
+ const mockRequest = {
473
+ user: mockUser,
474
+ space: mockSpace,
475
+ headers: {
476
+ [_collaboraonlineconstants.COLLABORA_HEADERS.Action]: _collaboraonlineconstants.COLLABORA_LOCK_ACTION.GET_LOCK
477
+ }
478
+ };
479
+ const existingLock = {
480
+ options: {
481
+ lockToken: 'existing-lock-token'
482
+ }
483
+ };
484
+ jest.spyOn(filesLockManager, 'getLocksByPath').mockResolvedValue([
485
+ existingLock
486
+ ]);
487
+ await service.manageLock(mockRequest, mockReply);
488
+ expect(mockReply.header).toHaveBeenCalledWith(_collaboraonlineconstants.COLLABORA_HEADERS.LockToken, 'existing-lock-token');
489
+ });
490
+ it('should not set header when no lock exists', async ()=>{
491
+ const mockRequest = {
492
+ user: mockUser,
493
+ space: mockSpace,
494
+ headers: {
495
+ [_collaboraonlineconstants.COLLABORA_HEADERS.Action]: _collaboraonlineconstants.COLLABORA_LOCK_ACTION.GET_LOCK
496
+ }
497
+ };
498
+ jest.spyOn(filesLockManager, 'getLocksByPath').mockResolvedValue([]);
499
+ await service.manageLock(mockRequest, mockReply);
500
+ expect(mockReply.header).not.toHaveBeenCalled();
501
+ });
502
+ });
503
+ describe('REFRESH_LOCK action', ()=>{
504
+ it('should refresh existing lock', async ()=>{
505
+ const mockRequest = {
506
+ user: mockUser,
507
+ space: mockSpace,
508
+ headers: {
509
+ [_collaboraonlineconstants.COLLABORA_HEADERS.Action]: _collaboraonlineconstants.COLLABORA_LOCK_ACTION.REFRESH_LOCK,
510
+ [_collaboraonlineconstants.COLLABORA_HEADERS.LockToken]: 'lock-token-to-refresh'
511
+ }
512
+ };
513
+ const existingLock = {
514
+ key: 'lock-key',
515
+ options: {
516
+ lockToken: 'lock-token-to-refresh'
517
+ }
518
+ };
519
+ jest.spyOn(filesLockManager, 'isLockedWithToken').mockResolvedValue(existingLock);
520
+ jest.spyOn(filesLockManager, 'refreshLockTimeout').mockResolvedValue(undefined);
521
+ await service.manageLock(mockRequest, mockReply);
522
+ expect(filesLockManager.refreshLockTimeout).toHaveBeenCalledWith(existingLock, expect.any(Number));
523
+ });
524
+ it('should throw error when lock does not exist', async ()=>{
525
+ const mockRequest = {
526
+ user: mockUser,
527
+ space: mockSpace,
528
+ headers: {
529
+ [_collaboraonlineconstants.COLLABORA_HEADERS.Action]: _collaboraonlineconstants.COLLABORA_LOCK_ACTION.REFRESH_LOCK,
530
+ [_collaboraonlineconstants.COLLABORA_HEADERS.LockToken]: 'non-existent-token'
531
+ }
532
+ };
533
+ jest.spyOn(filesLockManager, 'isLockedWithToken').mockResolvedValue(null);
534
+ await expect(service.manageLock(mockRequest, mockReply)).rejects.toThrow(new _common.HttpException('Lock not found', _common.HttpStatus.CONFLICT));
535
+ });
536
+ });
537
+ describe('Unknown action', ()=>{
538
+ it('should throw error for unknown lock action', async ()=>{
539
+ const mockRequest = {
540
+ user: mockUser,
541
+ space: mockSpace,
542
+ headers: {
543
+ [_collaboraonlineconstants.COLLABORA_HEADERS.Action]: 'UNKNOWN_ACTION'
544
+ }
545
+ };
546
+ await expect(service.manageLock(mockRequest, mockReply)).rejects.toThrow(new _common.HttpException('Unknown lock action', _common.HttpStatus.BAD_REQUEST));
547
+ });
548
+ });
549
+ });
550
+ });
551
+
552
+ //# sourceMappingURL=collabora-online-manager.service.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../backend/src/applications/files/modules/collabora-online/collabora-online-manager.service.spec.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 } from '@nestjs/common'\nimport { JwtService } from '@nestjs/jwt'\nimport { Test, TestingModule } from '@nestjs/testing'\nimport { FastifyReply } from 'fastify'\nimport fs from 'node:fs/promises'\nimport { Readable } from 'node:stream'\nimport { ContextManager } from '../../../../infrastructure/context/services/context-manager.service'\nimport { SPACE_OPERATION } from '../../../spaces/constants/spaces'\nimport type { SpaceEnv } from '../../../spaces/models/space-env.model'\nimport type { UserModel } from '../../../users/models/user.model'\nimport { DEPTH, LOCK_SCOPE } from '../../../webdav/constants/webdav'\nimport { FILE_MODE } from '../../constants/operations'\nimport { FileLockProps } from '../../interfaces/file-props.interface'\nimport { LockConflict } from '../../models/file-lock-error'\nimport { FilesLockManager } from '../../services/files-lock-manager.service'\nimport * as filesUtils from '../../utils/files'\nimport { CollaboraOnlineManager } from './collabora-online-manager.service'\nimport { COLLABORA_APP_LOCK, COLLABORA_HEADERS, COLLABORA_LOCK_ACTION } from './collabora-online.constants'\nimport type { FastifyCollaboraOnlineSpaceRequest } from './collabora-online.interface'\n\njest.mock('../../utils/files')\njest.mock('node:fs/promises')\njest.mock('../../../users/utils/avatar')\n\ndescribe(CollaboraOnlineManager.name, () => {\n let service: CollaboraOnlineManager\n let filesLockManager: FilesLockManager\n\n const mockUser: UserModel = {\n id: 1,\n login: 'testuser',\n email: 'test@example.com',\n fullName: 'Test User',\n language: 'en',\n role: 1,\n applications: []\n } as UserModel\n\n const mockSpace = {\n realPath: '/path/to/document.docx',\n url: '/files/document.docx',\n envPermissions: '',\n dbFile: {\n id: 1,\n path: '/document.docx',\n ownerId: 1,\n name: 'document.docx'\n }\n } as unknown as SpaceEnv\n\n beforeEach(async () => {\n const module: TestingModule = await Test.createTestingModule({\n providers: [\n CollaboraOnlineManager,\n {\n provide: ContextManager,\n useValue: {\n headerOriginUrl: jest.fn().mockReturnValue('https://domain.com')\n }\n },\n {\n provide: JwtService,\n useValue: {\n signAsync: jest.fn().mockResolvedValue('mock-jwt-token')\n }\n },\n {\n provide: FilesLockManager,\n useValue: {\n checkConflicts: jest.fn(),\n convertLockToFileLockProps: jest.fn(),\n create: jest.fn(),\n removeLock: jest.fn(),\n getLocksByPath: jest.fn(),\n isLockedWithToken: jest.fn(),\n refreshLockTimeout: jest.fn()\n }\n }\n ]\n }).compile()\n\n module.useLogger(['fatal'])\n service = module.get<CollaboraOnlineManager>(CollaboraOnlineManager)\n filesLockManager = module.get<FilesLockManager>(FilesLockManager)\n })\n\n afterEach(() => {\n jest.clearAllMocks()\n })\n\n it('should be defined', () => {\n expect(service).toBeDefined()\n })\n\n describe('getSettings', () => {\n beforeEach(() => {\n jest.spyOn(filesUtils, 'isPathExists').mockResolvedValue(true)\n jest.spyOn(filesUtils, 'isPathIsDir').mockResolvedValue(false)\n jest.spyOn(filesUtils, 'genUniqHashFromFileDBProps').mockReturnValue('file-hash-123')\n })\n\n it('should return settings with edit mode when user has modify permissions and no lock conflicts', async () => {\n const spaceWithPermissions = {\n ...mockSpace,\n envPermissions: SPACE_OPERATION.MODIFY\n } as unknown as SpaceEnv\n\n jest.spyOn(filesLockManager, 'checkConflicts').mockResolvedValue(undefined)\n\n const result = await service.getSettings(mockUser, spaceWithPermissions)\n\n expect(result).toEqual({\n documentServerUrl: expect.stringContaining('file-hash-123'),\n mode: FILE_MODE.EDIT,\n hasLock: false\n })\n expect(filesLockManager.checkConflicts).toHaveBeenCalledWith(\n mockSpace.dbFile,\n DEPTH.RESOURCE,\n expect.objectContaining({\n userId: mockUser.id,\n app: COLLABORA_APP_LOCK,\n lockScope: LOCK_SCOPE.SHARED\n })\n )\n })\n\n it('should return settings with view mode when lock conflict exists', async () => {\n const spaceWithPermissions = {\n ...mockSpace,\n envPermissions: SPACE_OPERATION.MODIFY\n } as unknown as SpaceEnv\n\n const mockLock = { userId: 2, options: { lockToken: 'token-123' } }\n const mockFileLockProps: FileLockProps = {\n owner: {\n id: 2,\n login: 'otheruser',\n email: 'other@example.com',\n fullName: 'Other User'\n },\n app: COLLABORA_APP_LOCK,\n isExclusive: false\n }\n\n jest.spyOn(filesLockManager, 'checkConflicts').mockRejectedValue(new LockConflict(mockLock as any, 'conflict'))\n jest.spyOn(filesLockManager, 'convertLockToFileLockProps').mockReturnValue(mockFileLockProps)\n\n const result = await service.getSettings(mockUser, spaceWithPermissions)\n\n expect(result.mode).toBe(FILE_MODE.VIEW)\n expect(result.hasLock).toEqual(mockFileLockProps)\n })\n\n it('should return view mode when user does not have modify permissions', async () => {\n const spaceWithoutPermissions = {\n ...mockSpace,\n envPermissions: ''\n } as unknown as SpaceEnv\n\n const result = await service.getSettings(mockUser, spaceWithoutPermissions)\n\n expect(result.mode).toBe(FILE_MODE.VIEW)\n expect(filesLockManager.checkConflicts).not.toHaveBeenCalled()\n })\n\n it('should throw error when document extension is not supported', async () => {\n const spaceWithUnsupportedFile = {\n ...mockSpace,\n realPath: '/path/to/document.xyz'\n } as unknown as SpaceEnv\n\n jest.spyOn(filesUtils, 'isPathExists').mockResolvedValue(true)\n jest.spyOn(filesUtils, 'isPathIsDir').mockResolvedValue(false)\n\n await expect(service.getSettings(mockUser, spaceWithUnsupportedFile)).rejects.toThrow(\n new HttpException('Document not supported', HttpStatus.BAD_REQUEST)\n )\n })\n\n it('should throw error when document does not exist', async () => {\n jest.spyOn(filesUtils, 'isPathExists').mockResolvedValue(false)\n\n await expect(service.getSettings(mockUser, mockSpace)).rejects.toThrow(new HttpException('Document not found', HttpStatus.NOT_FOUND))\n })\n\n it('should throw error when path is a directory', async () => {\n jest.spyOn(filesUtils, 'isPathExists').mockResolvedValue(true)\n jest.spyOn(filesUtils, 'isPathIsDir').mockResolvedValue(true)\n\n await expect(service.getSettings(mockUser, mockSpace)).rejects.toThrow(new HttpException('Document must be a file', HttpStatus.BAD_REQUEST))\n })\n })\n\n describe('checkFileInfo', () => {\n it('should return file information', async () => {\n const mockStats = {\n size: 1024,\n mtime: new Date('2024-01-01T10:00:00Z')\n }\n\n const mockRequest = {\n user: mockUser,\n space: {\n ...mockSpace,\n envPermissions: SPACE_OPERATION.MODIFY\n }\n } as unknown as FastifyCollaboraOnlineSpaceRequest\n\n jest.spyOn(fs, 'stat').mockResolvedValue(mockStats as any)\n jest.spyOn(filesUtils, 'fileName').mockReturnValue('document.docx')\n jest.spyOn(filesUtils, 'genEtag').mockReturnValue('etag-123')\n const { getAvatarBase64 } = await import('../../../users/utils/avatar')\n ;(getAvatarBase64 as jest.Mock).mockResolvedValue('base64-avatar')\n\n const result = await service.checkFileInfo(mockRequest)\n\n expect(result).toEqual({\n BaseFileName: 'document.docx',\n Version: 'etag-123',\n OwnerId: '1',\n Size: 1024,\n LastModifiedTime: '2024-01-01T10:00:00.000Z',\n UserId: '1',\n UserFriendlyName: 'Test User (test@example.com)',\n ReadOnly: false,\n UserExtraInfo: { avatar: 'base64-avatar' },\n UserCanNotWriteRelative: true,\n UserCanWrite: true,\n UserCanRename: false,\n SupportsUpdate: true,\n SupportsRename: false,\n SupportsExport: true,\n SupportsCoauth: true,\n SupportsLocks: true,\n SupportsGetLock: true\n })\n })\n\n it('should set UserCanWrite to false when user does not have modify permissions', async () => {\n const mockStats = {\n size: 1024,\n mtime: new Date('2024-01-01T10:00:00Z')\n }\n\n const mockRequest = {\n user: mockUser,\n space: {\n ...mockSpace,\n envPermissions: ''\n }\n } as unknown as FastifyCollaboraOnlineSpaceRequest\n\n jest.spyOn(fs, 'stat').mockResolvedValue(mockStats as any)\n jest.spyOn(filesUtils, 'fileName').mockReturnValue('document.docx')\n jest.spyOn(filesUtils, 'genEtag').mockReturnValue('etag-123')\n const { getAvatarBase64 } = await import('../../../users/utils/avatar')\n ;(getAvatarBase64 as jest.Mock).mockResolvedValue('base64-avatar')\n\n const result = await service.checkFileInfo(mockRequest)\n\n expect(result.UserCanWrite).toBe(false)\n })\n })\n\n describe('saveDocument', () => {\n beforeEach(() => {\n jest.spyOn(filesUtils, 'isPathExists').mockResolvedValue(true)\n jest.spyOn(filesUtils, 'isPathIsDir').mockResolvedValue(false)\n jest.spyOn(filesUtils, 'fileName').mockReturnValue('document.docx')\n jest.spyOn(filesUtils, 'uniqueFilePathFromDir').mockResolvedValue('/tmp/document-unique.docx')\n jest.spyOn(filesUtils, 'writeFromStream').mockResolvedValue(undefined)\n jest.spyOn(filesUtils, 'copyFileContent').mockResolvedValue(undefined)\n jest.spyOn(filesUtils, 'removeFiles').mockResolvedValue(undefined)\n })\n\n it('should save document successfully', async () => {\n const mockStats = {\n mtime: new Date('2024-01-01T11:00:00Z')\n }\n\n const mockRequest = {\n user: mockUser,\n space: mockSpace,\n headers: {\n 'content-length': '1024'\n },\n raw: new Readable()\n } as unknown as FastifyCollaboraOnlineSpaceRequest\n\n jest.spyOn(fs, 'stat').mockResolvedValue(mockStats as any)\n jest.spyOn(filesUtils, 'fileSize').mockResolvedValue(1024)\n\n const result = await service.saveDocument(mockRequest)\n\n expect(result).toEqual({\n LastModifiedTime: '2024-01-01T11:00:00.000Z'\n })\n expect(filesUtils.writeFromStream).toHaveBeenCalledWith('/tmp/document-unique.docx', mockRequest.raw)\n expect(filesUtils.copyFileContent).toHaveBeenCalledWith('/tmp/document-unique.docx', mockSpace.realPath)\n expect(filesUtils.removeFiles).toHaveBeenCalledWith('/tmp/document-unique.docx')\n })\n\n it('should throw error when document size mismatch', async () => {\n const mockRequest = {\n user: mockUser,\n space: mockSpace,\n headers: {\n 'content-length': '1024',\n [COLLABORA_HEADERS.Timestamp]: '2024-01-01T10:00:00.000Z'\n },\n raw: new Readable()\n } as unknown as FastifyCollaboraOnlineSpaceRequest\n\n jest.spyOn(filesUtils, 'fileSize').mockResolvedValue(512)\n jest.spyOn(fs, 'stat').mockResolvedValue({ mtime: new Date('2024-01-01T10:00:00.000Z') } as any)\n\n await expect(service.saveDocument(mockRequest)).rejects.toThrow(new HttpException('Size Mismatch', HttpStatus.BAD_REQUEST))\n })\n\n it('should throw error when timestamp mismatch', async () => {\n const mockRequest = {\n user: mockUser,\n space: mockSpace,\n headers: {\n 'content-length': '1024',\n [COLLABORA_HEADERS.Timestamp]: '2024-01-01T10:00:00.000Z'\n },\n raw: new Readable()\n } as unknown as FastifyCollaboraOnlineSpaceRequest\n\n jest.spyOn(fs, 'stat').mockResolvedValue({ mtime: new Date('2024-01-01T11:00:00.000Z') } as any)\n\n await expect(service.saveDocument(mockRequest)).rejects.toThrow(new HttpException({ LOOLStatusCode: 1010 }, HttpStatus.CONFLICT))\n })\n\n it('should throw error when document does not exist', async () => {\n const mockRequest = {\n user: mockUser,\n space: mockSpace,\n headers: {\n 'content-length': '1024'\n },\n raw: new Readable()\n } as unknown as FastifyCollaboraOnlineSpaceRequest\n\n jest.spyOn(filesUtils, 'isPathExists').mockResolvedValue(false)\n\n await expect(service.saveDocument(mockRequest)).rejects.toThrow(new HttpException('Document not found', HttpStatus.NOT_FOUND))\n })\n })\n\n describe('manageLock', () => {\n const mockReply = {\n header: jest.fn().mockReturnThis()\n } as unknown as FastifyReply\n\n describe('LOCK action', () => {\n it('should create a new lock', async () => {\n const mockRequest = {\n user: mockUser,\n space: mockSpace,\n headers: {\n [COLLABORA_HEADERS.Action]: COLLABORA_LOCK_ACTION.LOCK,\n [COLLABORA_HEADERS.LockToken]: 'new-lock-token'\n }\n } as unknown as FastifyCollaboraOnlineSpaceRequest\n\n jest.spyOn(filesLockManager, 'isLockedWithToken').mockResolvedValue(null)\n jest.spyOn(filesLockManager, 'create').mockResolvedValue([true, {} as any])\n\n await service.manageLock(mockRequest, mockReply)\n\n expect(filesLockManager.create).toHaveBeenCalledWith(\n mockUser,\n mockSpace.dbFile,\n COLLABORA_APP_LOCK,\n DEPTH.RESOURCE,\n expect.objectContaining({\n lockToken: 'new-lock-token',\n lockScope: LOCK_SCOPE.SHARED\n }),\n expect.any(Number)\n )\n })\n\n it('should refresh existing lock', async () => {\n const mockRequest = {\n user: mockUser,\n space: mockSpace,\n headers: {\n [COLLABORA_HEADERS.Action]: COLLABORA_LOCK_ACTION.LOCK,\n [COLLABORA_HEADERS.LockToken]: 'existing-lock-token'\n }\n } as unknown as FastifyCollaboraOnlineSpaceRequest\n\n const existingLock = { key: 'lock-key', options: { lockToken: 'existing-lock-token' } }\n jest.spyOn(filesLockManager, 'isLockedWithToken').mockResolvedValue(existingLock as any)\n jest.spyOn(filesLockManager, 'refreshLockTimeout').mockResolvedValue(undefined)\n\n await service.manageLock(mockRequest, mockReply)\n\n expect(filesLockManager.refreshLockTimeout).toHaveBeenCalledWith(existingLock, expect.any(Number))\n expect(filesLockManager.create).not.toHaveBeenCalled()\n })\n\n it('should throw conflict when lock creation fails', async () => {\n const mockRequest = {\n user: mockUser,\n space: mockSpace,\n headers: {\n [COLLABORA_HEADERS.Action]: COLLABORA_LOCK_ACTION.LOCK,\n [COLLABORA_HEADERS.LockToken]: 'new-lock-token'\n }\n } as unknown as FastifyCollaboraOnlineSpaceRequest\n\n const conflictingLock = { options: { lockToken: 'conflicting-token' } }\n jest.spyOn(filesLockManager, 'isLockedWithToken').mockResolvedValue(null)\n jest.spyOn(filesLockManager, 'create').mockResolvedValue([false, conflictingLock as any])\n\n await expect(service.manageLock(mockRequest, mockReply)).rejects.toThrow(new HttpException('The file is locked', HttpStatus.CONFLICT))\n expect(mockReply.header).toHaveBeenCalledWith(COLLABORA_HEADERS.LockToken, 'conflicting-token')\n })\n\n it('should throw error when lock token is missing', async () => {\n const mockRequest = {\n user: mockUser,\n space: mockSpace,\n headers: {\n [COLLABORA_HEADERS.Action]: COLLABORA_LOCK_ACTION.LOCK\n }\n } as unknown as FastifyCollaboraOnlineSpaceRequest\n\n await expect(service.manageLock(mockRequest, mockReply)).rejects.toThrow(new HttpException('Lock token is required', HttpStatus.CONFLICT))\n })\n })\n\n describe('UNLOCK action', () => {\n it('should remove existing lock', async () => {\n const mockRequest = {\n user: mockUser,\n space: mockSpace,\n headers: {\n [COLLABORA_HEADERS.Action]: COLLABORA_LOCK_ACTION.UNLOCK,\n [COLLABORA_HEADERS.LockToken]: 'lock-token-to-remove'\n }\n } as unknown as FastifyCollaboraOnlineSpaceRequest\n\n const existingLock = { key: 'lock-key', options: { lockToken: 'lock-token-to-remove' } }\n jest.spyOn(filesLockManager, 'isLockedWithToken').mockResolvedValue(existingLock as any)\n jest.spyOn(filesLockManager, 'removeLock').mockResolvedValue(undefined)\n\n await service.manageLock(mockRequest, mockReply)\n\n expect(filesLockManager.removeLock).toHaveBeenCalledWith('lock-key')\n })\n\n it('should throw error when lock does not exist', async () => {\n const mockRequest = {\n user: mockUser,\n space: mockSpace,\n headers: {\n [COLLABORA_HEADERS.Action]: COLLABORA_LOCK_ACTION.UNLOCK,\n [COLLABORA_HEADERS.LockToken]: 'non-existent-token'\n }\n } as unknown as FastifyCollaboraOnlineSpaceRequest\n\n jest.spyOn(filesLockManager, 'isLockedWithToken').mockResolvedValue(null)\n\n await expect(service.manageLock(mockRequest, mockReply)).rejects.toThrow(new HttpException('Lock not found', HttpStatus.CONFLICT))\n })\n })\n\n describe('GET_LOCK action', () => {\n it('should return lock token when lock exists', async () => {\n const mockRequest = {\n user: mockUser,\n space: mockSpace,\n headers: {\n [COLLABORA_HEADERS.Action]: COLLABORA_LOCK_ACTION.GET_LOCK\n }\n } as unknown as FastifyCollaboraOnlineSpaceRequest\n\n const existingLock = { options: { lockToken: 'existing-lock-token' } }\n jest.spyOn(filesLockManager, 'getLocksByPath').mockResolvedValue([existingLock as any])\n\n await service.manageLock(mockRequest, mockReply)\n\n expect(mockReply.header).toHaveBeenCalledWith(COLLABORA_HEADERS.LockToken, 'existing-lock-token')\n })\n\n it('should not set header when no lock exists', async () => {\n const mockRequest = {\n user: mockUser,\n space: mockSpace,\n headers: {\n [COLLABORA_HEADERS.Action]: COLLABORA_LOCK_ACTION.GET_LOCK\n }\n } as unknown as FastifyCollaboraOnlineSpaceRequest\n\n jest.spyOn(filesLockManager, 'getLocksByPath').mockResolvedValue([])\n\n await service.manageLock(mockRequest, mockReply)\n\n expect(mockReply.header).not.toHaveBeenCalled()\n })\n })\n\n describe('REFRESH_LOCK action', () => {\n it('should refresh existing lock', async () => {\n const mockRequest = {\n user: mockUser,\n space: mockSpace,\n headers: {\n [COLLABORA_HEADERS.Action]: COLLABORA_LOCK_ACTION.REFRESH_LOCK,\n [COLLABORA_HEADERS.LockToken]: 'lock-token-to-refresh'\n }\n } as unknown as FastifyCollaboraOnlineSpaceRequest\n\n const existingLock = { key: 'lock-key', options: { lockToken: 'lock-token-to-refresh' } }\n jest.spyOn(filesLockManager, 'isLockedWithToken').mockResolvedValue(existingLock as any)\n jest.spyOn(filesLockManager, 'refreshLockTimeout').mockResolvedValue(undefined)\n\n await service.manageLock(mockRequest, mockReply)\n\n expect(filesLockManager.refreshLockTimeout).toHaveBeenCalledWith(existingLock, expect.any(Number))\n })\n\n it('should throw error when lock does not exist', async () => {\n const mockRequest = {\n user: mockUser,\n space: mockSpace,\n headers: {\n [COLLABORA_HEADERS.Action]: COLLABORA_LOCK_ACTION.REFRESH_LOCK,\n [COLLABORA_HEADERS.LockToken]: 'non-existent-token'\n }\n } as unknown as FastifyCollaboraOnlineSpaceRequest\n\n jest.spyOn(filesLockManager, 'isLockedWithToken').mockResolvedValue(null)\n\n await expect(service.manageLock(mockRequest, mockReply)).rejects.toThrow(new HttpException('Lock not found', HttpStatus.CONFLICT))\n })\n })\n\n describe('Unknown action', () => {\n it('should throw error for unknown lock action', async () => {\n const mockRequest = {\n user: mockUser,\n space: mockSpace,\n headers: {\n [COLLABORA_HEADERS.Action]: 'UNKNOWN_ACTION'\n }\n } as unknown as FastifyCollaboraOnlineSpaceRequest\n\n await expect(service.manageLock(mockRequest, mockReply)).rejects.toThrow(new HttpException('Unknown lock action', HttpStatus.BAD_REQUEST))\n })\n })\n })\n})\n"],"names":["jest","mock","describe","CollaboraOnlineManager","name","service","filesLockManager","mockUser","id","login","email","fullName","language","role","applications","mockSpace","realPath","url","envPermissions","dbFile","path","ownerId","beforeEach","module","Test","createTestingModule","providers","provide","ContextManager","useValue","headerOriginUrl","fn","mockReturnValue","JwtService","signAsync","mockResolvedValue","FilesLockManager","checkConflicts","convertLockToFileLockProps","create","removeLock","getLocksByPath","isLockedWithToken","refreshLockTimeout","compile","useLogger","get","afterEach","clearAllMocks","it","expect","toBeDefined","spyOn","filesUtils","spaceWithPermissions","SPACE_OPERATION","MODIFY","undefined","result","getSettings","toEqual","documentServerUrl","stringContaining","mode","FILE_MODE","EDIT","hasLock","toHaveBeenCalledWith","DEPTH","RESOURCE","objectContaining","userId","app","COLLABORA_APP_LOCK","lockScope","LOCK_SCOPE","SHARED","mockLock","options","lockToken","mockFileLockProps","owner","isExclusive","mockRejectedValue","LockConflict","toBe","VIEW","spaceWithoutPermissions","not","toHaveBeenCalled","spaceWithUnsupportedFile","rejects","toThrow","HttpException","HttpStatus","BAD_REQUEST","NOT_FOUND","mockStats","size","mtime","Date","mockRequest","user","space","fs","getAvatarBase64","checkFileInfo","BaseFileName","Version","OwnerId","Size","LastModifiedTime","UserId","UserFriendlyName","ReadOnly","UserExtraInfo","avatar","UserCanNotWriteRelative","UserCanWrite","UserCanRename","SupportsUpdate","SupportsRename","SupportsExport","SupportsCoauth","SupportsLocks","SupportsGetLock","headers","raw","Readable","saveDocument","writeFromStream","copyFileContent","removeFiles","COLLABORA_HEADERS","Timestamp","LOOLStatusCode","CONFLICT","mockReply","header","mockReturnThis","Action","COLLABORA_LOCK_ACTION","LOCK","LockToken","manageLock","any","Number","existingLock","key","conflictingLock","UNLOCK","GET_LOCK","REFRESH_LOCK"],"mappings":"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"}