@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
|
@@ -36,22 +36,11 @@ function _ts_metadata(k, v) {
|
|
|
36
36
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
37
37
|
}
|
|
38
38
|
let FilesLockManager = class FilesLockManager {
|
|
39
|
-
async create(user, dbFile, depth,
|
|
40
|
-
let token;
|
|
41
|
-
let lockscope;
|
|
42
|
-
if (davLock) {
|
|
43
|
-
// webdav context
|
|
44
|
-
davLock.locktoken = this.genDAVToken();
|
|
45
|
-
token = davLock.locktoken;
|
|
46
|
-
lockscope = davLock.lockscope;
|
|
47
|
-
} else {
|
|
48
|
-
// api context
|
|
49
|
-
token = null;
|
|
50
|
-
lockscope = null;
|
|
51
|
-
}
|
|
39
|
+
async create(user, dbFile, app, depth, options, ttl) {
|
|
52
40
|
try {
|
|
53
41
|
await this.checkConflicts(dbFile, depth, {
|
|
54
|
-
|
|
42
|
+
app: app,
|
|
43
|
+
lockScope: options?.lockScope
|
|
55
44
|
});
|
|
56
45
|
} catch (e) {
|
|
57
46
|
if (e instanceof _filelockerror.LockConflict) {
|
|
@@ -65,16 +54,17 @@ let FilesLockManager = class FilesLockManager {
|
|
|
65
54
|
ttl ??= _cache.CACHE_LOCK_DEFAULT_TTL;
|
|
66
55
|
const key = `${_cache.CACHE_LOCK_PREFIX}|${this.genSuffixKey(dbFile, {
|
|
67
56
|
depth: depth,
|
|
68
|
-
token:
|
|
57
|
+
token: options?.lockToken
|
|
69
58
|
})}`;
|
|
70
59
|
const expiration = Math.floor((0, _shared.currentTimeStamp)() + ttl);
|
|
71
60
|
const lock = {
|
|
72
61
|
owner: user.asOwner(),
|
|
73
62
|
dbFilePath: dbFile.path,
|
|
74
63
|
key: key,
|
|
64
|
+
app: app,
|
|
75
65
|
depth: depth,
|
|
76
66
|
expiration: expiration,
|
|
77
|
-
|
|
67
|
+
options: options
|
|
78
68
|
};
|
|
79
69
|
this.logger.verbose(`${this.create.name} - ${key}`);
|
|
80
70
|
await this.cache.set(key, lock, ttl);
|
|
@@ -83,7 +73,7 @@ let FilesLockManager = class FilesLockManager {
|
|
|
83
73
|
lock
|
|
84
74
|
];
|
|
85
75
|
}
|
|
86
|
-
async createOrRefresh(user, dbFile, depth, ttl) {
|
|
76
|
+
async createOrRefresh(user, dbFile, app, depth, ttl) {
|
|
87
77
|
// Returns: [created, lock]
|
|
88
78
|
ttl ??= _cache.CACHE_LOCK_DEFAULT_TTL;
|
|
89
79
|
const locks = await this.getLocksByPath(dbFile);
|
|
@@ -106,7 +96,7 @@ let FilesLockManager = class FilesLockManager {
|
|
|
106
96
|
];
|
|
107
97
|
}
|
|
108
98
|
// Create the lock
|
|
109
|
-
const [ok, lock] = await this.create(user, dbFile, depth, null, ttl);
|
|
99
|
+
const [ok, lock] = await this.create(user, dbFile, app, depth, null, ttl);
|
|
110
100
|
if (!ok) {
|
|
111
101
|
throw new _filelockerror.LockConflict(lock, 'Conflicting lock');
|
|
112
102
|
}
|
|
@@ -136,7 +126,7 @@ let FilesLockManager = class FilesLockManager {
|
|
|
136
126
|
}
|
|
137
127
|
}
|
|
138
128
|
async isLockedWithToken(token, dbFilePath) {
|
|
139
|
-
// check if url (or any of its parents) is locked by the token
|
|
129
|
+
// check if the url (or any of its parents) is locked by the token
|
|
140
130
|
const lock = await this.getLockByToken(token);
|
|
141
131
|
return lock ? dbFilePath.startsWith(lock.dbFilePath) ? lock : null : null;
|
|
142
132
|
}
|
|
@@ -191,16 +181,16 @@ let FilesLockManager = class FilesLockManager {
|
|
|
191
181
|
}
|
|
192
182
|
async checkConflicts(dbFile, depth, options) {
|
|
193
183
|
/* Checks if a file could be modified, created, moved, or deleted
|
|
194
|
-
Throws
|
|
184
|
+
Throws a `LockConflict` error when there are parent locks (depth: 0) or child locks (depth: infinite) that prevent modification
|
|
195
185
|
Returns on the first conflict (compliant with the RFC 4918)
|
|
196
186
|
*/ for await (const l of this.searchParentLocks(dbFile, {
|
|
197
187
|
includeRoot: true,
|
|
198
188
|
depth: _webdav.DEPTH.INFINITY
|
|
199
189
|
})){
|
|
200
|
-
if (options?.lockScope && options?.lockScope === _webdav.LOCK_SCOPE.SHARED && l.
|
|
190
|
+
if (options?.lockScope && options?.lockScope === _webdav.LOCK_SCOPE.SHARED && l.options.lockScope === _webdav.LOCK_SCOPE.SHARED && options?.app === l.app) {
|
|
201
191
|
continue;
|
|
202
192
|
}
|
|
203
|
-
if (options?.userId === l.owner.id && (!l.
|
|
193
|
+
if (options?.userId === l.owner.id && (!l.options || options?.lockTokens?.length && options.lockTokens.indexOf(l.options.lockToken) > -1)) {
|
|
204
194
|
continue;
|
|
205
195
|
}
|
|
206
196
|
const conflict = `conflicting parent lock : ${dbFile.path} -> ${l.dbFilePath} (${l.owner.login})`;
|
|
@@ -209,7 +199,7 @@ let FilesLockManager = class FilesLockManager {
|
|
|
209
199
|
}
|
|
210
200
|
if (depth === _webdav.DEPTH.INFINITY) {
|
|
211
201
|
for await (const l of this.searchChildLocks(dbFile)){
|
|
212
|
-
if (options?.userId === l.owner.id && (!l.
|
|
202
|
+
if (options?.userId === l.owner.id && (!l.options || options?.lockTokens?.length && options.lockTokens.indexOf(l.options.lockToken) > -1)) {
|
|
213
203
|
continue;
|
|
214
204
|
}
|
|
215
205
|
const conflict = `conflicting child lock : ${dbFile.path} -> ${l.dbFilePath} (${l.owner.login})`;
|
|
@@ -235,11 +225,15 @@ let FilesLockManager = class FilesLockManager {
|
|
|
235
225
|
convertLockToFileLockProps(lock) {
|
|
236
226
|
if (!lock) return null;
|
|
237
227
|
return {
|
|
238
|
-
owner: lock
|
|
239
|
-
|
|
240
|
-
|
|
228
|
+
owner: lock.owner,
|
|
229
|
+
app: lock?.app,
|
|
230
|
+
info: lock?.options?.lockInfo,
|
|
231
|
+
isExclusive: lock?.options?.lockScope ? lock?.options?.lockScope === _webdav.LOCK_SCOPE.EXCLUSIVE : true
|
|
241
232
|
};
|
|
242
233
|
}
|
|
234
|
+
genDAVToken() {
|
|
235
|
+
return `${_webdav.LOCK_PREFIX}${_nodecrypto.default.randomUUID()}`;
|
|
236
|
+
}
|
|
243
237
|
async *searchParentLocks(dbFile, options = {}) {
|
|
244
238
|
const props = this.genSuffixKey(dbFile, {
|
|
245
239
|
ignorePath: true
|
|
@@ -275,9 +269,6 @@ let FilesLockManager = class FilesLockManager {
|
|
|
275
269
|
searchKeysByPath(path, props = '*', depth) {
|
|
276
270
|
return this.cache.keys(`${_cache.CACHE_LOCK_PREFIX}*${depth ? `|depth:${depth}` : ''}|path:${path}|${props}`);
|
|
277
271
|
}
|
|
278
|
-
genDAVToken() {
|
|
279
|
-
return `${_webdav.LOCK_PREFIX}${_nodecrypto.default.randomUUID()}`;
|
|
280
|
-
}
|
|
281
272
|
genSuffixKey(dbFile, options = {}) {
|
|
282
273
|
// return -> `depth:infinity|path:code/sync-in|spaceId:1` | `token:xxx|depth:0|path:code/sync-in.ts|ownerId:1`
|
|
283
274
|
// ignorePath -> `spaceId:1`
|
|
@@ -299,16 +290,17 @@ let FilesLockManager = class FilesLockManager {
|
|
|
299
290
|
constructor(cache){
|
|
300
291
|
this.cache = cache;
|
|
301
292
|
/* Philosophy
|
|
302
|
-
Currently this manager only
|
|
293
|
+
Currently this manager only handles conflicting locks between multiple users, not between clients
|
|
303
294
|
- The locks created from api
|
|
304
295
|
* They do not contain a token key or a davLock property
|
|
305
296
|
* They are exclusive only
|
|
306
297
|
* They must have a depth of 'infinity' (all children) or '0' (root and root members)
|
|
307
|
-
- The locks created from WebDAV or OnlyOffice are stored with a token and a davLock property
|
|
298
|
+
- The locks created from WebDAV or OnlyOffice or CollaboraOnline are stored with a token and a davLock property
|
|
308
299
|
* They must have a depth of 'infinity' (all children) or '0' (root and root members)
|
|
309
|
-
* They can be exclusive (WebDAV) or shared (OnlyOffice)
|
|
300
|
+
* They can be exclusive (WebDAV) or shared (OnlyOffice, CollaboraOnline)
|
|
310
301
|
* If created with WebDAV, they are stored with a token and a davLock property
|
|
311
|
-
* If created with OnlyOffice, they are stored with no token and a davLock property whose `locktoken` & `lockroot` properties are null
|
|
302
|
+
* If created with OnlyOffice, they are stored with no token and a davLock property whose `locktoken` & `lockroot` properties are `null`
|
|
303
|
+
* If created with CollaboraOnline, they are stored with token and a davLock property whose `lockroot` property is `null`
|
|
312
304
|
Cache token key format = `flock|token?:${uuid}|path:${path}|ownerId?:${number}|spaceId?:${number}|...props` => FileLock
|
|
313
305
|
*/ this.logger = new _common.Logger(FilesLockManager.name);
|
|
314
306
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../backend/src/applications/files/services/files-lock-manager.service.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { Injectable, Logger } from '@nestjs/common'\nimport crypto from 'node:crypto'\n\nimport { currentTimeStamp } from '../../../common/shared'\nimport { Cache } from '../../../infrastructure/cache/services/cache.service'\nimport { UserModel } from '../../users/models/user.model'\nimport { DEPTH, LOCK_DEPTH, LOCK_PREFIX, LOCK_SCOPE } from '../../webdav/constants/webdav'\nimport { WebDAVLock } from '../../webdav/interfaces/webdav.interface'\nimport { CACHE_LOCK_DEFAULT_TTL, CACHE_LOCK_PREFIX } from '../constants/cache'\nimport { FileDBProps } from '../interfaces/file-db-props.interface'\nimport { FileLock } from '../interfaces/file-lock.interface'\nimport { FileLockProps } from '../interfaces/file-props.interface'\nimport { LockConflict } from '../models/file-lock-error'\nimport { files } from '../schemas/files.schema'\nimport { dirName, fileName } from '../utils/files'\n\n@Injectable()\nexport class FilesLockManager {\n /* Philosophy\n Currently this manager only handle conflicting locks between multiple users, not between clients\n - The locks created from api\n * They do not contain a token key or a davLock property\n * They are exclusive only\n * They must have a depth of 'infinity' (all children) or '0' (root and root members)\n - The locks created from WebDAV or OnlyOffice are stored with a token and a davLock property\n * They must have a depth of 'infinity' (all children) or '0' (root and root members)\n * They can be exclusive (WebDAV) or shared (OnlyOffice)\n * If created with WebDAV, they are stored with a token and a davLock property\n * If created with OnlyOffice, they are stored with no token and a davLock property whose `locktoken` & `lockroot` properties are null\n Cache token key format = `flock|token?:${uuid}|path:${path}|ownerId?:${number}|spaceId?:${number}|...props` => FileLock\n */\n private readonly logger = new Logger(FilesLockManager.name)\n\n constructor(private readonly cache: Cache) {}\n\n async create(user: UserModel, dbFile: FileDBProps, depth: LOCK_DEPTH, davLock?: WebDAVLock, ttl?: number): Promise<[boolean, FileLock]> {\n let token: string\n let lockscope: LOCK_SCOPE\n if (davLock) {\n // webdav context\n davLock.locktoken = this.genDAVToken()\n token = davLock.locktoken\n lockscope = davLock.lockscope\n } else {\n // api context\n token = null\n lockscope = null\n }\n try {\n await this.checkConflicts(dbFile, depth, { lockScope: lockscope })\n } catch (e) {\n if (e instanceof LockConflict) {\n return [false, e.lock]\n }\n throw new Error(e)\n }\n ttl ??= CACHE_LOCK_DEFAULT_TTL\n const key = `${CACHE_LOCK_PREFIX}|${this.genSuffixKey(dbFile, { depth: depth, token: token })}`\n const expiration = Math.floor(currentTimeStamp() + ttl)\n const lock: FileLock = {\n owner: user.asOwner(),\n dbFilePath: dbFile.path,\n key: key,\n depth: depth,\n expiration: expiration,\n davLock: davLock\n }\n this.logger.verbose(`${this.create.name} - ${key}`)\n await this.cache.set(key, lock, ttl)\n return [true, lock]\n }\n\n async createOrRefresh(user: UserModel, dbFile: FileDBProps, depth: LOCK_DEPTH, ttl?: number): Promise<[boolean, FileLock]> {\n // Returns: [created, lock]\n ttl ??= CACHE_LOCK_DEFAULT_TTL\n const locks = await this.getLocksByPath(dbFile)\n // Check the existing lock and refresh it if needed\n if (locks.length > 0) {\n for (const lock of locks) {\n if (lock.owner.id === user.id) {\n // Refresh if more than half of the TTL has passed\n const mustRefresh = lock.expiration - currentTimeStamp() < ttl / 2\n if (mustRefresh) {\n this.refreshLockTimeout(lock, ttl).catch((e: Error) => this.logger.error(`${this.createOrRefresh.name} - ${e}`))\n }\n } else {\n throw new LockConflict(lock, 'Conflicting lock')\n }\n }\n return [false, locks[0]]\n }\n // Create the lock\n const [ok, lock] = await this.create(user, dbFile, depth, null, ttl)\n if (!ok) {\n throw new LockConflict(lock, 'Conflicting lock')\n }\n return [true, lock]\n }\n\n removeLock(key: string): Promise<boolean> {\n this.logger.verbose(`${this.removeLock.name} - ${key}`)\n return this.cache.del(key)\n }\n\n async removeChildLocks(user: UserModel, dbFile: FileDBProps) {\n const ownedLockKeys: string[] = []\n for await (const lock of this.searchChildLocks(dbFile)) {\n if (user.id === lock.owner.id) {\n ownedLockKeys.push(lock.key)\n continue\n }\n const conflict = `cannot remove conflicting child lock : ${dbFile.path} (${user.login}) -> ${lock.dbFilePath} (${lock.owner.login})`\n this.logger.debug(`${this.removeChildLocks.name} - ${conflict}`)\n throw new LockConflict(lock, conflict)\n }\n for (const key of ownedLockKeys) {\n this.logger.verbose(`${this.removeChildLocks.name} - child locks: ${key}`)\n await this.removeLock(key)\n }\n }\n\n async isLockedWithToken(token: string, dbFilePath: string): Promise<FileLock> {\n // check if url (or any of its parents) is locked by the token\n const lock = await this.getLockByToken(token)\n return lock ? (dbFilePath.startsWith(lock.dbFilePath) ? lock : null) : null\n }\n\n async browseParentChildLocks(dbFile: FileDBProps, includeRoot = true): Promise<Record<string, FileLock>> {\n // find child locks inside the path\n const childLocks: Record<string, FileLock> = includeRoot ? await this.browseLocks(dbFile) : {}\n const lengthFilter = dbFile.path === '.' ? 1 : dbFile.path.split('/').length + 1\n for await (const lock of this.searchChildLocks(dbFile)) {\n // filter child locks on length\n if (lengthFilter === lock.dbFilePath.split('/').length) {\n // !!! locks with shared scope can have the same file name, in this case we only keep the first entry\n const dbFileName = fileName(lock.dbFilePath)\n if (!(dbFileName in childLocks)) {\n childLocks[fileName(lock.dbFilePath)] = lock\n }\n }\n }\n return childLocks\n }\n\n async browseLocks(dbFile: FileDBProps): Promise<Record<string, FileLock>> {\n return Object.fromEntries((await this.getLocksByPath(dbFile)).map((l) => [fileName(l.dbFilePath), l]))\n }\n\n async refreshLockTimeout(lock: FileLock, ttl: number) {\n lock.expiration = currentTimeStamp() + ttl\n this.cache.set(lock.key, lock, ttl).catch((e: Error) => this.logger.error(`${this.refreshLockTimeout.name} - ${e}`))\n }\n\n async getLockByToken(token: string): Promise<FileLock> {\n const key = await this.searchKeyByToken(token)\n return key ? (await this.cache.get(key)) || null : null\n }\n\n async getLocksByPath(dbFile: FileDBProps): Promise<FileLock[]> {\n if (dbFile.path !== '.') {\n const props = this.genSuffixKey(dbFile, { ignorePath: true })\n const keys = await this.searchKeysByPath(dbFile.path, props)\n if (keys.length) {\n return (await this.cache.mget(keys)).filter(Boolean)\n }\n }\n return []\n }\n\n async isPathLocked(dbFile: FileDBProps): Promise<boolean> {\n if (dbFile.path !== '.') return false\n const props = this.genSuffixKey(dbFile, { ignorePath: true })\n return !!(await this.searchKeysByPath(dbFile.path, props)).length\n }\n\n async checkConflicts(\n dbFile: FileDBProps,\n depth: LOCK_DEPTH,\n options?: { userId?: number; lockScope?: LOCK_SCOPE; lockTokens?: string[] }\n ): Promise<void> {\n /* Checks if a file could be modified, created, moved, or deleted\n Throws an `LockConflict` error when there are parent locks (depth: 0) or child locks (depth: infinite) that prevent modification\n Returns on the first conflict (compliant with the RFC 4918)\n */\n for await (const l of this.searchParentLocks(dbFile, { includeRoot: true, depth: DEPTH.INFINITY })) {\n if (options?.lockScope && options?.lockScope === LOCK_SCOPE.SHARED && l.davLock.lockscope === LOCK_SCOPE.SHARED) {\n // Only compatible with shared locks (even by same owner)\n continue\n }\n if (options?.userId === l.owner.id && (!l.davLock || (options?.lockTokens?.length && options.lockTokens.indexOf(l.davLock.locktoken) > -1))) {\n // Owner owns this lock (no davLock if the lock was created from api)\n continue\n }\n const conflict = `conflicting parent lock : ${dbFile.path} -> ${l.dbFilePath} (${l.owner.login})`\n this.logger.debug(`${this.checkConflicts.name} - ${conflict}`)\n throw new LockConflict(l, conflict)\n }\n if (depth === DEPTH.INFINITY) {\n for await (const l of this.searchChildLocks(dbFile)) {\n if (options?.userId === l.owner.id && (!l.davLock || (options?.lockTokens?.length && options.lockTokens.indexOf(l.davLock.locktoken) > -1))) {\n // Owner owns this lock (no davLock if the lock was created from api)\n continue\n }\n const conflict = `conflicting child lock : ${dbFile.path} -> ${l.dbFilePath} (${l.owner.login})`\n this.logger.debug(`${this.checkConflicts.name} - ${conflict}`)\n throw new LockConflict(l, conflict)\n }\n }\n }\n\n async *searchChildLocks(dbFile: FileDBProps) {\n const props = this.genSuffixKey(dbFile, { ignorePath: true })\n const path = dbFile.path === '.' ? '*' : `${dbFile.path}/*`\n this.logger.verbose(`${this.searchChildLocks.name} - ${path} (${props})`)\n const keys = await this.searchKeysByPath(path, props)\n if (keys.length) {\n for (const l of (await this.cache.mget(keys)).filter(Boolean) as FileLock[]) {\n this.logger.verbose(`-> ${l.dbFilePath} (owner: ${l.owner.login})`)\n yield l\n }\n }\n }\n\n convertLockToFileLockProps(lock: FileLock): FileLockProps {\n if (!lock) return null\n return {\n owner: lock?.davLock?.owner || `${lock.owner.fullName} (${lock.owner.email})`,\n ownerLogin: lock.owner.login,\n isExclusive: lock?.davLock?.lockscope ? lock?.davLock?.lockscope === LOCK_SCOPE.EXCLUSIVE : true\n }\n }\n\n private async *searchParentLocks(dbFile: FileDBProps, options: { includeRoot?: boolean; depth?: LOCK_DEPTH } = {}): AsyncGenerator<FileLock> {\n const props = this.genSuffixKey(dbFile, { ignorePath: true })\n let path = dbFile.path\n const parentPath = dirName(path)\n this.logger.verbose(`${this.searchParentLocks.name} - (including root : ${options.includeRoot}) : ${path} (${props})`)\n if (!options.includeRoot) {\n path = dirName(path)\n }\n while (path !== '.') {\n // Even if the depth is \"infinite\", the path and the parent path could be locked with depth \"0\"\n const depth = options.depth && (dbFile.path === path || parentPath === path) ? null : options.depth\n const keys = await this.searchKeysByPath(path, props, depth)\n if (keys.length) {\n for (const l of (await this.cache.mget(keys)).filter(Boolean) as FileLock[]) {\n this.logger.verbose(`-> ${l.dbFilePath}`)\n yield l\n }\n }\n path = dirName(path)\n }\n }\n\n private async searchKeyByToken(token: string): Promise<string> {\n const keys = await this.cache.keys(`${CACHE_LOCK_PREFIX}|token:${token}|*`)\n if (!keys.length) {\n return null\n } else if (keys.length > 1) {\n this.logger.warn(`Several keys found for token : ${token} => ${JSON.stringify(keys)}`)\n }\n return keys[0]\n }\n\n private searchKeysByPath(path: string, props = '*', depth?: LOCK_DEPTH): Promise<string[]> {\n return this.cache.keys(`${CACHE_LOCK_PREFIX}*${depth ? `|depth:${depth}` : ''}|path:${path}|${props}`)\n }\n\n private genDAVToken(): string {\n return `${LOCK_PREFIX}${crypto.randomUUID()}`\n }\n\n private genSuffixKey(dbFile: FileDBProps, options: { ignorePath?: boolean; depth?: LOCK_DEPTH; token?: string } = {}): string {\n // return -> `depth:infinity|path:code/sync-in|spaceId:1` | `token:xxx|depth:0|path:code/sync-in.ts|ownerId:1`\n // ignorePath -> `spaceId:1`\n const suffixes = []\n for (const k of Object.keys(dbFile)\n .filter((k) => k !== files.inTrash.name && dbFile[k] !== null)\n .sort()) {\n if (k === files.path.name) {\n if (options.ignorePath) {\n continue\n }\n suffixes.unshift(`${k}:${dbFile[k]}`)\n } else {\n suffixes.push(`${k}:${dbFile[k]}`)\n }\n }\n if (options.depth) suffixes.unshift(`depth:${options.depth}`)\n if (options.token) suffixes.unshift(`token:${options.token}`)\n return suffixes.join('|')\n }\n}\n"],"names":["FilesLockManager","create","user","dbFile","depth","davLock","ttl","token","lockscope","locktoken","genDAVToken","checkConflicts","lockScope","e","LockConflict","lock","Error","CACHE_LOCK_DEFAULT_TTL","key","CACHE_LOCK_PREFIX","genSuffixKey","expiration","Math","floor","currentTimeStamp","owner","asOwner","dbFilePath","path","logger","verbose","name","cache","set","createOrRefresh","locks","getLocksByPath","length","id","mustRefresh","refreshLockTimeout","catch","error","ok","removeLock","del","removeChildLocks","ownedLockKeys","searchChildLocks","push","conflict","login","debug","isLockedWithToken","getLockByToken","startsWith","browseParentChildLocks","includeRoot","childLocks","browseLocks","lengthFilter","split","dbFileName","fileName","Object","fromEntries","map","l","searchKeyByToken","get","props","ignorePath","keys","searchKeysByPath","mget","filter","Boolean","isPathLocked","options","searchParentLocks","DEPTH","INFINITY","LOCK_SCOPE","SHARED","userId","lockTokens","indexOf","convertLockToFileLockProps","fullName","email","ownerLogin","isExclusive","EXCLUSIVE","parentPath","dirName","warn","JSON","stringify","LOCK_PREFIX","crypto","randomUUID","suffixes","k","files","inTrash","sort","unshift","join","Logger"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../backend/src/applications/files/services/files-lock-manager.service.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { Injectable, Logger } from '@nestjs/common'\nimport crypto from 'node:crypto'\n\nimport { currentTimeStamp } from '../../../common/shared'\nimport { Cache } from '../../../infrastructure/cache/services/cache.service'\nimport { UserModel } from '../../users/models/user.model'\nimport { DEPTH, LOCK_DEPTH, LOCK_PREFIX, LOCK_SCOPE } from '../../webdav/constants/webdav'\nimport { CACHE_LOCK_DEFAULT_TTL, CACHE_LOCK_PREFIX } from '../constants/cache'\nimport { FileDBProps } from '../interfaces/file-db-props.interface'\nimport { FileLock, FileLockOptions, LOCK_APP } from '../interfaces/file-lock.interface'\nimport { FileLockProps } from '../interfaces/file-props.interface'\nimport { LockConflict } from '../models/file-lock-error'\nimport { files } from '../schemas/files.schema'\nimport { dirName, fileName } from '../utils/files'\n\n@Injectable()\nexport class FilesLockManager {\n /* Philosophy\n Currently this manager only handles conflicting locks between multiple users, not between clients\n - The locks created from api\n * They do not contain a token key or a davLock property\n * They are exclusive only\n * They must have a depth of 'infinity' (all children) or '0' (root and root members)\n - The locks created from WebDAV or OnlyOffice or CollaboraOnline are stored with a token and a davLock property\n * They must have a depth of 'infinity' (all children) or '0' (root and root members)\n * They can be exclusive (WebDAV) or shared (OnlyOffice, CollaboraOnline)\n * If created with WebDAV, they are stored with a token and a davLock property\n * If created with OnlyOffice, they are stored with no token and a davLock property whose `locktoken` & `lockroot` properties are `null`\n * If created with CollaboraOnline, they are stored with token and a davLock property whose `lockroot` property is `null`\n Cache token key format = `flock|token?:${uuid}|path:${path}|ownerId?:${number}|spaceId?:${number}|...props` => FileLock\n */\n private readonly logger = new Logger(FilesLockManager.name)\n\n constructor(private readonly cache: Cache) {}\n\n async create(\n user: UserModel,\n dbFile: FileDBProps,\n app: LOCK_APP,\n depth: LOCK_DEPTH,\n options?: FileLockOptions,\n ttl?: number\n ): Promise<[boolean, FileLock]> {\n try {\n await this.checkConflicts(dbFile, depth, { app: app, lockScope: options?.lockScope })\n } catch (e) {\n if (e instanceof LockConflict) {\n return [false, e.lock]\n }\n throw new Error(e)\n }\n ttl ??= CACHE_LOCK_DEFAULT_TTL\n const key = `${CACHE_LOCK_PREFIX}|${this.genSuffixKey(dbFile, { depth: depth, token: options?.lockToken })}`\n const expiration = Math.floor(currentTimeStamp() + ttl)\n const lock: FileLock = {\n owner: user.asOwner(),\n dbFilePath: dbFile.path,\n key: key,\n app: app,\n depth: depth,\n expiration: expiration,\n options: options\n }\n this.logger.verbose(`${this.create.name} - ${key}`)\n await this.cache.set(key, lock, ttl)\n return [true, lock]\n }\n\n async createOrRefresh(user: UserModel, dbFile: FileDBProps, app: LOCK_APP, depth: LOCK_DEPTH, ttl?: number): Promise<[boolean, FileLock]> {\n // Returns: [created, lock]\n ttl ??= CACHE_LOCK_DEFAULT_TTL\n const locks = await this.getLocksByPath(dbFile)\n // Check the existing lock and refresh it if needed\n if (locks.length > 0) {\n for (const lock of locks) {\n if (lock.owner.id === user.id) {\n // Refresh if more than half of the TTL has passed\n const mustRefresh = lock.expiration - currentTimeStamp() < ttl / 2\n if (mustRefresh) {\n this.refreshLockTimeout(lock, ttl).catch((e: Error) => this.logger.error(`${this.createOrRefresh.name} - ${e}`))\n }\n } else {\n throw new LockConflict(lock, 'Conflicting lock')\n }\n }\n return [false, locks[0]]\n }\n // Create the lock\n const [ok, lock] = await this.create(user, dbFile, app, depth, null, ttl)\n if (!ok) {\n throw new LockConflict(lock, 'Conflicting lock')\n }\n return [true, lock]\n }\n\n removeLock(key: string): Promise<boolean> {\n this.logger.verbose(`${this.removeLock.name} - ${key}`)\n return this.cache.del(key)\n }\n\n async removeChildLocks(user: UserModel, dbFile: FileDBProps) {\n const ownedLockKeys: string[] = []\n for await (const lock of this.searchChildLocks(dbFile)) {\n if (user.id === lock.owner.id) {\n ownedLockKeys.push(lock.key)\n continue\n }\n const conflict = `cannot remove conflicting child lock : ${dbFile.path} (${user.login}) -> ${lock.dbFilePath} (${lock.owner.login})`\n this.logger.debug(`${this.removeChildLocks.name} - ${conflict}`)\n throw new LockConflict(lock, conflict)\n }\n for (const key of ownedLockKeys) {\n this.logger.verbose(`${this.removeChildLocks.name} - child locks: ${key}`)\n await this.removeLock(key)\n }\n }\n\n async isLockedWithToken(token: string, dbFilePath: string): Promise<FileLock> {\n // check if the url (or any of its parents) is locked by the token\n const lock = await this.getLockByToken(token)\n return lock ? (dbFilePath.startsWith(lock.dbFilePath) ? lock : null) : null\n }\n\n async browseParentChildLocks(dbFile: FileDBProps, includeRoot = true): Promise<Record<string, FileLock>> {\n // find child locks inside the path\n const childLocks: Record<string, FileLock> = includeRoot ? await this.browseLocks(dbFile) : {}\n const lengthFilter = dbFile.path === '.' ? 1 : dbFile.path.split('/').length + 1\n for await (const lock of this.searchChildLocks(dbFile)) {\n // filter child locks on length\n if (lengthFilter === lock.dbFilePath.split('/').length) {\n // !!! locks with shared scope can have the same file name, in this case we only keep the first entry\n const dbFileName = fileName(lock.dbFilePath)\n if (!(dbFileName in childLocks)) {\n childLocks[fileName(lock.dbFilePath)] = lock\n }\n }\n }\n return childLocks\n }\n\n async browseLocks(dbFile: FileDBProps): Promise<Record<string, FileLock>> {\n return Object.fromEntries((await this.getLocksByPath(dbFile)).map((l) => [fileName(l.dbFilePath), l]))\n }\n\n async refreshLockTimeout(lock: FileLock, ttl: number) {\n lock.expiration = currentTimeStamp() + ttl\n this.cache.set(lock.key, lock, ttl).catch((e: Error) => this.logger.error(`${this.refreshLockTimeout.name} - ${e}`))\n }\n\n async getLockByToken(token: string): Promise<FileLock> {\n const key = await this.searchKeyByToken(token)\n return key ? (await this.cache.get(key)) || null : null\n }\n\n async getLocksByPath(dbFile: FileDBProps): Promise<FileLock[]> {\n if (dbFile.path !== '.') {\n const props = this.genSuffixKey(dbFile, { ignorePath: true })\n const keys = await this.searchKeysByPath(dbFile.path, props)\n if (keys.length) {\n return (await this.cache.mget(keys)).filter(Boolean)\n }\n }\n return []\n }\n\n async isPathLocked(dbFile: FileDBProps): Promise<boolean> {\n if (dbFile.path !== '.') return false\n const props = this.genSuffixKey(dbFile, { ignorePath: true })\n return !!(await this.searchKeysByPath(dbFile.path, props)).length\n }\n\n async checkConflicts(\n dbFile: FileDBProps,\n depth: LOCK_DEPTH,\n options?: { userId?: number; app?: LOCK_APP; lockScope?: LOCK_SCOPE; lockTokens?: string[] }\n ): Promise<void> {\n /* Checks if a file could be modified, created, moved, or deleted\n Throws a `LockConflict` error when there are parent locks (depth: 0) or child locks (depth: infinite) that prevent modification\n Returns on the first conflict (compliant with the RFC 4918)\n */\n for await (const l of this.searchParentLocks(dbFile, { includeRoot: true, depth: DEPTH.INFINITY })) {\n if (options?.lockScope && options?.lockScope === LOCK_SCOPE.SHARED && l.options.lockScope === LOCK_SCOPE.SHARED && options?.app === l.app) {\n // Only compatible with shared locks (with the same owner and the same application)\n continue\n }\n if (options?.userId === l.owner.id && (!l.options || (options?.lockTokens?.length && options.lockTokens.indexOf(l.options.lockToken) > -1))) {\n // Owner owns this lock (no davLock if the lock was created from api)\n continue\n }\n const conflict = `conflicting parent lock : ${dbFile.path} -> ${l.dbFilePath} (${l.owner.login})`\n this.logger.debug(`${this.checkConflicts.name} - ${conflict}`)\n throw new LockConflict(l, conflict)\n }\n if (depth === DEPTH.INFINITY) {\n for await (const l of this.searchChildLocks(dbFile)) {\n if (options?.userId === l.owner.id && (!l.options || (options?.lockTokens?.length && options.lockTokens.indexOf(l.options.lockToken) > -1))) {\n // Owner owns this lock (no davLock if the lock was created from api)\n continue\n }\n const conflict = `conflicting child lock : ${dbFile.path} -> ${l.dbFilePath} (${l.owner.login})`\n this.logger.debug(`${this.checkConflicts.name} - ${conflict}`)\n throw new LockConflict(l, conflict)\n }\n }\n }\n\n async *searchChildLocks(dbFile: FileDBProps) {\n const props = this.genSuffixKey(dbFile, { ignorePath: true })\n const path = dbFile.path === '.' ? '*' : `${dbFile.path}/*`\n this.logger.verbose(`${this.searchChildLocks.name} - ${path} (${props})`)\n const keys = await this.searchKeysByPath(path, props)\n if (keys.length) {\n for (const l of (await this.cache.mget(keys)).filter(Boolean) as FileLock[]) {\n this.logger.verbose(`-> ${l.dbFilePath} (owner: ${l.owner.login})`)\n yield l\n }\n }\n }\n\n convertLockToFileLockProps(lock: FileLock): FileLockProps {\n if (!lock) return null\n return {\n owner: lock.owner,\n app: lock?.app,\n info: lock?.options?.lockInfo,\n isExclusive: lock?.options?.lockScope ? lock?.options?.lockScope === LOCK_SCOPE.EXCLUSIVE : true\n }\n }\n\n genDAVToken(): string {\n return `${LOCK_PREFIX}${crypto.randomUUID()}`\n }\n\n private async *searchParentLocks(dbFile: FileDBProps, options: { includeRoot?: boolean; depth?: LOCK_DEPTH } = {}): AsyncGenerator<FileLock> {\n const props = this.genSuffixKey(dbFile, { ignorePath: true })\n let path = dbFile.path\n const parentPath = dirName(path)\n this.logger.verbose(`${this.searchParentLocks.name} - (including root : ${options.includeRoot}) : ${path} (${props})`)\n if (!options.includeRoot) {\n path = dirName(path)\n }\n while (path !== '.') {\n // Even if the depth is \"infinite\", the path and the parent path could be locked with depth \"0\"\n const depth = options.depth && (dbFile.path === path || parentPath === path) ? null : options.depth\n const keys = await this.searchKeysByPath(path, props, depth)\n if (keys.length) {\n for (const l of (await this.cache.mget(keys)).filter(Boolean) as FileLock[]) {\n this.logger.verbose(`-> ${l.dbFilePath}`)\n yield l\n }\n }\n path = dirName(path)\n }\n }\n\n private async searchKeyByToken(token: string): Promise<string> {\n const keys = await this.cache.keys(`${CACHE_LOCK_PREFIX}|token:${token}|*`)\n if (!keys.length) {\n return null\n } else if (keys.length > 1) {\n this.logger.warn(`Several keys found for token : ${token} => ${JSON.stringify(keys)}`)\n }\n return keys[0]\n }\n\n private searchKeysByPath(path: string, props = '*', depth?: LOCK_DEPTH): Promise<string[]> {\n return this.cache.keys(`${CACHE_LOCK_PREFIX}*${depth ? `|depth:${depth}` : ''}|path:${path}|${props}`)\n }\n\n private genSuffixKey(dbFile: FileDBProps, options: { ignorePath?: boolean; depth?: LOCK_DEPTH; token?: string } = {}): string {\n // return -> `depth:infinity|path:code/sync-in|spaceId:1` | `token:xxx|depth:0|path:code/sync-in.ts|ownerId:1`\n // ignorePath -> `spaceId:1`\n const suffixes = []\n for (const k of Object.keys(dbFile)\n .filter((k) => k !== files.inTrash.name && dbFile[k] !== null)\n .sort()) {\n if (k === files.path.name) {\n if (options.ignorePath) {\n continue\n }\n suffixes.unshift(`${k}:${dbFile[k]}`)\n } else {\n suffixes.push(`${k}:${dbFile[k]}`)\n }\n }\n if (options.depth) suffixes.unshift(`depth:${options.depth}`)\n if (options.token) suffixes.unshift(`token:${options.token}`)\n return suffixes.join('|')\n }\n}\n"],"names":["FilesLockManager","create","user","dbFile","app","depth","options","ttl","checkConflicts","lockScope","e","LockConflict","lock","Error","CACHE_LOCK_DEFAULT_TTL","key","CACHE_LOCK_PREFIX","genSuffixKey","token","lockToken","expiration","Math","floor","currentTimeStamp","owner","asOwner","dbFilePath","path","logger","verbose","name","cache","set","createOrRefresh","locks","getLocksByPath","length","id","mustRefresh","refreshLockTimeout","catch","error","ok","removeLock","del","removeChildLocks","ownedLockKeys","searchChildLocks","push","conflict","login","debug","isLockedWithToken","getLockByToken","startsWith","browseParentChildLocks","includeRoot","childLocks","browseLocks","lengthFilter","split","dbFileName","fileName","Object","fromEntries","map","l","searchKeyByToken","get","props","ignorePath","keys","searchKeysByPath","mget","filter","Boolean","isPathLocked","searchParentLocks","DEPTH","INFINITY","LOCK_SCOPE","SHARED","userId","lockTokens","indexOf","convertLockToFileLockProps","info","lockInfo","isExclusive","EXCLUSIVE","genDAVToken","LOCK_PREFIX","crypto","randomUUID","parentPath","dirName","warn","JSON","stringify","suffixes","k","files","inTrash","sort","unshift","join","Logger"],"mappings":"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"}
|
|
@@ -19,6 +19,7 @@ const _nodefs = /*#__PURE__*/ _interop_require_default(require("node:fs"));
|
|
|
19
19
|
const _nodepath = /*#__PURE__*/ _interop_require_default(require("node:path"));
|
|
20
20
|
const _tar = require("tar");
|
|
21
21
|
const _image = require("../../../common/image");
|
|
22
|
+
const _shared = require("../../../common/shared");
|
|
22
23
|
const _contextmanagerservice = require("../../../infrastructure/context/services/context-manager.service");
|
|
23
24
|
const _applicationsconstants = require("../../applications.constants");
|
|
24
25
|
const _notifications = require("../../notifications/constants/notifications");
|
|
@@ -61,8 +62,8 @@ let FilesManager = class FilesManager {
|
|
|
61
62
|
return new _sendfile.SendFile(space.realPath, downloadName);
|
|
62
63
|
}
|
|
63
64
|
async saveStream(user, space, req, options) {
|
|
64
|
-
//
|
|
65
|
-
// space.realPath is replaced by tmpPath (if allowed)
|
|
65
|
+
// If tmpPath is used, we lock the final destination during the transfer
|
|
66
|
+
// space.realPath is replaced by tmpPath (if allowed). If the move operation failed, we remove the tmp file
|
|
66
67
|
const fExists = await (0, _files1.isPathExists)(space.realPath);
|
|
67
68
|
const fTmpExists = options?.tmpPath ? await (0, _files1.isPathExists)(options.tmpPath) : false;
|
|
68
69
|
if (fExists && req.method === _applicationsconstants.HTTP_METHOD.POST) {
|
|
@@ -72,33 +73,33 @@ let FilesManager = class FilesManager {
|
|
|
72
73
|
throw new _fileerror.FileError(_common.HttpStatus.METHOD_NOT_ALLOWED, 'The location is a directory');
|
|
73
74
|
}
|
|
74
75
|
if (options?.tmpPath) {
|
|
75
|
-
//
|
|
76
|
+
// Ensure tmpPath parent dir exists
|
|
76
77
|
await (0, _files1.makeDir)((0, _files1.dirName)(options.tmpPath), true);
|
|
77
78
|
} else if (!await (0, _files1.isPathExists)((0, _files1.dirName)(space.realPath))) {
|
|
78
79
|
throw new _fileerror.FileError(_common.HttpStatus.CONFLICT, 'Parent must exists');
|
|
79
80
|
}
|
|
80
81
|
/* File Lock */ let fileLock;
|
|
81
82
|
if (options?.dav) {
|
|
82
|
-
//
|
|
83
|
+
// Check locks
|
|
83
84
|
await this.filesLockManager.checkConflicts(space.dbFile, options?.dav?.depth || _webdav.DEPTH.RESOURCE, {
|
|
84
85
|
userId: user.id,
|
|
85
86
|
lockTokens: options.dav?.lockTokens
|
|
86
87
|
});
|
|
87
88
|
} else {
|
|
88
|
-
//
|
|
89
|
-
const [ok, lock] = await this.filesLockManager.create(user, space.dbFile, _webdav.DEPTH.RESOURCE);
|
|
89
|
+
// Create lock if there is no webdav context
|
|
90
|
+
const [ok, lock] = await this.filesLockManager.create(user, space.dbFile, _shared.SERVER_NAME, _webdav.DEPTH.RESOURCE);
|
|
90
91
|
if (!ok) {
|
|
91
92
|
throw new _filelockerror.LockConflict(lock, 'Conflicting lock');
|
|
92
93
|
}
|
|
93
94
|
fileLock = lock;
|
|
94
95
|
}
|
|
95
96
|
try {
|
|
96
|
-
//
|
|
97
|
+
// Check range
|
|
97
98
|
let startRange = 0;
|
|
98
99
|
if ((fExists || fTmpExists) && req.headers['content-range']) {
|
|
99
|
-
//
|
|
100
|
+
// With PUT method, some webdav clients use the `content-range` header,
|
|
100
101
|
// which is normally reserved for a response to a request containing the `range` header.
|
|
101
|
-
// However, for more compatibility let's accept it
|
|
102
|
+
// However, for more compatibility let's accept it.
|
|
102
103
|
const match = /\d+/.exec(req.headers['content-range']);
|
|
103
104
|
if (!match.length) {
|
|
104
105
|
throw new _fileerror.FileError(_common.HttpStatus.BAD_REQUEST, 'Content-range : header is malformed');
|
|
@@ -202,7 +203,7 @@ let FilesManager = class FilesManager {
|
|
|
202
203
|
};
|
|
203
204
|
// Use a short TTL for the PATCH method (which is also used for refreshing)
|
|
204
205
|
const ttl = patch ? _cache.CACHE_LOCK_FILE_TTL : undefined;
|
|
205
|
-
const [created, fileLock] = await this.filesLockManager.createOrRefresh(user, dbFile, _webdav.DEPTH.RESOURCE, ttl);
|
|
206
|
+
const [created, fileLock] = await this.filesLockManager.createOrRefresh(user, dbFile, _shared.SERVER_NAME, _webdav.DEPTH.RESOURCE, ttl);
|
|
206
207
|
// Do
|
|
207
208
|
try {
|
|
208
209
|
await (0, _files1.writeFromStream)(dstFile, part.file);
|
|
@@ -427,7 +428,7 @@ let FilesManager = class FilesManager {
|
|
|
427
428
|
const rPath = await (0, _files1.uniqueFilePathFromDir)(space.realPath);
|
|
428
429
|
const dbFile = space.dbFile;
|
|
429
430
|
dbFile.path = _nodepath.default.join((0, _files1.dirName)(dbFile.path), (0, _files1.fileName)(space.realPath));
|
|
430
|
-
const [ok, fileLock] = await this.filesLockManager.create(user, dbFile, _webdav.DEPTH.RESOURCE);
|
|
431
|
+
const [ok, fileLock] = await this.filesLockManager.create(user, dbFile, _shared.SERVER_NAME, _webdav.DEPTH.RESOURCE);
|
|
431
432
|
if (!ok) {
|
|
432
433
|
throw new _filelockerror.LockConflict(fileLock, 'Conflicting lock');
|
|
433
434
|
}
|
|
@@ -498,7 +499,7 @@ let FilesManager = class FilesManager {
|
|
|
498
499
|
if (dto.compressInDirectory) {
|
|
499
500
|
const dbFile = space.dbFile;
|
|
500
501
|
dbFile.path = _nodepath.default.join((0, _files1.dirName)(dbFile.path), (0, _files1.fileName)(dstPath));
|
|
501
|
-
const [ok, lock] = await this.filesLockManager.create(user, dbFile, _webdav.DEPTH.RESOURCE);
|
|
502
|
+
const [ok, lock] = await this.filesLockManager.create(user, dbFile, _shared.SERVER_NAME, _webdav.DEPTH.RESOURCE);
|
|
502
503
|
if (!ok) {
|
|
503
504
|
throw new _filelockerror.LockConflict(lock, 'Conflicting lock');
|
|
504
505
|
}
|
|
@@ -548,7 +549,7 @@ let FilesManager = class FilesManager {
|
|
|
548
549
|
// create lock
|
|
549
550
|
const dbFile = space.dbFile;
|
|
550
551
|
dbFile.path = _nodepath.default.join((0, _files1.dirName)(dbFile.path), (0, _files1.fileName)(dstPath));
|
|
551
|
-
const [ok, fileLock] = await this.filesLockManager.create(user, dbFile, _webdav.DEPTH.INFINITY);
|
|
552
|
+
const [ok, fileLock] = await this.filesLockManager.create(user, dbFile, _shared.SERVER_NAME, _webdav.DEPTH.INFINITY);
|
|
552
553
|
if (!ok) {
|
|
553
554
|
throw new _filelockerror.LockConflict(fileLock, 'Conflicting lock');
|
|
554
555
|
}
|
|
@@ -590,10 +591,10 @@ let FilesManager = class FilesManager {
|
|
|
590
591
|
this.logger.warn('Lock refresh must specify an existing resource');
|
|
591
592
|
throw new _fileerror.FileError(_common.HttpStatus.BAD_REQUEST, 'Lock refresh must specify an existing resource');
|
|
592
593
|
}
|
|
593
|
-
const [_created, lock] = await this.filesLockManager.createOrRefresh(user, space.dbFile, _webdav.DEPTH.RESOURCE, _cache.CACHE_LOCK_FILE_TTL);
|
|
594
|
+
const [_created, lock] = await this.filesLockManager.createOrRefresh(user, space.dbFile, _shared.SERVER_NAME, _webdav.DEPTH.RESOURCE, _cache.CACHE_LOCK_FILE_TTL);
|
|
594
595
|
return this.filesLockManager.convertLockToFileLockProps(lock);
|
|
595
596
|
}
|
|
596
|
-
async unlock(user, space,
|
|
597
|
+
async unlock(user, space, forceAsFileOwner = false) {
|
|
597
598
|
if (!await (0, _files1.isPathExists)(space.realPath)) {
|
|
598
599
|
this.logger.warn(`Unable to unlock: ${space.url} - resource does not exist`);
|
|
599
600
|
throw new _fileerror.FileError(_common.HttpStatus.BAD_REQUEST, 'Unlock must specify an existing resource');
|
|
@@ -604,7 +605,7 @@ let FilesManager = class FilesManager {
|
|
|
604
605
|
return;
|
|
605
606
|
}
|
|
606
607
|
for (const lock of fileLocks){
|
|
607
|
-
if (
|
|
608
|
+
if (forceAsFileOwner && space.dbFile?.ownerId === user.id || lock.owner.id === user.id) {
|
|
608
609
|
// Refresh if more than half of the TTL has passed
|
|
609
610
|
await this.filesLockManager.removeLock(lock.key);
|
|
610
611
|
} else {
|