@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.
- package/CHANGELOG.md +41 -4
- package/environment/environment.dist.yaml +15 -5
- package/package.json +18 -19
- package/server/app.bootstrap.js +1 -1
- package/server/app.bootstrap.js.map +1 -1
- package/server/app.constants.js +3 -2
- package/server/app.constants.js.map +1 -1
- package/server/applications/files/constants/cache.js +2 -5
- package/server/applications/files/constants/cache.js.map +1 -1
- package/server/applications/files/constants/files.js +4 -0
- package/server/applications/files/constants/files.js.map +1 -1
- package/server/applications/files/constants/operations.js +4 -0
- package/server/applications/files/constants/operations.js.map +1 -1
- package/server/applications/files/constants/routes.js +1 -26
- package/server/applications/files/constants/routes.js.map +1 -1
- package/server/applications/files/files.config.js +15 -39
- package/server/applications/files/files.config.js.map +1 -1
- package/server/applications/files/files.controller.js +4 -4
- package/server/applications/files/files.controller.js.map +1 -1
- package/server/applications/files/files.module.js +12 -9
- package/server/applications/files/files.module.js.map +1 -1
- package/server/applications/files/interfaces/file-lock.interface.js.map +1 -1
- package/server/applications/files/interfaces/file-props.interface.js.map +1 -1
- package/server/applications/files/modules/collabora-online/collabora-online-environment.decorator.js +32 -0
- package/server/applications/files/modules/collabora-online/collabora-online-environment.decorator.js.map +1 -0
- package/server/applications/files/modules/collabora-online/collabora-online-manager.service.js +280 -0
- package/server/applications/files/modules/collabora-online/collabora-online-manager.service.js.map +1 -0
- package/server/applications/files/modules/collabora-online/collabora-online-manager.service.spec.js +552 -0
- package/server/applications/files/modules/collabora-online/collabora-online-manager.service.spec.js.map +1 -0
- package/server/applications/files/modules/collabora-online/collabora-online.config.js +40 -0
- package/server/applications/files/modules/collabora-online/collabora-online.config.js.map +1 -0
- package/server/applications/files/modules/collabora-online/collabora-online.constants.js +110 -0
- package/server/applications/files/modules/collabora-online/collabora-online.constants.js.map +1 -0
- package/server/applications/files/modules/collabora-online/collabora-online.controller.js +128 -0
- package/server/applications/files/modules/collabora-online/collabora-online.controller.js.map +1 -0
- package/server/applications/files/modules/collabora-online/collabora-online.controller.spec.js +47 -0
- package/server/applications/files/modules/collabora-online/collabora-online.controller.spec.js.map +1 -0
- package/server/applications/files/{interfaces/only-office-config.interface.js → modules/collabora-online/collabora-online.dtos.js} +1 -1
- package/server/applications/files/modules/collabora-online/collabora-online.dtos.js.map +1 -0
- package/server/applications/files/{guards/files-only-office.guard.js → modules/collabora-online/collabora-online.guard.js} +7 -21
- package/server/applications/files/modules/collabora-online/collabora-online.guard.js.map +1 -0
- package/server/applications/files/modules/collabora-online/collabora-online.guard.spec.js +86 -0
- package/server/applications/files/modules/collabora-online/collabora-online.guard.spec.js.map +1 -0
- package/server/applications/files/modules/collabora-online/collabora-online.interface.js +10 -0
- package/server/applications/files/modules/collabora-online/collabora-online.interface.js.map +1 -0
- package/server/applications/files/modules/collabora-online/collabora-online.module.js +41 -0
- package/server/applications/files/modules/collabora-online/collabora-online.module.js.map +1 -0
- package/server/applications/files/modules/collabora-online/collabora-online.routes.js +35 -0
- package/server/applications/files/modules/collabora-online/collabora-online.routes.js.map +1 -0
- package/server/applications/files/modules/collabora-online/collabora-online.strategy.js +59 -0
- package/server/applications/files/modules/collabora-online/collabora-online.strategy.js.map +1 -0
- package/server/applications/files/modules/collabora-online/collabora-online.utils.js +28 -0
- package/server/applications/files/modules/collabora-online/collabora-online.utils.js.map +1 -0
- package/server/applications/files/{decorators → modules/only-office}/only-office-environment.decorator.js +5 -5
- package/server/applications/files/modules/only-office/only-office-environment.decorator.js.map +1 -0
- package/server/applications/files/{services/files-only-office-manager.service.js → modules/only-office/only-office-manager.service.js} +101 -97
- package/server/applications/files/modules/only-office/only-office-manager.service.js.map +1 -0
- package/server/applications/files/modules/only-office/only-office-manager.service.spec.js +477 -0
- package/server/applications/files/modules/only-office/only-office-manager.service.spec.js.map +1 -0
- package/server/applications/files/modules/only-office/only-office.config.js +51 -0
- package/server/applications/files/modules/only-office/only-office.config.js.map +1 -0
- package/server/applications/files/modules/only-office/only-office.constants.js +417 -0
- package/server/applications/files/modules/only-office/only-office.constants.js.map +1 -0
- package/server/applications/files/{files-only-office.controller.js → modules/only-office/only-office.controller.js} +35 -52
- package/server/applications/files/modules/only-office/only-office.controller.js.map +1 -0
- package/server/applications/files/{files-only-office.controller.spec.js → modules/only-office/only-office.controller.spec.js} +24 -21
- package/server/applications/files/modules/only-office/only-office.controller.spec.js.map +1 -0
- package/server/applications/files/modules/only-office/only-office.dtos.js +10 -0
- package/server/applications/files/modules/only-office/only-office.dtos.js.map +1 -0
- package/server/applications/files/modules/only-office/only-office.guard.js +40 -0
- package/server/applications/files/modules/only-office/only-office.guard.js.map +1 -0
- package/server/applications/files/{guards/files-only-office.guard.spec.js → modules/only-office/only-office.guard.spec.js} +15 -21
- package/server/applications/files/modules/only-office/only-office.guard.spec.js.map +1 -0
- package/server/applications/files/modules/only-office/only-office.interface.js +10 -0
- package/server/applications/files/modules/only-office/only-office.interface.js.map +1 -0
- package/server/applications/files/modules/only-office/only-office.module.js +41 -0
- package/server/applications/files/modules/only-office/only-office.module.js.map +1 -0
- package/server/applications/files/modules/only-office/only-office.routes.js +45 -0
- package/server/applications/files/modules/only-office/only-office.routes.js.map +1 -0
- package/server/applications/files/{guards/files-only-office.strategy.js → modules/only-office/only-office.strategy.js} +11 -11
- package/server/applications/files/modules/only-office/only-office.strategy.js.map +1 -0
- package/server/applications/files/services/files-lock-manager.service.js +25 -33
- package/server/applications/files/services/files-lock-manager.service.js.map +1 -1
- package/server/applications/files/services/files-manager.service.js +17 -16
- package/server/applications/files/services/files-manager.service.js.map +1 -1
- package/server/applications/files/services/files-methods.service.js +2 -2
- package/server/applications/files/services/files-methods.service.js.map +1 -1
- package/server/applications/files/services/files-methods.service.spec.js +5 -5
- package/server/applications/files/services/files-methods.service.spec.js.map +1 -1
- package/server/applications/files/services/files-recents.service.js +4 -0
- package/server/applications/files/services/files-recents.service.js.map +1 -1
- package/server/applications/files/services/files-scheduler.service.js +24 -5
- package/server/applications/files/services/files-scheduler.service.js.map +1 -1
- package/server/applications/files/utils/files.js +10 -2
- package/server/applications/files/utils/files.js.map +1 -1
- package/server/applications/links/constants/routes.js +5 -0
- package/server/applications/links/constants/routes.js.map +1 -1
- package/server/applications/links/interfaces/link-space.interface.js.map +1 -1
- package/server/applications/links/links.controller.js +25 -5
- package/server/applications/links/links.controller.js.map +1 -1
- package/server/applications/links/services/links-manager.service.js +43 -21
- package/server/applications/links/services/links-manager.service.js.map +1 -1
- package/server/applications/links/services/links-manager.service.spec.js +4 -3
- package/server/applications/links/services/links-manager.service.spec.js.map +1 -1
- package/server/applications/links/services/links-queries.service.js +9 -2
- package/server/applications/links/services/links-queries.service.js.map +1 -1
- package/server/applications/shares/interfaces/share-link.interface.js.map +1 -1
- package/server/applications/shares/services/shares-manager.service.js +3 -0
- package/server/applications/shares/services/shares-manager.service.js.map +1 -1
- package/server/applications/shares/services/shares-manager.service.spec.js +2 -1
- package/server/applications/shares/services/shares-manager.service.spec.js.map +1 -1
- package/server/applications/shares/services/shares-queries.service.js +1 -0
- package/server/applications/shares/services/shares-queries.service.js.map +1 -1
- package/server/applications/spaces/constants/spaces.js +2 -2
- package/server/applications/spaces/constants/spaces.js.map +1 -1
- package/server/applications/spaces/decorators/space-override-permission.decorator.js +18 -0
- package/server/applications/spaces/decorators/space-override-permission.decorator.js.map +1 -0
- package/server/applications/spaces/guards/space.guard.js +40 -33
- package/server/applications/spaces/guards/space.guard.js.map +1 -1
- package/server/applications/spaces/guards/space.guard.spec.js +10 -15
- package/server/applications/spaces/guards/space.guard.spec.js.map +1 -1
- package/server/applications/spaces/services/spaces-scheduler.service.js +9 -1
- package/server/applications/spaces/services/spaces-scheduler.service.js.map +1 -1
- package/server/applications/webdav/constants/webdav.js +4 -0
- package/server/applications/webdav/constants/webdav.js.map +1 -1
- package/server/applications/webdav/guards/webdav-protocol.guard.js +9 -8
- package/server/applications/webdav/guards/webdav-protocol.guard.js.map +1 -1
- package/server/applications/webdav/guards/webdav-protocol.guard.spec.js +1 -1
- package/server/applications/webdav/guards/webdav-protocol.guard.spec.js.map +1 -1
- package/server/applications/webdav/interfaces/webdav.interface.js.map +1 -1
- package/server/applications/webdav/services/webdav-methods.service.js +40 -17
- package/server/applications/webdav/services/webdav-methods.service.js.map +1 -1
- package/server/applications/webdav/services/webdav-methods.service.spec.js +2157 -1289
- package/server/applications/webdav/services/webdav-methods.service.spec.js.map +1 -1
- package/server/applications/webdav/utils/webdav.js +8 -4
- package/server/applications/webdav/utils/webdav.js.map +1 -1
- package/server/applications/webdav/webdav.controller.js +4 -4
- package/server/applications/webdav/webdav.controller.js.map +1 -1
- package/server/authentication/guards/auth-token-access.guard.js +8 -3
- package/server/authentication/guards/auth-token-access.guard.js.map +1 -1
- package/server/authentication/services/auth-methods/auth-method-two-fa.service.js +1 -1
- package/server/authentication/services/auth-methods/auth-method-two-fa.service.js.map +1 -1
- package/server/authentication/services/auth-methods/auth-method-two-fa.service.spec.js +350 -4
- package/server/authentication/services/auth-methods/auth-method-two-fa.service.spec.js.map +1 -1
- package/server/configuration/config.environment.js +5 -1
- package/server/configuration/config.environment.js.map +1 -1
- package/server/configuration/config.interfaces.js.map +1 -1
- package/static/3rdpartylicenses.txt +507 -507
- package/static/assets/pdfjs/build/pdf.mjs +93 -33
- package/static/assets/pdfjs/build/pdf.mjs.map +1 -1
- package/static/assets/pdfjs/build/pdf.sandbox.mjs +3 -3
- package/static/assets/pdfjs/build/pdf.sandbox.mjs.map +1 -1
- package/static/assets/pdfjs/build/pdf.worker.mjs +166 -54
- package/static/assets/pdfjs/build/pdf.worker.mjs.map +1 -1
- package/static/assets/pdfjs/version +1 -1
- package/static/assets/pdfjs/web/images/checkmark.svg +5 -0
- package/static/assets/pdfjs/web/images/pages_closeButton.svg +3 -0
- package/static/assets/pdfjs/web/images/pages_selected.svg +7 -0
- package/static/assets/pdfjs/web/images/pages_viewArrow.svg +3 -0
- package/static/assets/pdfjs/web/images/pages_viewButton.svg +3 -0
- package/static/assets/pdfjs/web/locale/be/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/bs/viewer.ftl +0 -5
- package/static/assets/pdfjs/web/locale/cs/viewer.ftl +4 -6
- package/static/assets/pdfjs/web/locale/cy/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/da/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/de/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/dsb/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/el/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/en-CA/viewer.ftl +6 -2
- package/static/assets/pdfjs/web/locale/en-GB/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/en-US/viewer.ftl +82 -17
- package/static/assets/pdfjs/web/locale/eo/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/es-AR/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/es-CL/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/es-ES/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/es-MX/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/eu/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/fi/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/fr/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/fur/viewer.ftl +0 -5
- package/static/assets/pdfjs/web/locale/fy-NL/viewer.ftl +3 -5
- package/static/assets/pdfjs/web/locale/gn/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/he/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/hr/viewer.ftl +66 -0
- package/static/assets/pdfjs/web/locale/hsb/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/hu/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/hy-AM/viewer.ftl +3 -8
- package/static/assets/pdfjs/web/locale/ia/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/id/viewer.ftl +0 -5
- package/static/assets/pdfjs/web/locale/is/viewer.ftl +0 -5
- package/static/assets/pdfjs/web/locale/it/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/ja/viewer.ftl +0 -14
- package/static/assets/pdfjs/web/locale/ka/viewer.ftl +4 -6
- package/static/assets/pdfjs/web/locale/kab/viewer.ftl +0 -5
- package/static/assets/pdfjs/web/locale/kk/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/ko/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/nb-NO/viewer.ftl +1 -3
- package/static/assets/pdfjs/web/locale/nl/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/nn-NO/viewer.ftl +4 -2
- package/static/assets/pdfjs/web/locale/pa-IN/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/pl/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/pt-BR/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/pt-PT/viewer.ftl +35 -0
- package/static/assets/pdfjs/web/locale/rm/viewer.ftl +0 -5
- package/static/assets/pdfjs/web/locale/ro/viewer.ftl +4 -6
- package/static/assets/pdfjs/web/locale/ru/viewer.ftl +3 -5
- package/static/assets/pdfjs/web/locale/sk/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/sl/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/sq/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/sv-SE/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/tg/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/th/viewer.ftl +2 -2
- package/static/assets/pdfjs/web/locale/tr/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/vi/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/zh-CN/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/locale/zh-TW/viewer.ftl +0 -2
- package/static/assets/pdfjs/web/viewer.css +1778 -835
- package/static/assets/pdfjs/web/viewer.html +167 -86
- package/static/assets/pdfjs/web/viewer.mjs +1106 -801
- package/static/assets/pdfjs/web/viewer.mjs.map +1 -1
- package/static/chunk-27V66YJV.js +2 -0
- package/static/{chunk-WJYVS27M.js → chunk-27Z3SYRL.js} +1 -1
- package/static/{chunk-NFIES7BC.js → chunk-2RWLNKZH.js} +1 -1
- package/static/chunk-2YQ4SX3A.js +13 -0
- package/static/{chunk-GENTF6JM.js → chunk-3JYMJQYT.js} +1 -1
- package/static/chunk-3QTROEHV.js +1 -0
- package/static/{chunk-ZPI7RQ2S.js → chunk-3RPUQ22U.js} +1 -1
- package/static/{chunk-R6VB3INJ.js → chunk-3WZ6F3LC.js} +1 -1
- package/static/chunk-3ZLBVUCX.js +2 -0
- package/static/{chunk-5HCVWZMA.js → chunk-45AZ6ZML.js} +1 -1
- package/static/chunk-46TJLPJY.js +1 -0
- package/static/chunk-4NIYCYRS.js +2 -0
- package/static/{chunk-XXYMVRSH.js → chunk-4TPFERL6.js} +1 -1
- package/static/{chunk-CAZSNVMS.js → chunk-5O66CLTD.js} +1 -1
- package/static/chunk-6OEOADR6.js +1 -0
- package/static/chunk-6WMXMIE4.js +1 -0
- package/static/{chunk-NK2NMAJI.js → chunk-7VRYTDX4.js} +1 -1
- package/static/{chunk-ASBPYTLT.js → chunk-ARS47O5X.js} +1 -1
- package/static/chunk-B6HQYQYG.js +1 -0
- package/static/chunk-BCN4T5DO.js +2 -0
- package/static/{chunk-PKU4IIIR.js → chunk-CCZWPM7Q.js} +1 -1
- package/static/{chunk-QUSS6SUC.js → chunk-CMNMPG6Z.js} +1 -1
- package/static/{chunk-GDPJRUVU.js → chunk-CSVPAZHK.js} +1 -1
- package/static/{chunk-BJARRIS6.js → chunk-D55YR5X7.js} +4 -4
- package/static/{chunk-Z6RJZIDG.js → chunk-D5FQ72R4.js} +1 -1
- package/static/{chunk-4DF2SQD4.js → chunk-DGCVA6BM.js} +1 -1
- package/static/{chunk-TVJQXN73.js → chunk-DVCN3P7Q.js} +1 -1
- package/static/chunk-E32J777S.js +5 -0
- package/static/{chunk-5NHB7SV3.js → chunk-FIUF2JM4.js} +1 -1
- package/static/{chunk-RJOHDAPM.js → chunk-G3PL6YX3.js} +1 -1
- package/static/chunk-G7RZN7HN.js +1 -0
- package/static/{chunk-DDRGLHOP.js → chunk-GQHXYX6Z.js} +1 -1
- package/static/{chunk-5HYSNQR4.js → chunk-GWRAGN3M.js} +1 -1
- package/static/{chunk-ZC5ZDCDC.js → chunk-GXWGB7WO.js} +1 -1
- package/static/{chunk-25PWAXTJ.js → chunk-HGODIZTV.js} +1 -1
- package/static/{chunk-4KXJ6C4N.js → chunk-HZAB6F4Q.js} +1 -1
- package/static/chunk-I3FR3A45.js +1 -0
- package/static/{chunk-A6J6SOM6.js → chunk-I5SPA4G2.js} +1 -1
- package/static/{chunk-TGHBDJZA.js → chunk-IMFO2MI7.js} +1 -1
- package/static/{chunk-CURVLK7L.js → chunk-JNTNMIUH.js} +1 -1
- package/static/chunk-JRXG43AA.js +2 -0
- package/static/{chunk-XAIOGRBO.js → chunk-KAUCN24H.js} +1 -1
- package/static/chunk-KDUAB76O.js +1 -0
- package/static/chunk-KPOQLDWF.js +1 -0
- package/static/{chunk-HE6EDXWI.js → chunk-KWFELZTM.js} +1 -1
- package/static/{chunk-2CAAJBRO.js → chunk-L3BIP4AA.js} +1 -1
- package/static/{chunk-U75PLYIJ.js → chunk-LGIVVJDD.js} +1 -1
- package/static/{chunk-JEVBUJQ4.js → chunk-LNLBIJZD.js} +1 -1
- package/static/chunk-LTJNLOX2.js +1 -0
- package/static/{chunk-SDR3UG2F.js → chunk-LZUHREOF.js} +1 -1
- package/static/{chunk-VO4WVT6K.js → chunk-NIR4YE2E.js} +1 -1
- package/static/{chunk-S6YKBWJE.js → chunk-NJJURHX4.js} +1 -1
- package/static/chunk-NNZWSNAW.js +1 -0
- package/static/chunk-NWKBB7J4.js +1 -0
- package/static/chunk-O3YLAEVE.js +3 -0
- package/static/chunk-OUHCDDT6.js +1 -0
- package/static/{chunk-ZRBLCAOK.js → chunk-PDG7DOEF.js} +1 -1
- package/static/chunk-POUWUMC4.js +1 -0
- package/static/{chunk-YTBSB2GE.js → chunk-PPJCVBJH.js} +1 -1
- package/static/{chunk-K3MOXDU5.js → chunk-PQZLR4P3.js} +1 -1
- package/static/chunk-PVYVY3GD.js +1 -0
- package/static/chunk-Q5X5TPAG.js +1 -0
- package/static/{chunk-LFAQLJZK.js → chunk-QHJT5H4M.js} +1 -1
- package/static/{chunk-A7DSX7VP.js → chunk-R4VMWCM5.js} +1 -1
- package/static/{chunk-27XEAHMV.js → chunk-R7PLNX75.js} +1 -1
- package/static/chunk-RJULB733.js +1 -0
- package/static/{chunk-MBFMTBVJ.js → chunk-RNVPQQKT.js} +5 -5
- package/static/chunk-RTNEBRKJ.js +1 -0
- package/static/{chunk-FXM7XXWA.js → chunk-S3TTWPQA.js} +1 -1
- package/static/{chunk-6VJI4X2A.js → chunk-SDJNZULP.js} +1 -1
- package/static/chunk-SNOOCDJD.js +1 -0
- package/static/chunk-T42BV6TR.js +1 -0
- package/static/{chunk-4OV3SAUS.js → chunk-TNCKNU6I.js} +1 -1
- package/static/{chunk-2LHHXDD5.js → chunk-ULSPQ3HP.js} +1 -1
- package/static/{chunk-4EUHBTWV.js → chunk-UOK3LKSX.js} +1 -1
- package/static/{chunk-7NI353LS.js → chunk-VD5JHSDS.js} +1 -1
- package/static/{chunk-YXWF2DGF.js → chunk-XBKCQCBI.js} +1 -1
- package/static/{chunk-KBWK65KM.js → chunk-XEWLBWFF.js} +1 -1
- package/static/{chunk-FLPZB3OX.js → chunk-XTVNHFKX.js} +1 -1
- package/static/chunk-ZCSHU3D7.js +1 -0
- package/static/{chunk-FRBTL2ER.js → chunk-ZEJLIGAY.js} +1 -1
- package/static/{chunk-7H5O4BLV.js → chunk-ZHOE5VEY.js} +1 -1
- package/static/chunk-ZOMRIN3G.js +2 -0
- package/static/index.html +2 -2
- package/static/main-YKDNJ7LK.js +11 -0
- package/static/{styles-S5HVK4H5.css → styles-XLLEY5Y3.css} +1 -1
- package/server/applications/files/constants/only-office.js +0 -531
- package/server/applications/files/constants/only-office.js.map +0 -1
- package/server/applications/files/decorators/only-office-environment.decorator.js.map +0 -1
- package/server/applications/files/files-only-office.controller.js.map +0 -1
- package/server/applications/files/files-only-office.controller.spec.js.map +0 -1
- package/server/applications/files/guards/files-only-office.guard.js.map +0 -1
- package/server/applications/files/guards/files-only-office.guard.spec.js.map +0 -1
- package/server/applications/files/guards/files-only-office.strategy.js.map +0 -1
- package/server/applications/files/interfaces/only-office-config.interface.js.map +0 -1
- package/server/applications/files/services/files-only-office-manager.service.js.map +0 -1
- package/server/applications/files/services/files-only-office-manager.service.spec.js +0 -58
- package/server/applications/files/services/files-only-office-manager.service.spec.js.map +0 -1
- package/static/chunk-2XY4PMI5.js +0 -1
- package/static/chunk-33WFRCUP.js +0 -1
- package/static/chunk-3LVFDMTN.js +0 -1
- package/static/chunk-42L6C5MT.js +0 -1
- package/static/chunk-5WCQBTXW.js +0 -1
- package/static/chunk-A7R246NW.js +0 -1
- package/static/chunk-BSB4VROD.js +0 -2
- package/static/chunk-DHFQIFOF.js +0 -1
- package/static/chunk-DRHPEERW.js +0 -2
- package/static/chunk-FCGTI42I.js +0 -1
- package/static/chunk-H4RLHI3Y.js +0 -1
- package/static/chunk-ITVA26X2.js +0 -2
- package/static/chunk-IUJ4IK26.js +0 -1
- package/static/chunk-L3PDWJZ3.js +0 -3
- package/static/chunk-LBXOAKBD.js +0 -1
- package/static/chunk-LZKI5P5T.js +0 -1
- package/static/chunk-MYM43ENO.js +0 -1
- package/static/chunk-MZBO5PAR.js +0 -1
- package/static/chunk-NAH4V2R6.js +0 -2
- package/static/chunk-O7UXVNR2.js +0 -1
- package/static/chunk-PCFH5HCI.js +0 -2
- package/static/chunk-SRBOO7AO.js +0 -1
- package/static/chunk-UUX3M6DC.js +0 -1
- package/static/chunk-VJ2HWQRJ.js +0 -5
- package/static/chunk-VZPCXSRG.js +0 -2
- package/static/chunk-W72JYHOH.js +0 -1
- package/static/chunk-XHQEF2IX.js +0 -1
- package/static/chunk-XKEBQNQJ.js +0 -1
- package/static/chunk-ZERBTNFW.js +0 -13
- package/static/main-FE6GWZXU.js +0 -11
- /package/static/assets/pdfjs/web/images/{toolbarButton-sidebarToggle.svg → toolbarButton-viewsManagerToggle.svg} +0 -0
package/server/applications/files/modules/collabora-online/collabora-online-manager.service.spec.js
ADDED
|
@@ -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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"}
|