@sync-in/server 1.8.1 → 1.9.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 +56 -11
- package/environment/environment.dist.yaml +29 -26
- package/package.json +15 -15
- package/server/app.constants.js +9 -0
- package/server/app.constants.js.map +1 -1
- package/server/app.module.js +4 -0
- package/server/app.module.js.map +1 -1
- package/server/applications/admin/admin.module.js +6 -1
- package/server/applications/admin/admin.module.js.map +1 -1
- package/server/applications/{notifications/interfaces/user-mail-notification.js → admin/interfaces/check-update.interfaces.js} +1 -1
- package/server/applications/admin/interfaces/check-update.interfaces.js.map +1 -0
- package/server/applications/admin/services/admin-scheduler.service.js +53 -0
- package/server/applications/admin/services/admin-scheduler.service.js.map +1 -0
- package/server/applications/admin/services/admin.service.js +102 -0
- package/server/applications/admin/services/admin.service.js.map +1 -0
- package/server/applications/admin/services/admin.service.spec.js +46 -0
- package/server/applications/admin/services/admin.service.spec.js.map +1 -0
- package/server/applications/admin/utils/check-update.js +28 -0
- package/server/applications/admin/utils/check-update.js.map +1 -0
- package/server/applications/comments/services/comments-manager.service.js +1 -1
- package/server/applications/comments/services/comments-manager.service.js.map +1 -1
- package/server/applications/comments/services/comments-manager.service.spec.js +1 -1
- package/server/applications/comments/services/comments-manager.service.spec.js.map +1 -1
- package/server/applications/comments/services/comments-queries.service.js.map +1 -1
- package/server/applications/files/constants/cache.js +10 -1
- package/server/applications/files/constants/cache.js.map +1 -1
- package/server/applications/files/constants/only-office.js +0 -25
- package/server/applications/files/constants/only-office.js.map +1 -1
- package/server/applications/files/constants/operations.js +20 -3
- package/server/applications/files/constants/operations.js.map +1 -1
- package/server/applications/files/dto/file-operations.dto.js +7 -0
- package/server/applications/files/dto/file-operations.dto.js.map +1 -1
- package/server/applications/files/files-only-office.controller.js +3 -2
- package/server/applications/files/files-only-office.controller.js.map +1 -1
- package/server/applications/files/files-only-office.controller.spec.js +6 -5
- package/server/applications/files/files-only-office.controller.spec.js.map +1 -1
- package/server/applications/files/files.controller.js +88 -8
- package/server/applications/files/files.controller.js.map +1 -1
- package/server/applications/files/files.controller.spec.js +11 -4
- package/server/applications/files/files.controller.spec.js.map +1 -1
- package/server/applications/files/guards/files-only-office.guard.js +6 -0
- package/server/applications/files/guards/files-only-office.guard.js.map +1 -1
- package/server/applications/files/interfaces/file-db-props.interface.js.map +1 -1
- package/server/applications/files/interfaces/file-props.interface.js.map +1 -1
- package/server/applications/files/interfaces/only-office-config.interface.js.map +1 -1
- package/server/applications/files/services/files-content-manager.service.js +3 -3
- package/server/applications/files/services/files-content-manager.service.js.map +1 -1
- package/server/applications/files/services/files-lock-manager.service.js +61 -22
- package/server/applications/files/services/files-lock-manager.service.js.map +1 -1
- package/server/applications/files/services/files-manager.service.js +122 -24
- package/server/applications/files/services/files-manager.service.js.map +1 -1
- package/server/applications/files/services/files-manager.service.spec.js +15 -0
- package/server/applications/files/services/files-manager.service.spec.js.map +1 -1
- package/server/applications/files/services/files-methods.service.js +38 -8
- package/server/applications/files/services/files-methods.service.js.map +1 -1
- package/server/applications/files/services/files-only-office-manager.service.js +12 -12
- package/server/applications/files/services/files-only-office-manager.service.js.map +1 -1
- package/server/applications/files/services/files-parser.service.js +1 -1
- package/server/applications/files/services/files-parser.service.js.map +1 -1
- package/server/applications/files/services/files-queries.service.js +10 -0
- package/server/applications/files/services/files-queries.service.js.map +1 -1
- package/server/applications/files/utils/files.js +1 -1
- package/server/applications/files/utils/files.js.map +1 -1
- package/server/applications/files/utils/send-file.js +3 -2
- package/server/applications/files/utils/send-file.js.map +1 -1
- package/server/applications/links/services/links-queries.service.js +1 -1
- package/server/applications/links/services/links-queries.service.js.map +1 -1
- package/server/applications/notifications/constants/notifications.js +5 -1
- package/server/applications/notifications/constants/notifications.js.map +1 -1
- package/server/applications/notifications/i18n/de.js +8 -2
- package/server/applications/notifications/i18n/de.js.map +1 -1
- package/server/applications/notifications/i18n/es.js +8 -2
- package/server/applications/notifications/i18n/es.js.map +1 -1
- package/server/applications/notifications/i18n/fr.js +8 -2
- package/server/applications/notifications/i18n/fr.js.map +1 -1
- package/server/applications/notifications/i18n/hi.js +8 -2
- package/server/applications/notifications/i18n/hi.js.map +1 -1
- package/server/applications/notifications/i18n/it.js +8 -2
- package/server/applications/notifications/i18n/it.js.map +1 -1
- package/server/applications/notifications/i18n/ja.js +8 -2
- package/server/applications/notifications/i18n/ja.js.map +1 -1
- package/server/applications/notifications/i18n/ko.js +8 -2
- package/server/applications/notifications/i18n/ko.js.map +1 -1
- package/server/applications/notifications/i18n/pl.js +8 -2
- package/server/applications/notifications/i18n/pl.js.map +1 -1
- package/server/applications/notifications/i18n/pt.js +8 -2
- package/server/applications/notifications/i18n/pt.js.map +1 -1
- package/server/applications/notifications/i18n/pt_br.js +8 -2
- package/server/applications/notifications/i18n/pt_br.js.map +1 -1
- package/server/applications/notifications/i18n/ru.js +8 -2
- package/server/applications/notifications/i18n/ru.js.map +1 -1
- package/server/applications/notifications/i18n/tr.js +8 -2
- package/server/applications/notifications/i18n/tr.js.map +1 -1
- package/server/applications/notifications/i18n/zh.js +8 -2
- package/server/applications/notifications/i18n/zh.js.map +1 -1
- package/server/applications/notifications/interfaces/notification-properties.interface.js.map +1 -1
- package/server/applications/notifications/interfaces/user-mail-notification.interface.js +10 -0
- package/server/applications/notifications/interfaces/user-mail-notification.interface.js.map +1 -0
- package/server/applications/notifications/mails/models.js +38 -3
- package/server/applications/notifications/mails/models.js.map +1 -1
- package/server/applications/notifications/mails/templates.js +1 -1
- package/server/applications/notifications/mails/templates.js.map +1 -1
- package/server/applications/notifications/services/notifications-manager.service.js +8 -1
- package/server/applications/notifications/services/notifications-manager.service.js.map +1 -1
- package/server/applications/notifications/services/notifications-queries.service.js.map +1 -1
- package/server/applications/shares/services/shares-manager.service.js +17 -10
- package/server/applications/shares/services/shares-manager.service.js.map +1 -1
- package/server/applications/shares/services/shares-manager.service.spec.js +10 -3
- package/server/applications/shares/services/shares-manager.service.spec.js.map +1 -1
- package/server/applications/shares/services/shares-queries.service.js +11 -1
- package/server/applications/shares/services/shares-queries.service.js.map +1 -1
- package/server/applications/spaces/guards/space.guard.spec.js +2 -2
- package/server/applications/spaces/guards/space.guard.spec.js.map +1 -1
- package/server/applications/spaces/services/spaces-browser.service.js +31 -11
- package/server/applications/spaces/services/spaces-browser.service.js.map +1 -1
- package/server/applications/spaces/services/spaces-manager.service.js +2 -2
- package/server/applications/spaces/services/spaces-manager.service.js.map +1 -1
- package/server/applications/spaces/services/spaces-queries.service.js +6 -2
- package/server/applications/spaces/services/spaces-queries.service.js.map +1 -1
- package/server/applications/spaces/utils/permissions.js +2 -2
- package/server/applications/spaces/utils/permissions.js.map +1 -1
- package/server/applications/sync/services/sync-manager.service.js +1 -0
- package/server/applications/sync/services/sync-manager.service.js.map +1 -1
- package/server/applications/sync/services/sync-paths-manager.service.js +1 -1
- package/server/applications/sync/services/sync-paths-manager.service.js.map +1 -1
- package/server/applications/sync/services/sync-paths-manager.service.spec.js +1 -1
- package/server/applications/sync/services/sync-paths-manager.service.spec.js.map +1 -1
- package/server/applications/users/constants/user.js +1 -1
- package/server/applications/users/constants/user.js.map +1 -1
- package/server/applications/users/services/admin-users-queries.service.js +8 -0
- package/server/applications/users/services/admin-users-queries.service.js.map +1 -1
- package/server/applications/users/services/users-queries.service.js +67 -68
- package/server/applications/users/services/users-queries.service.js.map +1 -1
- package/server/applications/users/users.module.js +2 -1
- package/server/applications/users/users.module.js.map +1 -1
- package/server/applications/webdav/guards/webdav-protocol.guard.js +4 -4
- package/server/applications/webdav/guards/webdav-protocol.guard.js.map +1 -1
- package/server/applications/webdav/guards/webdav-protocol.guard.spec.js +6 -6
- package/server/applications/webdav/guards/webdav-protocol.guard.spec.js.map +1 -1
- package/server/applications/webdav/services/webdav-methods.service.js +3 -2
- package/server/applications/webdav/services/webdav-methods.service.js.map +1 -1
- package/server/applications/webdav/services/webdav-methods.service.spec.js +2 -2
- package/server/applications/webdav/services/webdav-methods.service.spec.js.map +1 -1
- package/server/authentication/constants/auth-ldap.js +2 -0
- package/server/authentication/constants/auth-ldap.js.map +1 -1
- package/server/authentication/services/auth-methods/auth-method-ldap.service.js +34 -21
- package/server/authentication/services/auth-methods/auth-method-ldap.service.js.map +1 -1
- package/server/common/functions.js +0 -8
- package/server/common/functions.js.map +1 -1
- package/server/common/image.js +1 -0
- package/server/common/image.js.map +1 -1
- package/server/common/shared.js +18 -1
- package/server/common/shared.js.map +1 -1
- package/server/infrastructure/context/services/context-manager.service.js +3 -0
- package/server/infrastructure/context/services/context-manager.service.js.map +1 -1
- package/server/infrastructure/websocket/adapters/cluster.adapter.js +4 -4
- package/server/infrastructure/websocket/adapters/cluster.adapter.js.map +1 -1
- package/static/3rdpartylicenses.txt +1393 -44
- package/static/assets/favicon.svg +2 -25
- package/static/assets/logo-dark.svg +2 -32
- package/static/assets/logo.svg +2 -32
- package/static/assets/mimes/application-sql.svg +29 -1
- package/static/assets/mimes/application-x-sql.svg +29 -0
- package/static/assets/mimes/image-bmp.svg +12 -0
- package/static/assets/pdfjs/build/pdf.mjs +4869 -4454
- 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 +885 -596
- package/static/assets/pdfjs/build/pdf.worker.mjs.map +1 -1
- package/static/assets/pdfjs/version +1 -1
- package/static/assets/pdfjs/web/locale/be/viewer.ftl +40 -0
- package/static/assets/pdfjs/web/locale/bg/viewer.ftl +2 -0
- package/static/assets/pdfjs/web/locale/cs/viewer.ftl +41 -0
- package/static/assets/pdfjs/web/locale/cy/viewer.ftl +43 -0
- package/static/assets/pdfjs/web/locale/da/viewer.ftl +39 -0
- package/static/assets/pdfjs/web/locale/de/viewer.ftl +41 -0
- package/static/assets/pdfjs/web/locale/dsb/viewer.ftl +43 -0
- package/static/assets/pdfjs/web/locale/el/viewer.ftl +39 -0
- package/static/assets/pdfjs/web/locale/en-CA/viewer.ftl +35 -0
- package/static/assets/pdfjs/web/locale/en-GB/viewer.ftl +41 -0
- package/static/assets/pdfjs/web/locale/en-US/viewer.ftl +2 -2
- package/static/assets/pdfjs/web/locale/eo/viewer.ftl +39 -0
- package/static/assets/pdfjs/web/locale/es-AR/viewer.ftl +41 -0
- package/static/assets/pdfjs/web/locale/es-CL/viewer.ftl +39 -0
- package/static/assets/pdfjs/web/locale/es-ES/viewer.ftl +72 -0
- package/static/assets/pdfjs/web/locale/es-MX/viewer.ftl +120 -0
- package/static/assets/pdfjs/web/locale/eu/viewer.ftl +41 -0
- package/static/assets/pdfjs/web/locale/fi/viewer.ftl +41 -0
- package/static/assets/pdfjs/web/locale/fr/viewer.ftl +41 -0
- package/static/assets/pdfjs/web/locale/fur/viewer.ftl +77 -0
- package/static/assets/pdfjs/web/locale/fy-NL/viewer.ftl +41 -0
- package/static/assets/pdfjs/web/locale/gn/viewer.ftl +39 -0
- package/static/assets/pdfjs/web/locale/he/viewer.ftl +41 -0
- package/static/assets/pdfjs/web/locale/hsb/viewer.ftl +43 -0
- package/static/assets/pdfjs/web/locale/hu/viewer.ftl +41 -0
- package/static/assets/pdfjs/web/locale/hy-AM/viewer.ftl +1 -1
- package/static/assets/pdfjs/web/locale/ia/viewer.ftl +41 -0
- package/static/assets/pdfjs/web/locale/it/viewer.ftl +41 -0
- package/static/assets/pdfjs/web/locale/ja/viewer.ftl +32 -0
- package/static/assets/pdfjs/web/locale/ka/viewer.ftl +41 -0
- package/static/assets/pdfjs/web/locale/kab/viewer.ftl +73 -0
- package/static/assets/pdfjs/web/locale/kk/viewer.ftl +21 -0
- package/static/assets/pdfjs/web/locale/ko/viewer.ftl +38 -1
- package/static/assets/pdfjs/web/locale/nb-NO/viewer.ftl +39 -0
- package/static/assets/pdfjs/web/locale/nl/viewer.ftl +41 -0
- package/static/assets/pdfjs/web/locale/nn-NO/viewer.ftl +37 -0
- package/static/assets/pdfjs/web/locale/pa-IN/viewer.ftl +37 -0
- package/static/assets/pdfjs/web/locale/pl/viewer.ftl +39 -0
- package/static/assets/pdfjs/web/locale/pt-BR/viewer.ftl +39 -0
- package/static/assets/pdfjs/web/locale/rm/viewer.ftl +73 -0
- package/static/assets/pdfjs/web/locale/ro/viewer.ftl +41 -0
- package/static/assets/pdfjs/web/locale/ru/viewer.ftl +42 -0
- package/static/assets/pdfjs/web/locale/sc/viewer.ftl +6 -0
- package/static/assets/pdfjs/web/locale/sk/viewer.ftl +43 -0
- package/static/assets/pdfjs/web/locale/sl/viewer.ftl +42 -1
- package/static/assets/pdfjs/web/locale/sq/viewer.ftl +77 -0
- package/static/assets/pdfjs/web/locale/sv-SE/viewer.ftl +41 -0
- package/static/assets/pdfjs/web/locale/tg/viewer.ftl +39 -0
- package/static/assets/pdfjs/web/locale/th/viewer.ftl +35 -0
- package/static/assets/pdfjs/web/locale/tr/viewer.ftl +39 -0
- package/static/assets/pdfjs/web/locale/vi/viewer.ftl +42 -5
- package/static/assets/pdfjs/web/locale/zh-CN/viewer.ftl +35 -0
- package/static/assets/pdfjs/web/locale/zh-TW/viewer.ftl +37 -0
- package/static/assets/pdfjs/web/viewer.css +141 -110
- package/static/assets/pdfjs/web/viewer.html +7 -7
- package/static/assets/pdfjs/web/viewer.mjs +97 -14
- package/static/assets/pdfjs/web/viewer.mjs.map +1 -1
- package/static/chunk-22DWHRCL.js +1 -0
- package/static/chunk-23UUFZSR.js +1 -0
- package/static/{chunk-MTVSJTIW.js → chunk-24Q7OUU2.js} +1 -1
- package/static/chunk-25QTY2GI.js +1 -0
- package/static/chunk-2E7IJZLL.js +1 -0
- package/static/chunk-2FC5EKS5.js +1 -0
- package/static/chunk-2FOWUJQF.js +1 -0
- package/static/chunk-2XPHUNYN.js +1 -0
- package/static/chunk-32L7RG2G.js +1 -0
- package/static/chunk-3IISSX63.js +1 -0
- package/static/chunk-3OHSTP3R.js +1 -0
- package/static/chunk-42L6C5MT.js +1 -0
- package/static/chunk-4AGQL5GV.js +1 -0
- package/static/chunk-4BPSQMI2.js +1 -0
- package/static/chunk-4FDRWZWT.js +1 -0
- package/static/chunk-4FJUCMEG.js +1 -0
- package/static/chunk-4GCCF6PF.js +1 -0
- package/static/chunk-4ORP3SBY.js +1 -0
- package/static/chunk-4QBOHIC3.js +1 -0
- package/static/chunk-4YT6K5KY.js +1 -0
- package/static/chunk-556I6YIW.js +1 -0
- package/static/chunk-5DPIGJU4.js +1 -0
- package/static/chunk-5HNQLBSW.js +3 -0
- package/static/chunk-5IL7C45D.js +1 -0
- package/static/chunk-5KJXGMKR.js +1 -0
- package/static/chunk-5SPGSHKL.js +1 -0
- package/static/chunk-5WCQBTXW.js +1 -0
- package/static/chunk-5XUIPWOH.js +1 -0
- package/static/chunk-62WT7PI3.js +1 -0
- package/static/chunk-6F6OMQ5H.js +1 -0
- package/static/chunk-6I5BGQHT.js +1 -0
- package/static/chunk-6NOS45DG.js +1 -0
- package/static/chunk-6OKLPRCD.js +1 -0
- package/static/chunk-6VEJCG43.js +1 -0
- package/static/chunk-77SS36Z2.js +1 -0
- package/static/chunk-7AXEPO3G.js +1 -0
- package/static/chunk-7CFSJ4BO.js +1 -0
- package/static/chunk-7CKHC72R.js +1 -0
- package/static/chunk-7DUTYOJG.js +1 -0
- package/static/chunk-7HKFYRPF.js +1 -0
- package/static/chunk-7NZJZATZ.js +1 -0
- package/static/chunk-AADK5D2H.js +1 -0
- package/static/chunk-ACUF7IKP.js +1 -0
- package/static/chunk-AGREZPV4.js +1 -0
- package/static/chunk-ATBJWFA3.js +1 -0
- package/static/chunk-ATXLZN2B.js +1 -0
- package/static/chunk-AZ5TF5Y3.js +1 -0
- package/static/chunk-BBHYIURC.js +1 -0
- package/static/chunk-BHZEPHRI.js +13 -0
- package/static/chunk-BQZWSZNN.js +1 -0
- package/static/chunk-BSB4VROD.js +2 -0
- package/static/chunk-BYWSTP3P.js +1 -0
- package/static/chunk-C3AAEQKW.js +1 -0
- package/static/chunk-CCGGCHGN.js +1 -0
- package/static/chunk-CFTKW432.js +1 -0
- package/static/chunk-CNOVT6KU.js +1 -0
- package/static/chunk-D56H3XE2.js +1 -0
- package/static/chunk-DFOOSIIA.js +1 -0
- package/static/chunk-DHFQIFOF.js +1 -0
- package/static/{chunk-3GMLWAFZ.js → chunk-DIC2MVRI.js} +1 -1
- package/static/chunk-DJDRX53V.js +2 -0
- package/static/chunk-DKGXUMLT.js +1 -0
- package/static/chunk-DRHPEERW.js +2 -0
- package/static/chunk-DWYP6ZGG.js +1 -0
- package/static/chunk-EDJAISWO.js +13 -0
- package/static/chunk-EIYRBM4J.js +1 -0
- package/static/chunk-EKEGRXCV.js +7 -0
- package/static/chunk-EL6QL4TP.js +1 -0
- package/static/chunk-ERDZ7IVF.js +1 -0
- package/static/chunk-EVQKKVUZ.js +1 -0
- package/static/chunk-F2XG7EWI.js +1 -0
- package/static/chunk-F672FY5I.js +1 -0
- package/static/chunk-F6V37MKG.js +1 -0
- package/static/chunk-F7TXTNZC.js +1 -0
- package/static/chunk-FCGTI42I.js +1 -0
- package/static/chunk-FQHOSSCO.js +1 -0
- package/static/chunk-FTSIPHMG.js +1 -0
- package/static/chunk-GAGHHYLF.js +1 -0
- package/static/{chunk-JPT5WEAT.js → chunk-GOJYWL2M.js} +1 -1
- package/static/chunk-H6WOTGQ5.js +1 -0
- package/static/{chunk-CHJ64RJM.js → chunk-H6ZXFINQ.js} +1 -1
- package/static/chunk-HC7F57NA.js +1 -0
- package/static/chunk-HHWXIK2M.js +7 -0
- package/static/chunk-HKRGIRKB.js +3 -0
- package/static/chunk-HNMGPG72.js +1 -0
- package/static/chunk-HS4S6BV3.js +1 -0
- package/static/chunk-IJ7K7ATQ.js +1 -0
- package/static/chunk-IOIBQGHN.js +562 -0
- package/static/chunk-ITVA26X2.js +2 -0
- package/static/chunk-J6YSFHLZ.js +1 -0
- package/static/chunk-JAEJ6IMV.js +1 -0
- package/static/chunk-JB5R6V33.js +1 -0
- package/static/chunk-JF6WIV6M.js +1 -0
- package/static/chunk-JGB4LLUT.js +1 -0
- package/static/chunk-JGXVTKLG.js +1 -0
- package/static/chunk-JMYAD7E2.js +1 -0
- package/static/chunk-JSE63Q5X.js +1 -0
- package/static/chunk-JVV3ZL6L.js +1 -0
- package/static/chunk-JXZCNFW7.js +1 -0
- package/static/chunk-KAVP6UXH.js +1 -0
- package/static/{chunk-3R74L4UU.js → chunk-KDEEERWZ.js} +1 -1
- package/static/chunk-KHRF67SG.js +1 -0
- package/static/chunk-KLOUBIO4.js +1 -0
- package/static/chunk-KMF3ZRAO.js +1 -0
- package/static/chunk-KNZ3AQPR.js +1 -0
- package/static/chunk-KT3TWCST.js +1 -0
- package/static/chunk-L6SYG23T.js +1 -0
- package/static/chunk-LJSVNPPQ.js +1 -0
- package/static/{chunk-LNTUR3GU.js → chunk-LRDKG274.js} +1 -1
- package/static/chunk-LRQSPCYZ.js +1 -0
- package/static/chunk-LUSVISM6.js +1 -0
- package/static/chunk-LXQGVNU2.js +1 -0
- package/static/{chunk-LVSNIS5P.js → chunk-LYZGJZNP.js} +1 -1
- package/static/chunk-LZKI5P5T.js +1 -0
- package/static/chunk-M4XL3JN5.js +6 -0
- package/static/{chunk-UNCPXHHT.js → chunk-MGWG7OD7.js} +1 -1
- package/static/chunk-MKUUWY6Y.js +1 -0
- package/static/{chunk-VJTXJ43D.js → chunk-MNNCSSHN.js} +1 -1
- package/static/chunk-MR3U7TKQ.js +1 -0
- package/static/chunk-MRF3CNLZ.js +1 -0
- package/static/chunk-MRMSMTWD.js +1 -0
- package/static/chunk-MVZJSG5R.js +1 -0
- package/static/chunk-MYM43ENO.js +1 -0
- package/static/chunk-N3P6P6GW.js +7 -0
- package/static/chunk-NAH4V2R6.js +2 -0
- package/static/chunk-NBBDVVUF.js +1 -0
- package/static/chunk-NMF2ZFBE.js +1 -0
- package/static/chunk-NN4ONTOT.js +1 -0
- package/static/chunk-NOPACN4F.js +1 -0
- package/static/chunk-NYJPOP4L.js +1 -0
- package/static/chunk-OJCAIKUK.js +1 -0
- package/static/chunk-OQRWXCLY.js +1 -0
- package/static/chunk-PCFH5HCI.js +2 -0
- package/static/chunk-PG54TWBO.js +4 -0
- package/static/chunk-PJF5XUTO.js +1 -0
- package/static/{chunk-FJE6BOFL.js → chunk-PSUAQBYM.js} +1 -1
- package/static/chunk-PTLYIUFW.js +1 -0
- package/static/chunk-PZGLDZZM.js +1 -0
- package/static/chunk-Q4VNZGFI.js +1 -0
- package/static/chunk-Q556XB3S.js +1 -0
- package/static/{chunk-PB4AIT7O.js → chunk-Q7IXRPOO.js} +1 -1
- package/static/chunk-Q7U2VPIS.js +1 -0
- package/static/chunk-QM6CQMEX.js +1 -0
- package/static/chunk-QMHUIHSR.js +1 -0
- package/static/chunk-QNFNXDSX.js +1 -0
- package/static/chunk-QVFPHTOH.js +1 -0
- package/static/chunk-R4MI25E2.js +1 -0
- package/static/chunk-R7JRAR3P.js +1 -0
- package/static/chunk-R7PNKQU2.js +1 -0
- package/static/chunk-RCAORRB7.js +1 -0
- package/static/chunk-RK7XRDNB.js +1 -0
- package/static/chunk-RO7SAOLK.js +1 -0
- package/static/chunk-RQUUINHV.js +1 -0
- package/static/chunk-RT3K6DZR.js +1 -0
- package/static/chunk-RUN556VW.js +1 -0
- package/static/chunk-RX3YQ67K.js +1 -0
- package/static/chunk-S6EVLDHA.js +5 -0
- package/static/chunk-S7S5M3AZ.js +1 -0
- package/static/chunk-SBLNYV74.js +1 -0
- package/static/chunk-SIZCHHUA.js +1 -0
- package/static/chunk-SRBOO7AO.js +1 -0
- package/static/{chunk-PVDHBQRM.js → chunk-STA7NTYL.js} +1 -1
- package/static/chunk-T3YI3BSS.js +1 -0
- package/static/chunk-T74SMT7I.js +1 -0
- package/static/chunk-TAL3RTTQ.js +1 -0
- package/static/chunk-TJZKTNNS.js +1 -0
- package/static/chunk-UJTFWZEC.js +1 -0
- package/static/chunk-UPGVU5LG.js +1 -0
- package/static/chunk-UQ6O3I6W.js +1 -0
- package/static/{chunk-5NMSIIQB.js → chunk-V43RGNXA.js} +1 -1
- package/static/chunk-VWIRXLNE.js +1 -0
- package/static/chunk-VZMVGIVW.js +1 -0
- package/static/chunk-VZPCXSRG.js +2 -0
- package/static/chunk-WR3MA3L3.js +1 -0
- package/static/chunk-XCLK7NJL.js +1 -0
- package/static/{chunk-DSWEWLXJ.js → chunk-XCPDPB5G.js} +1 -1
- package/static/chunk-XEGHEUP5.js +1 -0
- package/static/chunk-XKEBQNQJ.js +1 -0
- package/static/chunk-XOF4UW3S.js +1 -0
- package/static/chunk-XOTKK2NJ.js +1 -0
- package/static/chunk-XX7JXKA6.js +1 -0
- package/static/chunk-Y2I36A4K.js +1 -0
- package/static/chunk-Y44XDRM5.js +1 -0
- package/static/{chunk-QO6BTONN.js → chunk-Y4MAPE2C.js} +1 -1
- package/static/chunk-Y5RLD72B.js +1 -0
- package/static/{chunk-DPUVSXRB.js → chunk-Y5XTRCFK.js} +1 -1
- package/static/chunk-Y63UUJGJ.js +1 -0
- package/static/chunk-YBNAC7QM.js +1 -0
- package/static/chunk-YCTCESL4.js +1 -0
- package/static/chunk-YMAN4LIU.js +1 -0
- package/static/chunk-YTDE6SXT.js +1 -0
- package/static/chunk-YZPIUJB3.js +1 -0
- package/static/chunk-ZCOWBVOT.js +1 -0
- package/static/chunk-ZHRYYMYE.js +1 -0
- package/static/chunk-ZNXTOQFG.js +1 -0
- package/static/{chunk-URHTCJ7G.js → chunk-ZQLBPLXI.js} +1 -1
- package/static/favicon.ico +0 -0
- package/static/index.html +2 -2
- package/static/main-3PLRDZTO.js +11 -0
- package/static/styles-Q4OZOSSK.css +1 -0
- package/server/applications/notifications/interfaces/user-mail-notification.js.map +0 -1
- package/static/assets/codemirror/mode/apl/apl.js +0 -174
- package/static/assets/codemirror/mode/asciiarmor/asciiarmor.js +0 -74
- package/static/assets/codemirror/mode/asn.1/asn.1.js +0 -204
- package/static/assets/codemirror/mode/asterisk/asterisk.js +0 -220
- package/static/assets/codemirror/mode/brainfuck/brainfuck.js +0 -85
- package/static/assets/codemirror/mode/clike/clike.js +0 -942
- package/static/assets/codemirror/mode/clojure/clojure.js +0 -293
- package/static/assets/codemirror/mode/cmake/cmake.js +0 -97
- package/static/assets/codemirror/mode/cobol/cobol.js +0 -255
- package/static/assets/codemirror/mode/coffeescript/coffeescript.js +0 -359
- package/static/assets/codemirror/mode/commonlisp/commonlisp.js +0 -125
- package/static/assets/codemirror/mode/crystal/crystal.js +0 -433
- package/static/assets/codemirror/mode/css/css.js +0 -862
- package/static/assets/codemirror/mode/cypher/cypher.js +0 -152
- package/static/assets/codemirror/mode/d/d.js +0 -223
- package/static/assets/codemirror/mode/dart/dart.js +0 -168
- package/static/assets/codemirror/mode/diff/diff.js +0 -47
- package/static/assets/codemirror/mode/django/django.js +0 -356
- package/static/assets/codemirror/mode/dockerfile/dockerfile.js +0 -211
- package/static/assets/codemirror/mode/dtd/dtd.js +0 -142
- package/static/assets/codemirror/mode/dylan/dylan.js +0 -352
- package/static/assets/codemirror/mode/ebnf/ebnf.js +0 -195
- package/static/assets/codemirror/mode/ecl/ecl.js +0 -206
- package/static/assets/codemirror/mode/eiffel/eiffel.js +0 -160
- package/static/assets/codemirror/mode/elm/elm.js +0 -245
- package/static/assets/codemirror/mode/erlang/erlang.js +0 -619
- package/static/assets/codemirror/mode/factor/factor.js +0 -85
- package/static/assets/codemirror/mode/fcl/fcl.js +0 -173
- package/static/assets/codemirror/mode/forth/forth.js +0 -180
- package/static/assets/codemirror/mode/fortran/fortran.js +0 -188
- package/static/assets/codemirror/mode/gas/gas.js +0 -355
- package/static/assets/codemirror/mode/gfm/gfm.js +0 -129
- package/static/assets/codemirror/mode/gherkin/gherkin.js +0 -194
- package/static/assets/codemirror/mode/go/go.js +0 -187
- package/static/assets/codemirror/mode/groovy/groovy.js +0 -245
- package/static/assets/codemirror/mode/haml/haml.js +0 -161
- package/static/assets/codemirror/mode/handlebars/handlebars.js +0 -70
- package/static/assets/codemirror/mode/haskell/haskell.js +0 -268
- package/static/assets/codemirror/mode/haskell-literate/haskell-literate.js +0 -43
- package/static/assets/codemirror/mode/haxe/haxe.js +0 -515
- package/static/assets/codemirror/mode/htmlembedded/htmlembedded.js +0 -37
- package/static/assets/codemirror/mode/htmlmixed/htmlmixed.js +0 -153
- package/static/assets/codemirror/mode/http/http.js +0 -113
- package/static/assets/codemirror/mode/idl/idl.js +0 -290
- package/static/assets/codemirror/mode/javascript/javascript.js +0 -960
- package/static/assets/codemirror/mode/jinja2/jinja2.js +0 -193
- package/static/assets/codemirror/mode/jsx/jsx.js +0 -149
- package/static/assets/codemirror/mode/julia/julia.js +0 -390
- package/static/assets/codemirror/mode/livescript/livescript.js +0 -280
- package/static/assets/codemirror/mode/lua/lua.js +0 -160
- package/static/assets/codemirror/mode/markdown/markdown.js +0 -886
- package/static/assets/codemirror/mode/mathematica/mathematica.js +0 -176
- package/static/assets/codemirror/mode/mbox/mbox.js +0 -129
- package/static/assets/codemirror/mode/meta.js +0 -221
- package/static/assets/codemirror/mode/mirc/mirc.js +0 -193
- package/static/assets/codemirror/mode/mllike/mllike.js +0 -359
- package/static/assets/codemirror/mode/modelica/modelica.js +0 -245
- package/static/assets/codemirror/mode/mscgen/mscgen.js +0 -175
- package/static/assets/codemirror/mode/mumps/mumps.js +0 -148
- package/static/assets/codemirror/mode/nginx/nginx.js +0 -178
- package/static/assets/codemirror/mode/nsis/nsis.js +0 -95
- package/static/assets/codemirror/mode/ntriples/ntriples.js +0 -195
- package/static/assets/codemirror/mode/octave/octave.js +0 -139
- package/static/assets/codemirror/mode/oz/oz.js +0 -252
- package/static/assets/codemirror/mode/pascal/pascal.js +0 -136
- package/static/assets/codemirror/mode/pegjs/pegjs.js +0 -111
- package/static/assets/codemirror/mode/perl/perl.js +0 -836
- package/static/assets/codemirror/mode/php/php.js +0 -234
- package/static/assets/codemirror/mode/pig/pig.js +0 -178
- package/static/assets/codemirror/mode/powershell/powershell.js +0 -398
- package/static/assets/codemirror/mode/properties/properties.js +0 -78
- package/static/assets/codemirror/mode/protobuf/protobuf.js +0 -72
- package/static/assets/codemirror/mode/pug/pug.js +0 -591
- package/static/assets/codemirror/mode/puppet/puppet.js +0 -220
- package/static/assets/codemirror/mode/python/python.js +0 -402
- package/static/assets/codemirror/mode/q/q.js +0 -139
- package/static/assets/codemirror/mode/r/r.js +0 -190
- package/static/assets/codemirror/mode/rpm/changes/index.html +0 -66
- package/static/assets/codemirror/mode/rpm/rpm.js +0 -109
- package/static/assets/codemirror/mode/rst/rst.js +0 -557
- package/static/assets/codemirror/mode/ruby/ruby.js +0 -303
- package/static/assets/codemirror/mode/rust/rust.js +0 -72
- package/static/assets/codemirror/mode/sas/sas.js +0 -303
- package/static/assets/codemirror/mode/sass/sass.js +0 -459
- package/static/assets/codemirror/mode/scheme/scheme.js +0 -284
- package/static/assets/codemirror/mode/shell/shell.js +0 -168
- package/static/assets/codemirror/mode/sieve/sieve.js +0 -193
- package/static/assets/codemirror/mode/slim/slim.js +0 -575
- package/static/assets/codemirror/mode/smalltalk/smalltalk.js +0 -168
- package/static/assets/codemirror/mode/smarty/smarty.js +0 -225
- package/static/assets/codemirror/mode/solr/solr.js +0 -104
- package/static/assets/codemirror/mode/soy/soy.js +0 -665
- package/static/assets/codemirror/mode/sparql/sparql.js +0 -184
- package/static/assets/codemirror/mode/spreadsheet/spreadsheet.js +0 -112
- package/static/assets/codemirror/mode/sql/sql.js +0 -529
- package/static/assets/codemirror/mode/stex/stex.js +0 -264
- package/static/assets/codemirror/mode/stylus/stylus.js +0 -775
- package/static/assets/codemirror/mode/swift/swift.js +0 -221
- package/static/assets/codemirror/mode/tcl/tcl.js +0 -140
- package/static/assets/codemirror/mode/textile/textile.js +0 -469
- package/static/assets/codemirror/mode/tiddlywiki/tiddlywiki.css +0 -14
- package/static/assets/codemirror/mode/tiddlywiki/tiddlywiki.js +0 -308
- package/static/assets/codemirror/mode/tiki/tiki.css +0 -26
- package/static/assets/codemirror/mode/tiki/tiki.js +0 -312
- package/static/assets/codemirror/mode/toml/toml.js +0 -88
- package/static/assets/codemirror/mode/tornado/tornado.js +0 -68
- package/static/assets/codemirror/mode/troff/troff.js +0 -84
- package/static/assets/codemirror/mode/ttcn/ttcn.js +0 -283
- package/static/assets/codemirror/mode/ttcn-cfg/ttcn-cfg.js +0 -214
- package/static/assets/codemirror/mode/turtle/turtle.js +0 -162
- package/static/assets/codemirror/mode/twig/twig.js +0 -141
- package/static/assets/codemirror/mode/vb/vb.js +0 -275
- package/static/assets/codemirror/mode/vbscript/vbscript.js +0 -350
- package/static/assets/codemirror/mode/velocity/velocity.js +0 -202
- package/static/assets/codemirror/mode/verilog/verilog.js +0 -781
- package/static/assets/codemirror/mode/vhdl/vhdl.js +0 -189
- package/static/assets/codemirror/mode/vue/vue.js +0 -77
- package/static/assets/codemirror/mode/wast/wast.js +0 -132
- package/static/assets/codemirror/mode/webidl/webidl.js +0 -195
- package/static/assets/codemirror/mode/xml/xml.js +0 -417
- package/static/assets/codemirror/mode/xquery/xquery.js +0 -448
- package/static/assets/codemirror/mode/yacas/yacas.js +0 -204
- package/static/assets/codemirror/mode/yaml/yaml.js +0 -120
- package/static/assets/codemirror/mode/yaml-frontmatter/yaml-frontmatter.js +0 -72
- package/static/assets/codemirror/mode/z80/z80.js +0 -116
- package/static/chunk-2KLC4T2Z.js +0 -1
- package/static/chunk-2VMSXRCB.js +0 -12
- package/static/chunk-3OHSRRKH.js +0 -4
- package/static/chunk-3R4WKOHQ.js +0 -1
- package/static/chunk-3XVM35O2.js +0 -1
- package/static/chunk-3YVRP3VM.js +0 -2
- package/static/chunk-5UKZLU5H.js +0 -1
- package/static/chunk-AF24EYXU.js +0 -1
- package/static/chunk-AKQVEHO6.js +0 -2
- package/static/chunk-BCVX464U.js +0 -2
- package/static/chunk-BQV4FRM6.js +0 -1
- package/static/chunk-CETH7UYS.js +0 -1
- package/static/chunk-DIT6W7VM.js +0 -562
- package/static/chunk-DKSEQTMX.js +0 -1
- package/static/chunk-FZ3JPGYZ.js +0 -1
- package/static/chunk-IQSKQXC3.js +0 -1
- package/static/chunk-ITUFI2BJ.js +0 -1
- package/static/chunk-LCTZJ537.js +0 -1
- package/static/chunk-LK2UCQJ6.js +0 -1
- package/static/chunk-LP5TBXEN.js +0 -7
- package/static/chunk-N3U6637P.js +0 -1
- package/static/chunk-NNV4OXSB.js +0 -1
- package/static/chunk-O6FYXVHI.js +0 -1
- package/static/chunk-OOGP4WSH.js +0 -2
- package/static/chunk-PCWDQPOM.js +0 -2
- package/static/chunk-PNR6M34W.js +0 -1
- package/static/chunk-Q5KM7LTX.js +0 -1
- package/static/chunk-QHC6ZPQ4.js +0 -1
- package/static/chunk-QMRBZHE4.js +0 -1
- package/static/chunk-QSJRY3TF.js +0 -1
- package/static/chunk-QUUIRSYT.js +0 -1
- package/static/chunk-RFH46UW3.js +0 -1
- package/static/chunk-RSXHRKM5.js +0 -1
- package/static/chunk-RV3VZJPZ.js +0 -1
- package/static/chunk-S7HNXVRB.js +0 -1
- package/static/chunk-SBZ572Q4.js +0 -2
- package/static/chunk-SJR5R3Y4.js +0 -1
- package/static/chunk-SLHTEGRU.js +0 -1
- package/static/chunk-SSFF27P2.js +0 -24
- package/static/chunk-V3LHHZYN.js +0 -1
- package/static/chunk-VQQKMY2C.js +0 -1
- package/static/chunk-WSSU2HXE.js +0 -1
- package/static/chunk-XDZGW64M.js +0 -3
- package/static/chunk-XTRDKGKG.js +0 -1
- package/static/chunk-YLWTEC3X.js +0 -1
- package/static/chunk-Z5J5F5SX.js +0 -1
- package/static/main-4H5BJY3J.js +0 -9
- package/static/scripts-WRDOQIU5.js +0 -24
- package/static/styles-2C2UNCNB.css +0 -1
|
@@ -131,7 +131,7 @@ let FilesContentManager = class FilesContentManager {
|
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
} catch (e) {
|
|
134
|
-
this.logger.warn(`${this.parseFiles.name} - ${context.indexSuffix} - unable to parse
|
|
134
|
+
this.logger.warn(`${this.parseFiles.name} - ${context.indexSuffix} - unable to parse: ${dir} - ${e}`);
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
async analyzeFile(realPath, context, isRootFile = false) {
|
|
@@ -144,7 +144,7 @@ let FilesContentManager = class FilesContentManager {
|
|
|
144
144
|
try {
|
|
145
145
|
stats = await _promises.default.stat(realPath);
|
|
146
146
|
} catch (e) {
|
|
147
|
-
this.logger.warn(`${this.analyzeFile.name} - unable to stats ${realPath}
|
|
147
|
+
this.logger.warn(`${this.analyzeFile.name} - unable to stats: ${realPath} - ${e}`);
|
|
148
148
|
return null;
|
|
149
149
|
}
|
|
150
150
|
if (stats.size === 0 || stats.size > this.maxDocumentSize) {
|
|
@@ -181,7 +181,7 @@ let FilesContentManager = class FilesContentManager {
|
|
|
181
181
|
});
|
|
182
182
|
return content.length ? content : null;
|
|
183
183
|
} catch (e) {
|
|
184
|
-
this.logger.warn(`${this.parseContent.name} - unable to index
|
|
184
|
+
this.logger.warn(`${this.parseContent.name} - unable to index: ${rPath} - ${e}`);
|
|
185
185
|
}
|
|
186
186
|
return null;
|
|
187
187
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../backend/src/applications/files/services/files-content-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 fs from 'fs/promises'\nimport { Stats } from 'node:fs'\nimport path from 'node:path'\nimport { indexableExtensions, shareIndexPrefix, spaceIndexPrefix, userIndexPrefix } from '../constants/indexing'\nimport { FileIndexContext, FileParseContext } from '../interfaces/file-parse-index'\nimport { FilesIndexer } from '../models/files-indexer'\nimport { FileContent } from '../schemas/file-content.interface'\nimport { docTextify } from '../utils/doc-textify/doc-textify'\nimport { getMimeType } from '../utils/files'\nimport { FilesParser } from './files-parser.service'\n\n@Injectable()\nexport class FilesContentManager {\n private readonly maxDocumentSize = 150 * 1_000_000\n private readonly logger = new Logger(FilesContentManager.name)\n\n constructor(\n private readonly filesIndexer: FilesIndexer,\n private readonly filesParser: FilesParser\n ) {}\n\n async parseAndIndexAllFiles(): Promise<void> {\n const indexSuffixes: string[] = []\n for await (const [id, type, paths] of this.filesParser.allPaths()) {\n let indexSuffix: string\n switch (type) {\n case 'user':\n indexSuffix = `${userIndexPrefix}${id}`\n break\n case 'space':\n indexSuffix = `${spaceIndexPrefix}${id}`\n break\n case 'share':\n indexSuffix = `${shareIndexPrefix}${id}`\n }\n try {\n await this.indexFiles(indexSuffix, paths)\n } catch (e) {\n this.logger.error(`${this.parseAndIndexAllFiles.name} : ${e}`)\n }\n indexSuffixes.push(indexSuffix)\n }\n // clean up old tables\n await this.filesIndexer.cleanIndexes(indexSuffixes)\n }\n\n private async indexFiles(indexSuffix: string, paths: FileParseContext[]): Promise<void> {\n const indexName = this.filesIndexer.getIndexName(indexSuffix)\n if (!(await this.filesIndexer.createIndex(indexName))) {\n return\n }\n const context: FileIndexContext = {\n indexSuffix: indexSuffix,\n pathPrefix: '',\n regexBasePath: undefined,\n db: await this.filesIndexer.getRecordStats(indexName),\n fs: new Set()\n }\n let indexedRecords = 0\n let errorRecords = 0\n\n for (const p of paths) {\n context.regexBasePath = new RegExp(`^/?${p.realPath}/?`)\n context.pathPrefix = p.pathPrefix || ''\n if (!p.isDir) {\n // Handles the space root file or shared file case\n const rootFileContent = await this.analyzeFile(p.realPath, context, true)\n if (rootFileContent !== null) {\n this.filesIndexer.insertRecord(indexName, rootFileContent).catch((e: Error) => {\n errorRecords++\n this.logger.error(`${this.indexFiles.name} - ${indexSuffix} | ${rootFileContent.name} : ${e}`)\n })\n indexedRecords++\n }\n continue\n }\n for await (const fileContent of this.parseFiles(p.realPath, context)) {\n this.filesIndexer.insertRecord(indexName, fileContent).catch((e: Error) => {\n errorRecords++\n this.logger.error(`${this.indexFiles.name} - ${indexSuffix} | ${fileContent.name} : ${e}`)\n })\n indexedRecords++\n }\n }\n\n if (context.db.size === 0 && indexedRecords === 0) {\n // case when no data\n this.filesIndexer\n .dropIndex(indexName)\n .catch((e: Error) => this.logger.error(`${this.indexFiles.name} - ${indexSuffix} - unable to drop index : ${e}`))\n this.logger.log(`${this.indexFiles.name} - ${indexSuffix} - no data, index not stored`)\n } else {\n // clean up old records\n const recordsToDelete: number[] = [...context.db.keys()].filter((key) => !context.fs.has(key))\n if (recordsToDelete.length > 0) {\n this.filesIndexer\n .deleteRecords(indexName, recordsToDelete)\n .catch((e: Error) => this.logger.error(`${this.indexFiles.name} - ${indexSuffix} - unable to delete records : ${e}`))\n }\n if (indexedRecords === 0 && errorRecords === 0 && recordsToDelete.length === 0) {\n this.logger.log(`${this.indexFiles.name} - ${indexSuffix} - no new data`)\n } else {\n this.logger.log(\n `${this.indexFiles.name} - ${indexSuffix} - indexed: ${indexedRecords - errorRecords}, errors: ${errorRecords}, deleted: ${recordsToDelete.length}`\n )\n }\n }\n }\n\n private async *parseFiles(dir: string, context: FileIndexContext): AsyncGenerator<FileContent> {\n try {\n for (const entry of await fs.readdir(dir, { withFileTypes: true })) {\n const realPath = path.join(entry.parentPath, entry.name)\n if (entry.isDirectory()) {\n yield* this.parseFiles(realPath, context)\n continue\n }\n const fileContent = await this.analyzeFile(realPath, context)\n if (fileContent !== null) {\n yield fileContent\n }\n }\n } catch (e) {\n this.logger.warn(`${this.parseFiles.name} - ${context.indexSuffix} - unable to parse : ${dir} : ${e}`)\n }\n }\n\n private async analyzeFile(realPath: string, context: FileIndexContext, isRootFile = false): Promise<FileContent> {\n const extension = path.extname(realPath).slice(1).toLowerCase()\n if (!indexableExtensions.has(extension)) return null\n\n const fileName = isRootFile ? path.basename(context.pathPrefix) : path.basename(realPath)\n\n // ignore temporary documents\n if (fileName.startsWith('~$')) return null\n\n let stats: Stats\n try {\n stats = await fs.stat(realPath)\n } catch (e) {\n this.logger.warn(`${this.analyzeFile.name} - unable to stats ${realPath} : ${e}`)\n return null\n }\n if (stats.size === 0 || stats.size > this.maxDocumentSize) {\n return null\n }\n\n const filePath = isRootFile\n ? path.dirname(context.pathPrefix) || '.'\n : path.join(context.pathPrefix, path.dirname(realPath).replace(context.regexBasePath, '') || '.')\n\n const f = context.db.get(stats.ino)\n if (f && f.size === stats.size && f.path === filePath && f.name === fileName) {\n // no changes, store inode id & skip it\n context.fs.add(stats.ino)\n return null\n }\n\n // store inode id\n context.fs.add(stats.ino)\n\n // store the content with null value to not parse it later\n return {\n id: stats.ino,\n path: filePath,\n name: fileName,\n mime: getMimeType(realPath, false),\n size: stats.size,\n mtime: stats.mtime.getTime(),\n content: await this.parseContent(realPath, extension)\n }\n }\n\n private async parseContent(rPath: string, extension: string): Promise<string> {\n try {\n const content = await docTextify(\n rPath,\n { newlineDelimiter: ' ', minCharsToExtract: 10 },\n {\n extension: extension,\n verified: true\n }\n )\n return content.length ? content : null\n } catch (e) {\n this.logger.warn(`${this.parseContent.name} - unable to index : ${rPath} : ${e}`)\n }\n return null\n }\n}\n"],"names":["FilesContentManager","parseAndIndexAllFiles","indexSuffixes","id","type","paths","filesParser","allPaths","indexSuffix","userIndexPrefix","spaceIndexPrefix","shareIndexPrefix","indexFiles","e","logger","error","name","push","filesIndexer","cleanIndexes","indexName","getIndexName","createIndex","context","pathPrefix","regexBasePath","undefined","db","getRecordStats","fs","Set","indexedRecords","errorRecords","p","RegExp","realPath","isDir","rootFileContent","analyzeFile","insertRecord","catch","fileContent","parseFiles","size","dropIndex","log","recordsToDelete","keys","filter","key","has","length","deleteRecords","dir","entry","readdir","withFileTypes","path","join","parentPath","isDirectory","warn","isRootFile","extension","extname","slice","toLowerCase","indexableExtensions","fileName","basename","startsWith","stats","stat","maxDocumentSize","filePath","dirname","replace","f","get","ino","add","mime","getMimeType","mtime","getTime","content","parseContent","rPath","docTextify","newlineDelimiter","minCharsToExtract","verified","Logger"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../backend/src/applications/files/services/files-content-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 fs from 'fs/promises'\nimport { Stats } from 'node:fs'\nimport path from 'node:path'\nimport { indexableExtensions, shareIndexPrefix, spaceIndexPrefix, userIndexPrefix } from '../constants/indexing'\nimport { FileIndexContext, FileParseContext } from '../interfaces/file-parse-index'\nimport { FilesIndexer } from '../models/files-indexer'\nimport { FileContent } from '../schemas/file-content.interface'\nimport { docTextify } from '../utils/doc-textify/doc-textify'\nimport { getMimeType } from '../utils/files'\nimport { FilesParser } from './files-parser.service'\n\n@Injectable()\nexport class FilesContentManager {\n private readonly maxDocumentSize = 150 * 1_000_000\n private readonly logger = new Logger(FilesContentManager.name)\n\n constructor(\n private readonly filesIndexer: FilesIndexer,\n private readonly filesParser: FilesParser\n ) {}\n\n async parseAndIndexAllFiles(): Promise<void> {\n const indexSuffixes: string[] = []\n for await (const [id, type, paths] of this.filesParser.allPaths()) {\n let indexSuffix: string\n switch (type) {\n case 'user':\n indexSuffix = `${userIndexPrefix}${id}`\n break\n case 'space':\n indexSuffix = `${spaceIndexPrefix}${id}`\n break\n case 'share':\n indexSuffix = `${shareIndexPrefix}${id}`\n }\n try {\n await this.indexFiles(indexSuffix, paths)\n } catch (e) {\n this.logger.error(`${this.parseAndIndexAllFiles.name} : ${e}`)\n }\n indexSuffixes.push(indexSuffix)\n }\n // clean up old tables\n await this.filesIndexer.cleanIndexes(indexSuffixes)\n }\n\n private async indexFiles(indexSuffix: string, paths: FileParseContext[]): Promise<void> {\n const indexName = this.filesIndexer.getIndexName(indexSuffix)\n if (!(await this.filesIndexer.createIndex(indexName))) {\n return\n }\n const context: FileIndexContext = {\n indexSuffix: indexSuffix,\n pathPrefix: '',\n regexBasePath: undefined,\n db: await this.filesIndexer.getRecordStats(indexName),\n fs: new Set()\n }\n let indexedRecords = 0\n let errorRecords = 0\n\n for (const p of paths) {\n context.regexBasePath = new RegExp(`^/?${p.realPath}/?`)\n context.pathPrefix = p.pathPrefix || ''\n if (!p.isDir) {\n // Handles the space root file or shared file case\n const rootFileContent = await this.analyzeFile(p.realPath, context, true)\n if (rootFileContent !== null) {\n this.filesIndexer.insertRecord(indexName, rootFileContent).catch((e: Error) => {\n errorRecords++\n this.logger.error(`${this.indexFiles.name} - ${indexSuffix} | ${rootFileContent.name} : ${e}`)\n })\n indexedRecords++\n }\n continue\n }\n for await (const fileContent of this.parseFiles(p.realPath, context)) {\n this.filesIndexer.insertRecord(indexName, fileContent).catch((e: Error) => {\n errorRecords++\n this.logger.error(`${this.indexFiles.name} - ${indexSuffix} | ${fileContent.name} : ${e}`)\n })\n indexedRecords++\n }\n }\n\n if (context.db.size === 0 && indexedRecords === 0) {\n // case when no data\n this.filesIndexer\n .dropIndex(indexName)\n .catch((e: Error) => this.logger.error(`${this.indexFiles.name} - ${indexSuffix} - unable to drop index : ${e}`))\n this.logger.log(`${this.indexFiles.name} - ${indexSuffix} - no data, index not stored`)\n } else {\n // clean up old records\n const recordsToDelete: number[] = [...context.db.keys()].filter((key) => !context.fs.has(key))\n if (recordsToDelete.length > 0) {\n this.filesIndexer\n .deleteRecords(indexName, recordsToDelete)\n .catch((e: Error) => this.logger.error(`${this.indexFiles.name} - ${indexSuffix} - unable to delete records : ${e}`))\n }\n if (indexedRecords === 0 && errorRecords === 0 && recordsToDelete.length === 0) {\n this.logger.log(`${this.indexFiles.name} - ${indexSuffix} - no new data`)\n } else {\n this.logger.log(\n `${this.indexFiles.name} - ${indexSuffix} - indexed: ${indexedRecords - errorRecords}, errors: ${errorRecords}, deleted: ${recordsToDelete.length}`\n )\n }\n }\n }\n\n private async *parseFiles(dir: string, context: FileIndexContext): AsyncGenerator<FileContent> {\n try {\n for (const entry of await fs.readdir(dir, { withFileTypes: true })) {\n const realPath = path.join(entry.parentPath, entry.name)\n if (entry.isDirectory()) {\n yield* this.parseFiles(realPath, context)\n continue\n }\n const fileContent = await this.analyzeFile(realPath, context)\n if (fileContent !== null) {\n yield fileContent\n }\n }\n } catch (e) {\n this.logger.warn(`${this.parseFiles.name} - ${context.indexSuffix} - unable to parse: ${dir} - ${e}`)\n }\n }\n\n private async analyzeFile(realPath: string, context: FileIndexContext, isRootFile = false): Promise<FileContent> {\n const extension = path.extname(realPath).slice(1).toLowerCase()\n if (!indexableExtensions.has(extension)) return null\n\n const fileName = isRootFile ? path.basename(context.pathPrefix) : path.basename(realPath)\n\n // ignore temporary documents\n if (fileName.startsWith('~$')) return null\n\n let stats: Stats\n try {\n stats = await fs.stat(realPath)\n } catch (e) {\n this.logger.warn(`${this.analyzeFile.name} - unable to stats: ${realPath} - ${e}`)\n return null\n }\n if (stats.size === 0 || stats.size > this.maxDocumentSize) {\n return null\n }\n\n const filePath = isRootFile\n ? path.dirname(context.pathPrefix) || '.'\n : path.join(context.pathPrefix, path.dirname(realPath).replace(context.regexBasePath, '') || '.')\n\n const f = context.db.get(stats.ino)\n if (f && f.size === stats.size && f.path === filePath && f.name === fileName) {\n // no changes, store inode id & skip it\n context.fs.add(stats.ino)\n return null\n }\n\n // store inode id\n context.fs.add(stats.ino)\n\n // store the content with null value to not parse it later\n return {\n id: stats.ino,\n path: filePath,\n name: fileName,\n mime: getMimeType(realPath, false),\n size: stats.size,\n mtime: stats.mtime.getTime(),\n content: await this.parseContent(realPath, extension)\n }\n }\n\n private async parseContent(rPath: string, extension: string): Promise<string> {\n try {\n const content = await docTextify(\n rPath,\n { newlineDelimiter: ' ', minCharsToExtract: 10 },\n {\n extension: extension,\n verified: true\n }\n )\n return content.length ? content : null\n } catch (e) {\n this.logger.warn(`${this.parseContent.name} - unable to index: ${rPath} - ${e}`)\n }\n return null\n }\n}\n"],"names":["FilesContentManager","parseAndIndexAllFiles","indexSuffixes","id","type","paths","filesParser","allPaths","indexSuffix","userIndexPrefix","spaceIndexPrefix","shareIndexPrefix","indexFiles","e","logger","error","name","push","filesIndexer","cleanIndexes","indexName","getIndexName","createIndex","context","pathPrefix","regexBasePath","undefined","db","getRecordStats","fs","Set","indexedRecords","errorRecords","p","RegExp","realPath","isDir","rootFileContent","analyzeFile","insertRecord","catch","fileContent","parseFiles","size","dropIndex","log","recordsToDelete","keys","filter","key","has","length","deleteRecords","dir","entry","readdir","withFileTypes","path","join","parentPath","isDirectory","warn","isRootFile","extension","extname","slice","toLowerCase","indexableExtensions","fileName","basename","startsWith","stats","stat","maxDocumentSize","filePath","dirname","replace","f","get","ino","add","mime","getMimeType","mtime","getTime","content","parseContent","rPath","docTextify","newlineDelimiter","minCharsToExtract","verified","Logger"],"mappings":"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"}
|
|
@@ -36,7 +36,7 @@ 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,
|
|
39
|
+
async create(user, dbFile, depth, davLock, ttl) {
|
|
40
40
|
let token;
|
|
41
41
|
let lockscope;
|
|
42
42
|
if (davLock) {
|
|
@@ -62,7 +62,7 @@ let FilesLockManager = class FilesLockManager {
|
|
|
62
62
|
}
|
|
63
63
|
throw new Error(e);
|
|
64
64
|
}
|
|
65
|
-
ttl ??=
|
|
65
|
+
ttl ??= _cache.CACHE_LOCK_DEFAULT_TTL;
|
|
66
66
|
const key = `${_cache.CACHE_LOCK_PREFIX}|${this.genSuffixKey(dbFile, {
|
|
67
67
|
depth: depth,
|
|
68
68
|
token: token
|
|
@@ -83,6 +83,38 @@ let FilesLockManager = class FilesLockManager {
|
|
|
83
83
|
lock
|
|
84
84
|
];
|
|
85
85
|
}
|
|
86
|
+
async createOrRefresh(user, dbFile, depth, ttl) {
|
|
87
|
+
// Returns: [created, lock]
|
|
88
|
+
ttl ??= _cache.CACHE_LOCK_DEFAULT_TTL;
|
|
89
|
+
const locks = await this.getLocksByPath(dbFile);
|
|
90
|
+
// Check the existing lock and refresh it if needed
|
|
91
|
+
if (locks.length > 0) {
|
|
92
|
+
for (const lock of locks){
|
|
93
|
+
if (lock.owner.id === user.id) {
|
|
94
|
+
// Refresh if more than half of the TTL has passed
|
|
95
|
+
const mustRefresh = lock.expiration - (0, _shared.currentTimeStamp)() < ttl / 2;
|
|
96
|
+
if (mustRefresh) {
|
|
97
|
+
this.refreshLockTimeout(lock, ttl).catch((e)=>this.logger.error(`${this.createOrRefresh.name} - ${e}`));
|
|
98
|
+
}
|
|
99
|
+
} else {
|
|
100
|
+
throw new _filelockerror.LockConflict(lock, 'Conflicting lock');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return [
|
|
104
|
+
false,
|
|
105
|
+
locks[0]
|
|
106
|
+
];
|
|
107
|
+
}
|
|
108
|
+
// Create the lock
|
|
109
|
+
const [ok, lock] = await this.create(user, dbFile, depth, null, ttl);
|
|
110
|
+
if (!ok) {
|
|
111
|
+
throw new _filelockerror.LockConflict(lock, 'Conflicting lock');
|
|
112
|
+
}
|
|
113
|
+
return [
|
|
114
|
+
true,
|
|
115
|
+
lock
|
|
116
|
+
];
|
|
117
|
+
}
|
|
86
118
|
removeLock(key) {
|
|
87
119
|
this.logger.verbose(`${this.removeLock.name} - ${key}`);
|
|
88
120
|
return this.cache.del(key);
|
|
@@ -159,7 +191,7 @@ let FilesLockManager = class FilesLockManager {
|
|
|
159
191
|
}
|
|
160
192
|
async checkConflicts(dbFile, depth, options) {
|
|
161
193
|
/* Checks if a file could be modified, created, moved, or deleted
|
|
162
|
-
Throws an `LockConflict` error when there are parent locks (depth: 0) or child locks (depth: infinite) that prevent
|
|
194
|
+
Throws an `LockConflict` error when there are parent locks (depth: 0) or child locks (depth: infinite) that prevent modification
|
|
163
195
|
Returns on the first conflict (compliant with the RFC 4918)
|
|
164
196
|
*/ for await (const l of this.searchParentLocks(dbFile, {
|
|
165
197
|
includeRoot: true,
|
|
@@ -186,6 +218,28 @@ let FilesLockManager = class FilesLockManager {
|
|
|
186
218
|
}
|
|
187
219
|
}
|
|
188
220
|
}
|
|
221
|
+
async *searchChildLocks(dbFile) {
|
|
222
|
+
const props = this.genSuffixKey(dbFile, {
|
|
223
|
+
ignorePath: true
|
|
224
|
+
});
|
|
225
|
+
const path = dbFile.path === '.' ? '*' : `${dbFile.path}/*`;
|
|
226
|
+
this.logger.verbose(`${this.searchChildLocks.name} - ${path} (${props})`);
|
|
227
|
+
const keys = await this.searchKeysByPath(path, props);
|
|
228
|
+
if (keys.length) {
|
|
229
|
+
for (const l of (await this.cache.mget(keys)).filter(Boolean)){
|
|
230
|
+
this.logger.verbose(`-> ${l.dbFilePath} (owner: ${l.owner.login})`);
|
|
231
|
+
yield l;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
convertLockToFileLockProps(lock) {
|
|
236
|
+
if (!lock) return null;
|
|
237
|
+
return {
|
|
238
|
+
owner: lock?.davLock?.owner || `${lock.owner.fullName} (${lock.owner.email})`,
|
|
239
|
+
ownerLogin: lock.owner.login,
|
|
240
|
+
isExclusive: lock?.davLock?.lockscope ? lock?.davLock?.lockscope === _webdav.LOCK_SCOPE.EXCLUSIVE : true
|
|
241
|
+
};
|
|
242
|
+
}
|
|
189
243
|
async *searchParentLocks(dbFile, options = {}) {
|
|
190
244
|
const props = this.genSuffixKey(dbFile, {
|
|
191
245
|
ignorePath: true
|
|
@@ -209,20 +263,6 @@ let FilesLockManager = class FilesLockManager {
|
|
|
209
263
|
path = (0, _files.dirName)(path);
|
|
210
264
|
}
|
|
211
265
|
}
|
|
212
|
-
async *searchChildLocks(dbFile) {
|
|
213
|
-
const props = this.genSuffixKey(dbFile, {
|
|
214
|
-
ignorePath: true
|
|
215
|
-
});
|
|
216
|
-
const path = dbFile.path === '.' ? '*' : `${dbFile.path}/*`;
|
|
217
|
-
this.logger.verbose(`${this.searchChildLocks.name} - ${path} (${props})`);
|
|
218
|
-
const keys = await this.searchKeysByPath(path, props);
|
|
219
|
-
if (keys.length) {
|
|
220
|
-
for (const l of (await this.cache.mget(keys)).filter(Boolean)){
|
|
221
|
-
this.logger.verbose(`-> ${l.dbFilePath} (owner: ${l.owner.login})`);
|
|
222
|
-
yield l;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
266
|
async searchKeyByToken(token) {
|
|
227
267
|
const keys = await this.cache.keys(`${_cache.CACHE_LOCK_PREFIX}|token:${token}|*`);
|
|
228
268
|
if (!keys.length) {
|
|
@@ -258,10 +298,7 @@ let FilesLockManager = class FilesLockManager {
|
|
|
258
298
|
}
|
|
259
299
|
constructor(cache){
|
|
260
300
|
this.cache = cache;
|
|
261
|
-
|
|
262
|
-
}
|
|
263
|
-
};
|
|
264
|
-
/* Philosophy
|
|
301
|
+
/* Philosophy
|
|
265
302
|
Currently this manager only handle conflicting locks between multiple users, not between clients
|
|
266
303
|
- The locks created from api
|
|
267
304
|
* They do not contain a token key or a davLock property
|
|
@@ -273,7 +310,9 @@ let FilesLockManager = class FilesLockManager {
|
|
|
273
310
|
* If created with WebDAV, they are stored with a token and a davLock property
|
|
274
311
|
* If created with OnlyOffice, they are stored with no token and a davLock property whose `locktoken` & `lockroot` properties are null
|
|
275
312
|
Cache token key format = `flock|token?:${uuid}|path:${path}|ownerId?:${number}|spaceId?:${number}|...props` => FileLock
|
|
276
|
-
*/
|
|
313
|
+
*/ this.logger = new _common.Logger(FilesLockManager.name);
|
|
314
|
+
}
|
|
315
|
+
};
|
|
277
316
|
FilesLockManager = _ts_decorate([
|
|
278
317
|
(0, _common.Injectable)(),
|
|
279
318
|
_ts_metadata("design:type", Function),
|
|
@@ -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_PREFIX } from '../constants/cache'\nimport { FileDBProps } from '../interfaces/file-db-props.interface'\nimport { FileLock } from '../interfaces/file-lock.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 public static readonly defaultLockTimeoutSeconds = 86400 // 1 day\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, ttl?: number, davLock?: WebDAVLock): 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 ??= FilesLockManager.defaultLockTimeoutSeconds\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 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 modifications\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 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 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 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","ttl","davLock","token","lockscope","locktoken","genDAVToken","checkConflicts","lockScope","e","LockConflict","lock","Error","defaultLockTimeoutSeconds","key","CACHE_LOCK_PREFIX","genSuffixKey","expiration","Math","floor","currentTimeStamp","owner","asOwner","dbFilePath","path","logger","verbose","name","cache","set","removeLock","del","removeChildLocks","ownedLockKeys","searchChildLocks","id","push","conflict","login","debug","isLockedWithToken","getLockByToken","startsWith","browseParentChildLocks","includeRoot","childLocks","browseLocks","lengthFilter","split","length","dbFileName","fileName","Object","fromEntries","getLocksByPath","map","l","refreshLockTimeout","catch","error","searchKeyByToken","get","props","ignorePath","keys","searchKeysByPath","mget","filter","Boolean","isPathLocked","options","searchParentLocks","DEPTH","INFINITY","LOCK_SCOPE","SHARED","userId","lockTokens","indexOf","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 { 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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"}
|
|
@@ -17,14 +17,18 @@ const _common = require("@nestjs/common");
|
|
|
17
17
|
const _archiver = /*#__PURE__*/ _interop_require_default(require("archiver"));
|
|
18
18
|
const _nodefs = /*#__PURE__*/ _interop_require_default(require("node:fs"));
|
|
19
19
|
const _nodepath = /*#__PURE__*/ _interop_require_default(require("node:path"));
|
|
20
|
-
const _promises = require("node:stream/promises");
|
|
21
20
|
const _tar = require("tar");
|
|
22
21
|
const _image = require("../../../common/image");
|
|
22
|
+
const _contextmanagerservice = require("../../../infrastructure/context/services/context-manager.service");
|
|
23
23
|
const _applicationsconstants = require("../../applications.constants");
|
|
24
|
+
const _notifications = require("../../notifications/constants/notifications");
|
|
25
|
+
const _notificationsmanagerservice = require("../../notifications/services/notifications-manager.service");
|
|
24
26
|
const _spaces = require("../../spaces/constants/spaces");
|
|
27
|
+
const _spacesmanagerservice = require("../../spaces/services/spaces-manager.service");
|
|
25
28
|
const _paths = require("../../spaces/utils/paths");
|
|
26
29
|
const _permissions = require("../../spaces/utils/permissions");
|
|
27
30
|
const _webdav = require("../../webdav/constants/webdav");
|
|
31
|
+
const _cache = require("../constants/cache");
|
|
28
32
|
const _compress = require("../constants/compress");
|
|
29
33
|
const _files = require("../constants/files");
|
|
30
34
|
const _operations = require("../constants/operations");
|
|
@@ -142,11 +146,16 @@ let FilesManager = class FilesManager {
|
|
|
142
146
|
}
|
|
143
147
|
async saveMultipart(user, space, req) {
|
|
144
148
|
/* Accepted methods:
|
|
145
|
-
POST:
|
|
146
|
-
PUT:
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
149
|
+
POST: Creates new resource
|
|
150
|
+
PUT: Creates or fully replaces a resource at the given URI (even if intermediate paths do not exist)
|
|
151
|
+
PATCH: Updates the content of an existing resource without creating a new one.
|
|
152
|
+
In this text-editing scenario, locking and refreshing occur automatically, but unlocking must be handled explicitly via
|
|
153
|
+
the `unlock` method.
|
|
154
|
+
*/ const overwrite = req.method === _applicationsconstants.HTTP_METHOD.PUT;
|
|
155
|
+
const patch = req.method === _applicationsconstants.HTTP_METHOD.PATCH;
|
|
156
|
+
const realParentPath = (0, _files1.dirName)(space.realPath);
|
|
157
|
+
if (!overwrite) {
|
|
158
|
+
if (!patch && await (0, _files1.isPathExists)(space.realPath)) {
|
|
150
159
|
throw new _fileerror.FileError(_common.HttpStatus.BAD_REQUEST, 'Resource already exists');
|
|
151
160
|
}
|
|
152
161
|
if (!await (0, _files1.isPathExists)(realParentPath)) {
|
|
@@ -158,31 +167,53 @@ let FilesManager = class FilesManager {
|
|
|
158
167
|
}
|
|
159
168
|
const basePath = realParentPath + _nodepath.default.sep;
|
|
160
169
|
for await (const part of req.files()){
|
|
161
|
-
//
|
|
162
|
-
const
|
|
163
|
-
//
|
|
170
|
+
// If the request uses the PATCH method, the file name corresponds to the space
|
|
171
|
+
const partFileName = patch ? (0, _files1.fileName)(space.realPath) : part.filename;
|
|
172
|
+
// `part.filename` may contain a path like foo/bar.txt
|
|
173
|
+
const dstFile = _nodepath.default.resolve(basePath, partFileName);
|
|
174
|
+
// Prevent path traversal
|
|
164
175
|
if (!dstFile.startsWith(basePath)) {
|
|
165
176
|
throw new _fileerror.FileError(_common.HttpStatus.FORBIDDEN, 'Location is not allowed');
|
|
166
177
|
}
|
|
167
|
-
// make dir in space
|
|
168
178
|
const dstDir = (0, _files1.dirName)(dstFile);
|
|
179
|
+
if (overwrite) {
|
|
180
|
+
// Prevent errors when an uploaded file would replace a directory with the same name
|
|
181
|
+
// Only applies in `overwrite` cases
|
|
182
|
+
if (await (0, _files1.isPathExists)(dstFile) && await (0, _files1.isPathIsDir)(dstFile)) {
|
|
183
|
+
// If a directory already exists at the destination path, delete it to allow overwriting with the uploaded file
|
|
184
|
+
const dstUrl = _nodepath.default.join(_nodepath.default.dirname(space.url), partFileName);
|
|
185
|
+
const dstSpace = await this.spacesManager.spaceEnv(user, dstUrl.split('/'));
|
|
186
|
+
await this.delete(user, dstSpace);
|
|
187
|
+
} else if (await (0, _files1.isPathExists)(dstDir) && !await (0, _files1.isPathIsDir)(dstDir)) {
|
|
188
|
+
// If the destination's parent exists but is a file, remove it so we can create the directory
|
|
189
|
+
const dstUrl = _nodepath.default.join(_nodepath.default.dirname(space.url), _nodepath.default.dirname(partFileName));
|
|
190
|
+
const dstSpace = await this.spacesManager.spaceEnv(user, dstUrl.split('/'));
|
|
191
|
+
await this.delete(user, dstSpace);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// Create the directory in the space
|
|
169
195
|
if (!await (0, _files1.isPathExists)(dstDir)) {
|
|
170
196
|
await (0, _files1.makeDir)(dstDir, true);
|
|
171
197
|
}
|
|
172
|
-
//
|
|
198
|
+
// Create or refresh lock
|
|
173
199
|
const dbFile = {
|
|
174
200
|
...space.dbFile,
|
|
175
|
-
path: _nodepath.default.join((0, _files1.dirName)(space.dbFile.path),
|
|
201
|
+
path: _nodepath.default.join((0, _files1.dirName)(space.dbFile.path), partFileName)
|
|
176
202
|
};
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
203
|
+
// Use a short TTL for the PATCH method (which is also used for refreshing)
|
|
204
|
+
const ttl = patch ? _cache.CACHE_LOCK_FILE_TTL : undefined;
|
|
205
|
+
const [created, fileLock] = await this.filesLockManager.createOrRefresh(user, dbFile, _webdav.DEPTH.RESOURCE, ttl);
|
|
206
|
+
// Do
|
|
180
207
|
try {
|
|
181
|
-
await (0,
|
|
182
|
-
highWaterMark: _files.DEFAULT_HIGH_WATER_MARK
|
|
183
|
-
}));
|
|
208
|
+
await (0, _files1.writeFromStream)(dstFile, part.file);
|
|
184
209
|
} finally{
|
|
185
|
-
|
|
210
|
+
if (!patch && created) {
|
|
211
|
+
// Remove the file lock only if it has not been refreshed
|
|
212
|
+
await this.filesLockManager.removeLock(fileLock.key);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (patch) {
|
|
216
|
+
break;
|
|
186
217
|
}
|
|
187
218
|
}
|
|
188
219
|
}
|
|
@@ -212,7 +243,7 @@ let FilesManager = class FilesManager {
|
|
|
212
243
|
const fileExtension = _nodepath.default.extname(space.realPath);
|
|
213
244
|
if (checkDocument && fileExtension !== '.txt' && Object.values(_samples.DOCUMENT_TYPE).indexOf(fileExtension) > -1) {
|
|
214
245
|
const srcSample = _nodepath.default.join(__dirname, `${_samples.SAMPLE_PATH_WITHOUT_EXT}${fileExtension}`);
|
|
215
|
-
return (0, _files1.
|
|
246
|
+
return (0, _files1.copyFileContent)(srcSample, space.realPath);
|
|
216
247
|
} else {
|
|
217
248
|
return (0, _files1.createEmptyFile)(space.realPath);
|
|
218
249
|
}
|
|
@@ -370,12 +401,12 @@ let FilesManager = class FilesManager {
|
|
|
370
401
|
...trashFileDB,
|
|
371
402
|
path: _nodepath.default.join((0, _files1.dirName)(trashFileDB.path), (0, _files1.fileName)(dstTrash.path))
|
|
372
403
|
};
|
|
373
|
-
this.filesQueries.moveFiles(trashFileDB, dstTrashFileDB, dstTrash.isDir)
|
|
404
|
+
await this.filesQueries.moveFiles(trashFileDB, dstTrashFileDB, dstTrash.isDir);
|
|
374
405
|
}
|
|
375
406
|
await (0, _files1.moveFiles)(space.realPath, trashFile, true);
|
|
376
407
|
} else {
|
|
377
|
-
|
|
378
|
-
|
|
408
|
+
// unsupported case: delete the file (this shouldn't happen)
|
|
409
|
+
this.logger.error(`Unable to find trash path for space - *${space.alias}* (${space.id}) : delete permanently : ${space.realPath}`);
|
|
379
410
|
forceDeleteInDB = true;
|
|
380
411
|
await (0, _files1.removeFiles)(space.realPath);
|
|
381
412
|
}
|
|
@@ -553,9 +584,73 @@ let FilesManager = class FilesManager {
|
|
|
553
584
|
throw new _fileerror.FileError(_common.HttpStatus.BAD_REQUEST, 'File is not an image');
|
|
554
585
|
}
|
|
555
586
|
}
|
|
556
|
-
|
|
587
|
+
async lock(user, space) {
|
|
588
|
+
const rExists = await (0, _files1.isPathExists)(space.realPath);
|
|
589
|
+
if (!rExists) {
|
|
590
|
+
this.logger.warn('Lock refresh must specify an existing resource');
|
|
591
|
+
throw new _fileerror.FileError(_common.HttpStatus.BAD_REQUEST, 'Lock refresh must specify an existing resource');
|
|
592
|
+
}
|
|
593
|
+
const [_created, lock] = await this.filesLockManager.createOrRefresh(user, space.dbFile, _webdav.DEPTH.RESOURCE, _cache.CACHE_LOCK_FILE_TTL);
|
|
594
|
+
return this.filesLockManager.convertLockToFileLockProps(lock);
|
|
595
|
+
}
|
|
596
|
+
async unlock(user, space, forceAsOwner = false) {
|
|
597
|
+
if (!await (0, _files1.isPathExists)(space.realPath)) {
|
|
598
|
+
this.logger.warn(`Unable to unlock: ${space.url} - resource does not exist`);
|
|
599
|
+
throw new _fileerror.FileError(_common.HttpStatus.BAD_REQUEST, 'Unlock must specify an existing resource');
|
|
600
|
+
}
|
|
601
|
+
const fileLocks = await this.filesLockManager.getLocksByPath(space.dbFile);
|
|
602
|
+
if (fileLocks.length === 0) {
|
|
603
|
+
this.logger.warn(`Unable to find lock: ${space.url} - resource does not exist`);
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
for (const lock of fileLocks){
|
|
607
|
+
if (forceAsOwner && space.dbFile?.ownerId === user.id || lock.owner.id === user.id) {
|
|
608
|
+
// Refresh if more than half of the TTL has passed
|
|
609
|
+
await this.filesLockManager.removeLock(lock.key);
|
|
610
|
+
} else {
|
|
611
|
+
throw new _filelockerror.LockConflict(lock, 'Conflicting lock');
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
async unlockRequest(user, space) {
|
|
616
|
+
const fileLocks = await this.filesLockManager.getLocksByPath(space.dbFile);
|
|
617
|
+
if (fileLocks.length === 0) {
|
|
618
|
+
this.logger.warn(`Unable to find lock: ${space.url} - resource does not exist`);
|
|
619
|
+
throw new _fileerror.FileError(_common.HttpStatus.NOT_FOUND, 'Lock not found');
|
|
620
|
+
}
|
|
621
|
+
for (const lock of fileLocks){
|
|
622
|
+
if (lock.owner.id !== user.id) {
|
|
623
|
+
const notification = {
|
|
624
|
+
app: _notifications.NOTIFICATION_APP.UNLOCK_REQUEST,
|
|
625
|
+
event: _notifications.NOTIFICATION_APP_EVENT.UNLOCK_REQUEST,
|
|
626
|
+
element: (0, _files1.fileName)(space.url),
|
|
627
|
+
url: (0, _files1.dirName)(space.url)
|
|
628
|
+
};
|
|
629
|
+
this.notificationsManager.create([
|
|
630
|
+
lock.owner.id
|
|
631
|
+
], notification, {
|
|
632
|
+
author: user,
|
|
633
|
+
currentUrl: this.contextManager.headerOriginUrl()
|
|
634
|
+
}).catch((e)=>this.logger.error(`${this.unlockRequest.name} - ${e}`));
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
async getSize(space) {
|
|
639
|
+
if (!await (0, _files1.isPathExists)(space.realPath)) {
|
|
640
|
+
throw new _fileerror.FileError(_common.HttpStatus.NOT_FOUND, 'Location not found');
|
|
641
|
+
}
|
|
642
|
+
if (await (0, _files1.isPathIsDir)(space.realPath)) {
|
|
643
|
+
return (await (0, _files1.dirSize)(space.realPath))[0];
|
|
644
|
+
} else {
|
|
645
|
+
return await (0, _files1.fileSize)(space.realPath);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
constructor(http, filesQueries, spacesManager, contextManager, notificationsManager, filesLockManager){
|
|
557
649
|
this.http = http;
|
|
558
650
|
this.filesQueries = filesQueries;
|
|
651
|
+
this.spacesManager = spacesManager;
|
|
652
|
+
this.contextManager = contextManager;
|
|
653
|
+
this.notificationsManager = notificationsManager;
|
|
559
654
|
this.filesLockManager = filesLockManager;
|
|
560
655
|
/* Spaces permissions are checked in the space guard, except for the copy/move destination */ this.logger = new _common.Logger(FilesManager.name);
|
|
561
656
|
}
|
|
@@ -566,6 +661,9 @@ FilesManager = _ts_decorate([
|
|
|
566
661
|
_ts_metadata("design:paramtypes", [
|
|
567
662
|
typeof _axios.HttpService === "undefined" ? Object : _axios.HttpService,
|
|
568
663
|
typeof _filesqueriesservice.FilesQueries === "undefined" ? Object : _filesqueriesservice.FilesQueries,
|
|
664
|
+
typeof _spacesmanagerservice.SpacesManager === "undefined" ? Object : _spacesmanagerservice.SpacesManager,
|
|
665
|
+
typeof _contextmanagerservice.ContextManager === "undefined" ? Object : _contextmanagerservice.ContextManager,
|
|
666
|
+
typeof _notificationsmanagerservice.NotificationsManager === "undefined" ? Object : _notificationsmanagerservice.NotificationsManager,
|
|
569
667
|
typeof _fileslockmanagerservice.FilesLockManager === "undefined" ? Object : _fileslockmanagerservice.FilesLockManager
|
|
570
668
|
])
|
|
571
669
|
], FilesManager);
|