@sync-in/server 1.9.6 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (344) hide show
  1. package/CHANGELOG.md +29 -4
  2. package/environment/environment.dist.yaml +15 -5
  3. package/package.json +13 -14
  4. package/server/app.bootstrap.js +1 -1
  5. package/server/app.bootstrap.js.map +1 -1
  6. package/server/app.constants.js +3 -2
  7. package/server/app.constants.js.map +1 -1
  8. package/server/applications/files/constants/cache.js +2 -5
  9. package/server/applications/files/constants/cache.js.map +1 -1
  10. package/server/applications/files/constants/files.js +4 -0
  11. package/server/applications/files/constants/files.js.map +1 -1
  12. package/server/applications/files/constants/operations.js +4 -0
  13. package/server/applications/files/constants/operations.js.map +1 -1
  14. package/server/applications/files/constants/routes.js +1 -26
  15. package/server/applications/files/constants/routes.js.map +1 -1
  16. package/server/applications/files/files.config.js +15 -39
  17. package/server/applications/files/files.config.js.map +1 -1
  18. package/server/applications/files/files.controller.js +4 -4
  19. package/server/applications/files/files.controller.js.map +1 -1
  20. package/server/applications/files/files.module.js +12 -9
  21. package/server/applications/files/files.module.js.map +1 -1
  22. package/server/applications/files/interfaces/file-lock.interface.js.map +1 -1
  23. package/server/applications/files/interfaces/file-props.interface.js.map +1 -1
  24. package/server/applications/files/modules/collabora-online/collabora-online-environment.decorator.js +32 -0
  25. package/server/applications/files/modules/collabora-online/collabora-online-environment.decorator.js.map +1 -0
  26. package/server/applications/files/modules/collabora-online/collabora-online-manager.service.js +280 -0
  27. package/server/applications/files/modules/collabora-online/collabora-online-manager.service.js.map +1 -0
  28. package/server/applications/files/modules/collabora-online/collabora-online-manager.service.spec.js +552 -0
  29. package/server/applications/files/modules/collabora-online/collabora-online-manager.service.spec.js.map +1 -0
  30. package/server/applications/files/modules/collabora-online/collabora-online.config.js +40 -0
  31. package/server/applications/files/modules/collabora-online/collabora-online.config.js.map +1 -0
  32. package/server/applications/files/modules/collabora-online/collabora-online.constants.js +110 -0
  33. package/server/applications/files/modules/collabora-online/collabora-online.constants.js.map +1 -0
  34. package/server/applications/files/modules/collabora-online/collabora-online.controller.js +128 -0
  35. package/server/applications/files/modules/collabora-online/collabora-online.controller.js.map +1 -0
  36. package/server/applications/files/modules/collabora-online/collabora-online.controller.spec.js +47 -0
  37. package/server/applications/files/modules/collabora-online/collabora-online.controller.spec.js.map +1 -0
  38. package/server/applications/files/{interfaces/only-office-config.interface.js → modules/collabora-online/collabora-online.dtos.js} +1 -1
  39. package/server/applications/files/modules/collabora-online/collabora-online.dtos.js.map +1 -0
  40. package/server/applications/files/{guards/files-only-office.guard.js → modules/collabora-online/collabora-online.guard.js} +7 -21
  41. package/server/applications/files/modules/collabora-online/collabora-online.guard.js.map +1 -0
  42. package/server/applications/files/modules/collabora-online/collabora-online.guard.spec.js +86 -0
  43. package/server/applications/files/modules/collabora-online/collabora-online.guard.spec.js.map +1 -0
  44. package/server/applications/files/modules/collabora-online/collabora-online.interface.js +10 -0
  45. package/server/applications/files/modules/collabora-online/collabora-online.interface.js.map +1 -0
  46. package/server/applications/files/modules/collabora-online/collabora-online.module.js +41 -0
  47. package/server/applications/files/modules/collabora-online/collabora-online.module.js.map +1 -0
  48. package/server/applications/files/modules/collabora-online/collabora-online.routes.js +35 -0
  49. package/server/applications/files/modules/collabora-online/collabora-online.routes.js.map +1 -0
  50. package/server/applications/files/modules/collabora-online/collabora-online.strategy.js +59 -0
  51. package/server/applications/files/modules/collabora-online/collabora-online.strategy.js.map +1 -0
  52. package/server/applications/files/modules/collabora-online/collabora-online.utils.js +28 -0
  53. package/server/applications/files/modules/collabora-online/collabora-online.utils.js.map +1 -0
  54. package/server/applications/files/{decorators → modules/only-office}/only-office-environment.decorator.js +5 -5
  55. package/server/applications/files/modules/only-office/only-office-environment.decorator.js.map +1 -0
  56. package/server/applications/files/{services/files-only-office-manager.service.js → modules/only-office/only-office-manager.service.js} +101 -97
  57. package/server/applications/files/modules/only-office/only-office-manager.service.js.map +1 -0
  58. package/server/applications/files/modules/only-office/only-office-manager.service.spec.js +477 -0
  59. package/server/applications/files/modules/only-office/only-office-manager.service.spec.js.map +1 -0
  60. package/server/applications/files/modules/only-office/only-office.config.js +51 -0
  61. package/server/applications/files/modules/only-office/only-office.config.js.map +1 -0
  62. package/server/applications/files/modules/only-office/only-office.constants.js +417 -0
  63. package/server/applications/files/modules/only-office/only-office.constants.js.map +1 -0
  64. package/server/applications/files/{files-only-office.controller.js → modules/only-office/only-office.controller.js} +35 -52
  65. package/server/applications/files/modules/only-office/only-office.controller.js.map +1 -0
  66. package/server/applications/files/{files-only-office.controller.spec.js → modules/only-office/only-office.controller.spec.js} +24 -21
  67. package/server/applications/files/modules/only-office/only-office.controller.spec.js.map +1 -0
  68. package/server/applications/files/modules/only-office/only-office.dtos.js +10 -0
  69. package/server/applications/files/modules/only-office/only-office.dtos.js.map +1 -0
  70. package/server/applications/files/modules/only-office/only-office.guard.js +40 -0
  71. package/server/applications/files/modules/only-office/only-office.guard.js.map +1 -0
  72. package/server/applications/files/{guards/files-only-office.guard.spec.js → modules/only-office/only-office.guard.spec.js} +15 -21
  73. package/server/applications/files/modules/only-office/only-office.guard.spec.js.map +1 -0
  74. package/server/applications/files/modules/only-office/only-office.interface.js +10 -0
  75. package/server/applications/files/modules/only-office/only-office.interface.js.map +1 -0
  76. package/server/applications/files/modules/only-office/only-office.module.js +41 -0
  77. package/server/applications/files/modules/only-office/only-office.module.js.map +1 -0
  78. package/server/applications/files/modules/only-office/only-office.routes.js +45 -0
  79. package/server/applications/files/modules/only-office/only-office.routes.js.map +1 -0
  80. package/server/applications/files/{guards/files-only-office.strategy.js → modules/only-office/only-office.strategy.js} +11 -11
  81. package/server/applications/files/modules/only-office/only-office.strategy.js.map +1 -0
  82. package/server/applications/files/services/files-lock-manager.service.js +25 -33
  83. package/server/applications/files/services/files-lock-manager.service.js.map +1 -1
  84. package/server/applications/files/services/files-manager.service.js +17 -16
  85. package/server/applications/files/services/files-manager.service.js.map +1 -1
  86. package/server/applications/files/services/files-methods.service.js +2 -2
  87. package/server/applications/files/services/files-methods.service.js.map +1 -1
  88. package/server/applications/files/services/files-methods.service.spec.js +3 -1
  89. package/server/applications/files/services/files-methods.service.spec.js.map +1 -1
  90. package/server/applications/files/services/files-scheduler.service.js +2 -2
  91. package/server/applications/files/services/files-scheduler.service.js.map +1 -1
  92. package/server/applications/files/utils/files.js +10 -2
  93. package/server/applications/files/utils/files.js.map +1 -1
  94. package/server/applications/links/constants/routes.js +5 -0
  95. package/server/applications/links/constants/routes.js.map +1 -1
  96. package/server/applications/links/interfaces/link-space.interface.js.map +1 -1
  97. package/server/applications/links/links.controller.js +25 -5
  98. package/server/applications/links/links.controller.js.map +1 -1
  99. package/server/applications/links/services/links-manager.service.js +43 -21
  100. package/server/applications/links/services/links-manager.service.js.map +1 -1
  101. package/server/applications/links/services/links-manager.service.spec.js +4 -3
  102. package/server/applications/links/services/links-manager.service.spec.js.map +1 -1
  103. package/server/applications/links/services/links-queries.service.js +9 -2
  104. package/server/applications/links/services/links-queries.service.js.map +1 -1
  105. package/server/applications/shares/interfaces/share-link.interface.js.map +1 -1
  106. package/server/applications/shares/services/shares-manager.service.js +3 -0
  107. package/server/applications/shares/services/shares-manager.service.js.map +1 -1
  108. package/server/applications/shares/services/shares-manager.service.spec.js +2 -1
  109. package/server/applications/shares/services/shares-manager.service.spec.js.map +1 -1
  110. package/server/applications/shares/services/shares-queries.service.js +1 -0
  111. package/server/applications/shares/services/shares-queries.service.js.map +1 -1
  112. package/server/applications/spaces/constants/spaces.js +2 -2
  113. package/server/applications/spaces/constants/spaces.js.map +1 -1
  114. package/server/applications/spaces/decorators/space-override-permission.decorator.js +18 -0
  115. package/server/applications/spaces/decorators/space-override-permission.decorator.js.map +1 -0
  116. package/server/applications/spaces/guards/space.guard.js +40 -33
  117. package/server/applications/spaces/guards/space.guard.js.map +1 -1
  118. package/server/applications/spaces/guards/space.guard.spec.js +10 -15
  119. package/server/applications/spaces/guards/space.guard.spec.js.map +1 -1
  120. package/server/applications/webdav/constants/webdav.js +4 -0
  121. package/server/applications/webdav/constants/webdav.js.map +1 -1
  122. package/server/applications/webdav/guards/webdav-protocol.guard.js +9 -8
  123. package/server/applications/webdav/guards/webdav-protocol.guard.js.map +1 -1
  124. package/server/applications/webdav/guards/webdav-protocol.guard.spec.js +1 -1
  125. package/server/applications/webdav/guards/webdav-protocol.guard.spec.js.map +1 -1
  126. package/server/applications/webdav/interfaces/webdav.interface.js.map +1 -1
  127. package/server/applications/webdav/services/webdav-methods.service.js +40 -17
  128. package/server/applications/webdav/services/webdav-methods.service.js.map +1 -1
  129. package/server/applications/webdav/services/webdav-methods.service.spec.js +2157 -1289
  130. package/server/applications/webdav/services/webdav-methods.service.spec.js.map +1 -1
  131. package/server/applications/webdav/utils/webdav.js +8 -4
  132. package/server/applications/webdav/utils/webdav.js.map +1 -1
  133. package/server/applications/webdav/webdav.controller.js +4 -4
  134. package/server/applications/webdav/webdav.controller.js.map +1 -1
  135. package/server/authentication/guards/auth-token-access.guard.js +8 -3
  136. package/server/authentication/guards/auth-token-access.guard.js.map +1 -1
  137. package/server/authentication/services/auth-methods/auth-method-two-fa.service.js +1 -1
  138. package/server/authentication/services/auth-methods/auth-method-two-fa.service.js.map +1 -1
  139. package/server/authentication/services/auth-methods/auth-method-two-fa.service.spec.js +350 -4
  140. package/server/authentication/services/auth-methods/auth-method-two-fa.service.spec.js.map +1 -1
  141. package/server/configuration/config.environment.js +5 -1
  142. package/server/configuration/config.environment.js.map +1 -1
  143. package/server/configuration/config.interfaces.js.map +1 -1
  144. package/static/3rdpartylicenses.txt +507 -507
  145. package/static/assets/pdfjs/build/pdf.mjs +93 -33
  146. package/static/assets/pdfjs/build/pdf.mjs.map +1 -1
  147. package/static/assets/pdfjs/build/pdf.sandbox.mjs +3 -3
  148. package/static/assets/pdfjs/build/pdf.sandbox.mjs.map +1 -1
  149. package/static/assets/pdfjs/build/pdf.worker.mjs +166 -54
  150. package/static/assets/pdfjs/build/pdf.worker.mjs.map +1 -1
  151. package/static/assets/pdfjs/version +1 -1
  152. package/static/assets/pdfjs/web/images/checkmark.svg +5 -0
  153. package/static/assets/pdfjs/web/images/pages_closeButton.svg +3 -0
  154. package/static/assets/pdfjs/web/images/pages_selected.svg +7 -0
  155. package/static/assets/pdfjs/web/images/pages_viewArrow.svg +3 -0
  156. package/static/assets/pdfjs/web/images/pages_viewButton.svg +3 -0
  157. package/static/assets/pdfjs/web/locale/be/viewer.ftl +0 -2
  158. package/static/assets/pdfjs/web/locale/bs/viewer.ftl +0 -5
  159. package/static/assets/pdfjs/web/locale/cs/viewer.ftl +4 -6
  160. package/static/assets/pdfjs/web/locale/cy/viewer.ftl +0 -2
  161. package/static/assets/pdfjs/web/locale/da/viewer.ftl +0 -2
  162. package/static/assets/pdfjs/web/locale/de/viewer.ftl +0 -2
  163. package/static/assets/pdfjs/web/locale/dsb/viewer.ftl +0 -2
  164. package/static/assets/pdfjs/web/locale/el/viewer.ftl +0 -2
  165. package/static/assets/pdfjs/web/locale/en-CA/viewer.ftl +6 -2
  166. package/static/assets/pdfjs/web/locale/en-GB/viewer.ftl +0 -2
  167. package/static/assets/pdfjs/web/locale/en-US/viewer.ftl +82 -17
  168. package/static/assets/pdfjs/web/locale/eo/viewer.ftl +0 -2
  169. package/static/assets/pdfjs/web/locale/es-AR/viewer.ftl +0 -2
  170. package/static/assets/pdfjs/web/locale/es-CL/viewer.ftl +0 -2
  171. package/static/assets/pdfjs/web/locale/es-ES/viewer.ftl +0 -2
  172. package/static/assets/pdfjs/web/locale/es-MX/viewer.ftl +0 -2
  173. package/static/assets/pdfjs/web/locale/eu/viewer.ftl +0 -2
  174. package/static/assets/pdfjs/web/locale/fi/viewer.ftl +0 -2
  175. package/static/assets/pdfjs/web/locale/fr/viewer.ftl +0 -2
  176. package/static/assets/pdfjs/web/locale/fur/viewer.ftl +0 -5
  177. package/static/assets/pdfjs/web/locale/fy-NL/viewer.ftl +3 -5
  178. package/static/assets/pdfjs/web/locale/gn/viewer.ftl +0 -2
  179. package/static/assets/pdfjs/web/locale/he/viewer.ftl +0 -2
  180. package/static/assets/pdfjs/web/locale/hr/viewer.ftl +66 -0
  181. package/static/assets/pdfjs/web/locale/hsb/viewer.ftl +0 -2
  182. package/static/assets/pdfjs/web/locale/hu/viewer.ftl +0 -2
  183. package/static/assets/pdfjs/web/locale/hy-AM/viewer.ftl +3 -8
  184. package/static/assets/pdfjs/web/locale/ia/viewer.ftl +0 -2
  185. package/static/assets/pdfjs/web/locale/id/viewer.ftl +0 -5
  186. package/static/assets/pdfjs/web/locale/is/viewer.ftl +0 -5
  187. package/static/assets/pdfjs/web/locale/it/viewer.ftl +0 -2
  188. package/static/assets/pdfjs/web/locale/ja/viewer.ftl +0 -14
  189. package/static/assets/pdfjs/web/locale/ka/viewer.ftl +4 -6
  190. package/static/assets/pdfjs/web/locale/kab/viewer.ftl +0 -5
  191. package/static/assets/pdfjs/web/locale/kk/viewer.ftl +0 -2
  192. package/static/assets/pdfjs/web/locale/ko/viewer.ftl +0 -2
  193. package/static/assets/pdfjs/web/locale/nb-NO/viewer.ftl +1 -3
  194. package/static/assets/pdfjs/web/locale/nl/viewer.ftl +0 -2
  195. package/static/assets/pdfjs/web/locale/nn-NO/viewer.ftl +4 -2
  196. package/static/assets/pdfjs/web/locale/pa-IN/viewer.ftl +0 -2
  197. package/static/assets/pdfjs/web/locale/pl/viewer.ftl +0 -2
  198. package/static/assets/pdfjs/web/locale/pt-BR/viewer.ftl +0 -2
  199. package/static/assets/pdfjs/web/locale/pt-PT/viewer.ftl +35 -0
  200. package/static/assets/pdfjs/web/locale/rm/viewer.ftl +0 -5
  201. package/static/assets/pdfjs/web/locale/ro/viewer.ftl +4 -6
  202. package/static/assets/pdfjs/web/locale/ru/viewer.ftl +3 -5
  203. package/static/assets/pdfjs/web/locale/sk/viewer.ftl +0 -2
  204. package/static/assets/pdfjs/web/locale/sl/viewer.ftl +0 -2
  205. package/static/assets/pdfjs/web/locale/sq/viewer.ftl +0 -2
  206. package/static/assets/pdfjs/web/locale/sv-SE/viewer.ftl +0 -2
  207. package/static/assets/pdfjs/web/locale/tg/viewer.ftl +0 -2
  208. package/static/assets/pdfjs/web/locale/th/viewer.ftl +2 -2
  209. package/static/assets/pdfjs/web/locale/tr/viewer.ftl +0 -2
  210. package/static/assets/pdfjs/web/locale/vi/viewer.ftl +0 -2
  211. package/static/assets/pdfjs/web/locale/zh-CN/viewer.ftl +0 -2
  212. package/static/assets/pdfjs/web/locale/zh-TW/viewer.ftl +0 -2
  213. package/static/assets/pdfjs/web/viewer.css +1778 -835
  214. package/static/assets/pdfjs/web/viewer.html +167 -86
  215. package/static/assets/pdfjs/web/viewer.mjs +1106 -801
  216. package/static/assets/pdfjs/web/viewer.mjs.map +1 -1
  217. package/static/chunk-27V66YJV.js +2 -0
  218. package/static/{chunk-BVKDW5XO.js → chunk-27Z3SYRL.js} +1 -1
  219. package/static/{chunk-HLKZCMKV.js → chunk-2RWLNKZH.js} +1 -1
  220. package/static/chunk-2YQ4SX3A.js +13 -0
  221. package/static/{chunk-GENTF6JM.js → chunk-3JYMJQYT.js} +1 -1
  222. package/static/chunk-3QTROEHV.js +1 -0
  223. package/static/{chunk-C5T7RZSD.js → chunk-3RPUQ22U.js} +1 -1
  224. package/static/{chunk-EPDWJEPD.js → chunk-3WZ6F3LC.js} +1 -1
  225. package/static/chunk-3ZLBVUCX.js +2 -0
  226. package/static/{chunk-SF6Q6VRC.js → chunk-45AZ6ZML.js} +1 -1
  227. package/static/chunk-46TJLPJY.js +1 -0
  228. package/static/chunk-4NIYCYRS.js +2 -0
  229. package/static/{chunk-GLPKRULI.js → chunk-4TPFERL6.js} +1 -1
  230. package/static/{chunk-KAAFVHYE.js → chunk-5O66CLTD.js} +1 -1
  231. package/static/chunk-6OEOADR6.js +1 -0
  232. package/static/chunk-6WMXMIE4.js +1 -0
  233. package/static/{chunk-QKMN3S4M.js → chunk-7VRYTDX4.js} +1 -1
  234. package/static/{chunk-Z2KBIZ5D.js → chunk-ARS47O5X.js} +1 -1
  235. package/static/chunk-B6HQYQYG.js +1 -0
  236. package/static/chunk-BCN4T5DO.js +2 -0
  237. package/static/{chunk-7HL5Z6PF.js → chunk-CCZWPM7Q.js} +1 -1
  238. package/static/{chunk-DU4Q4RWJ.js → chunk-CMNMPG6Z.js} +1 -1
  239. package/static/{chunk-BX3QZ7IL.js → chunk-CSVPAZHK.js} +1 -1
  240. package/static/{chunk-BJARRIS6.js → chunk-D55YR5X7.js} +4 -4
  241. package/static/{chunk-NHMYAVJK.js → chunk-D5FQ72R4.js} +1 -1
  242. package/static/{chunk-7QYALK5T.js → chunk-DGCVA6BM.js} +1 -1
  243. package/static/{chunk-IBC7CFBQ.js → chunk-DVCN3P7Q.js} +1 -1
  244. package/static/chunk-E32J777S.js +5 -0
  245. package/static/{chunk-FEQUP26G.js → chunk-FIUF2JM4.js} +1 -1
  246. package/static/{chunk-ODAQRAPO.js → chunk-G3PL6YX3.js} +1 -1
  247. package/static/chunk-G7RZN7HN.js +1 -0
  248. package/static/{chunk-IIKL33TV.js → chunk-GQHXYX6Z.js} +1 -1
  249. package/static/{chunk-5HYSNQR4.js → chunk-GWRAGN3M.js} +1 -1
  250. package/static/{chunk-ANH4VNOS.js → chunk-GXWGB7WO.js} +1 -1
  251. package/static/{chunk-25PWAXTJ.js → chunk-HGODIZTV.js} +1 -1
  252. package/static/{chunk-X5UDV4ZB.js → chunk-HZAB6F4Q.js} +1 -1
  253. package/static/chunk-I3FR3A45.js +1 -0
  254. package/static/{chunk-JYHTSSKW.js → chunk-I5SPA4G2.js} +1 -1
  255. package/static/{chunk-DQAQUSVW.js → chunk-IMFO2MI7.js} +1 -1
  256. package/static/{chunk-AYYJZMBE.js → chunk-JNTNMIUH.js} +1 -1
  257. package/static/chunk-JRXG43AA.js +2 -0
  258. package/static/{chunk-3AR5VNJE.js → chunk-KAUCN24H.js} +1 -1
  259. package/static/{chunk-3WS72A6C.js → chunk-KDUAB76O.js} +1 -1
  260. package/static/chunk-KPOQLDWF.js +1 -0
  261. package/static/{chunk-UO7ATVQG.js → chunk-KWFELZTM.js} +1 -1
  262. package/static/{chunk-2CAAJBRO.js → chunk-L3BIP4AA.js} +1 -1
  263. package/static/{chunk-DK2LAJEL.js → chunk-LGIVVJDD.js} +1 -1
  264. package/static/{chunk-5ATJIR5S.js → chunk-LNLBIJZD.js} +1 -1
  265. package/static/chunk-LTJNLOX2.js +1 -0
  266. package/static/{chunk-GRV44RYI.js → chunk-LZUHREOF.js} +1 -1
  267. package/static/{chunk-XIQXRSZ2.js → chunk-NIR4YE2E.js} +1 -1
  268. package/static/{chunk-TOCCCZP2.js → chunk-NJJURHX4.js} +1 -1
  269. package/static/chunk-NNZWSNAW.js +1 -0
  270. package/static/chunk-NWKBB7J4.js +1 -0
  271. package/static/chunk-O3YLAEVE.js +3 -0
  272. package/static/chunk-OUHCDDT6.js +1 -0
  273. package/static/{chunk-B4TDS6AQ.js → chunk-PDG7DOEF.js} +1 -1
  274. package/static/chunk-POUWUMC4.js +1 -0
  275. package/static/{chunk-NQCKX2AD.js → chunk-PPJCVBJH.js} +1 -1
  276. package/static/{chunk-ZCOEP4O2.js → chunk-PQZLR4P3.js} +1 -1
  277. package/static/chunk-PVYVY3GD.js +1 -0
  278. package/static/chunk-Q5X5TPAG.js +1 -0
  279. package/static/{chunk-LFAQLJZK.js → chunk-QHJT5H4M.js} +1 -1
  280. package/static/{chunk-D6QWQHWE.js → chunk-R4VMWCM5.js} +1 -1
  281. package/static/{chunk-O233BXWK.js → chunk-R7PLNX75.js} +1 -1
  282. package/static/chunk-RJULB733.js +1 -0
  283. package/static/{chunk-E5C4QRNQ.js → chunk-RNVPQQKT.js} +5 -5
  284. package/static/chunk-RTNEBRKJ.js +1 -0
  285. package/static/{chunk-GYYJ4FWN.js → chunk-S3TTWPQA.js} +1 -1
  286. package/static/{chunk-4GBA6EJ4.js → chunk-SDJNZULP.js} +1 -1
  287. package/static/chunk-SNOOCDJD.js +1 -0
  288. package/static/chunk-T42BV6TR.js +1 -0
  289. package/static/{chunk-5KVI243T.js → chunk-TNCKNU6I.js} +1 -1
  290. package/static/{chunk-OVUMPMVM.js → chunk-ULSPQ3HP.js} +1 -1
  291. package/static/{chunk-5NFH4E2B.js → chunk-UOK3LKSX.js} +1 -1
  292. package/static/{chunk-CHMDM2ZW.js → chunk-VD5JHSDS.js} +1 -1
  293. package/static/{chunk-2F42MZQ5.js → chunk-XBKCQCBI.js} +1 -1
  294. package/static/{chunk-2U5VKTML.js → chunk-XEWLBWFF.js} +1 -1
  295. package/static/{chunk-FSGT46LM.js → chunk-XTVNHFKX.js} +1 -1
  296. package/static/chunk-ZCSHU3D7.js +1 -0
  297. package/static/{chunk-QUUQOBTF.js → chunk-ZEJLIGAY.js} +1 -1
  298. package/static/{chunk-7H5O4BLV.js → chunk-ZHOE5VEY.js} +1 -1
  299. package/static/chunk-ZOMRIN3G.js +2 -0
  300. package/static/index.html +2 -2
  301. package/static/main-YKDNJ7LK.js +11 -0
  302. package/static/{styles-S5HVK4H5.css → styles-XLLEY5Y3.css} +1 -1
  303. package/server/applications/files/constants/only-office.js +0 -531
  304. package/server/applications/files/constants/only-office.js.map +0 -1
  305. package/server/applications/files/decorators/only-office-environment.decorator.js.map +0 -1
  306. package/server/applications/files/files-only-office.controller.js.map +0 -1
  307. package/server/applications/files/files-only-office.controller.spec.js.map +0 -1
  308. package/server/applications/files/guards/files-only-office.guard.js.map +0 -1
  309. package/server/applications/files/guards/files-only-office.guard.spec.js.map +0 -1
  310. package/server/applications/files/guards/files-only-office.strategy.js.map +0 -1
  311. package/server/applications/files/interfaces/only-office-config.interface.js.map +0 -1
  312. package/server/applications/files/services/files-only-office-manager.service.js.map +0 -1
  313. package/server/applications/files/services/files-only-office-manager.service.spec.js +0 -58
  314. package/server/applications/files/services/files-only-office-manager.service.spec.js.map +0 -1
  315. package/static/chunk-42L6C5MT.js +0 -1
  316. package/static/chunk-4ZKAVMB4.js +0 -1
  317. package/static/chunk-5GIWZKNS.js +0 -1
  318. package/static/chunk-5WCQBTXW.js +0 -1
  319. package/static/chunk-B2A4HNDC.js +0 -1
  320. package/static/chunk-BSB4VROD.js +0 -2
  321. package/static/chunk-CUC7R6C2.js +0 -1
  322. package/static/chunk-DHFQIFOF.js +0 -1
  323. package/static/chunk-DRHPEERW.js +0 -2
  324. package/static/chunk-FCGTI42I.js +0 -1
  325. package/static/chunk-FCR5AEHR.js +0 -3
  326. package/static/chunk-HB5DC7RJ.js +0 -1
  327. package/static/chunk-ITVA26X2.js +0 -2
  328. package/static/chunk-KWKZN53T.js +0 -1
  329. package/static/chunk-LBXOAKBD.js +0 -1
  330. package/static/chunk-LZKI5P5T.js +0 -1
  331. package/static/chunk-MGMDT4VN.js +0 -1
  332. package/static/chunk-MWUUM2NK.js +0 -13
  333. package/static/chunk-MYM43ENO.js +0 -1
  334. package/static/chunk-NAH4V2R6.js +0 -2
  335. package/static/chunk-PCFH5HCI.js +0 -2
  336. package/static/chunk-Q6B4OVER.js +0 -5
  337. package/static/chunk-QV5LQKTS.js +0 -1
  338. package/static/chunk-S4UTSOPV.js +0 -1
  339. package/static/chunk-SRBOO7AO.js +0 -1
  340. package/static/chunk-VZPCXSRG.js +0 -2
  341. package/static/chunk-XKEBQNQJ.js +0 -1
  342. package/static/chunk-YYTDPI5S.js +0 -1
  343. package/static/main-ODUA232E.js +0 -11
  344. /package/static/assets/pdfjs/web/images/{toolbarButton-sidebarToggle.svg → toolbarButton-viewsManagerToggle.svg} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../backend/src/applications/files/utils/files.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 { HttpStatus } from '@nestjs/common'\nimport { WriteStream } from 'fs'\nimport fse from 'fs-extra'\nimport mime from 'mime-types'\nimport crypto from 'node:crypto'\nimport { createReadStream, createWriteStream, Dirent, statSync } from 'node:fs'\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { Readable } from 'node:stream'\nimport { pipeline } from 'node:stream/promises'\nimport { formatDateISOString } from '../../../common/functions'\nimport { currentTimeStamp, isValidFileName, regExpPreventPathTraversal } from '../../../common/shared'\nimport { DEFAULT_HIGH_WATER_MARK, EXTRA_MIMES_TYPE } from '../constants/files'\nimport type { FileProps } from '../interfaces/file-props.interface'\nimport { FileError } from '../models/file-error'\n\nexport function sanitizePath(fPath: string): string {\n return path.normalize(fPath).replace(regExpPreventPathTraversal, '')\n}\n\nexport function sanitizeName(name: string): string {\n return name\n .replace(/^\\s+|[. ]+$/g, '') // trimStart + trimEnd + strip trailing dots\n .replace(/[/\\\\]/g, '') // remove slashes\n .replace(/\\.\\./g, '') // remove '..'\n}\n\nexport function checkFileName(fPath: string): string {\n const fName = fileName(fPath)\n try {\n isValidFileName(fName)\n return fName\n } catch {\n throw new FileError(HttpStatus.BAD_REQUEST, 'Forbidden characters')\n }\n}\n\nexport function isPathExists(rPath: string): Promise<boolean> {\n return fse.pathExists(rPath)\n}\n\nexport async function isPathIsReadable(rPath: string): Promise<boolean> {\n try {\n await fs.access(rPath, fs.constants.R_OK)\n } catch {\n return false\n }\n return true\n}\n\nexport async function isPathIsWriteable(rPath: string): Promise<boolean> {\n try {\n await fs.access(rPath, fs.constants.W_OK)\n } catch {\n return false\n }\n return true\n}\n\nexport async function isPathIsDir(rPath: string): Promise<boolean> {\n return (await fs.stat(rPath)).isDirectory()\n}\n\nexport function fileName(fPath: string): string {\n return path.posix.basename(fPath)\n}\n\nexport function dirName(fPath: string): string {\n return path.dirname(fPath)\n}\n\nexport async function fileSize(rPath: string): Promise<number> {\n return (await fs.stat(rPath)).size\n}\n\nexport function createEmptyFile(rPath: string): Promise<void> {\n return fs.writeFile(rPath, '')\n}\n\nexport function makeDir(rPath: string, recursive?: boolean): Promise<string> {\n return fs.mkdir(rPath, { recursive: recursive })\n}\n\nexport function getMimeType(fPath: string, isDir: boolean): string {\n if (isDir) {\n return 'directory'\n }\n const extName: string = path.extname(fPath)\n if (EXTRA_MIMES_TYPE.has(extName)) {\n return EXTRA_MIMES_TYPE.get(extName)\n }\n const m = mime.lookup(extName)\n if (m) {\n return m.replace('/', '-')\n }\n return 'file'\n}\n\nexport function genEtag(file?: Pick<FileProps, 'size' | 'mtime'>, rPath?: string): string {\n if (!file) {\n if (!rPath) throw new Error('File or path are missing')\n const stats = statSync(rPath)\n file = { size: stats.size, mtime: stats.mtime.getTime() }\n }\n return `W/\"${file.size.toString(16)}-${file.mtime.toString(16)}\"`\n}\n\nexport function removeFiles(rPath: string): Promise<void> {\n // if the file does not exist, no error is thrown\n return fse.remove(rPath)\n}\n\nexport async function getProps(rPath: string, fPath?: string, isDir?: boolean): Promise<FileProps> {\n const stats = await fs.stat(rPath)\n const isDirectory = isDir === undefined ? stats.isDirectory() : isDir\n return {\n id: -stats.ino, // use negative number to avoid conflicts with existing database ids\n path: dirName(fPath !== undefined ? fPath : rPath),\n name: fileName(fPath !== undefined ? fPath : rPath),\n isDir: isDirectory,\n size: isDirectory ? 0 : stats.size,\n ctime: stats.birthtime.getTime(),\n mtime: stats.mtime.getTime(),\n mime: getMimeType(rPath, isDirectory)\n }\n}\n\nexport function touchFile(rPath: string, mtime?: number): Promise<void> {\n if (!mtime) mtime = currentTimeStamp()\n return fs.utimes(rPath, mtime, mtime)\n}\n\nexport async function copyFiles(srcPath: string, dstPath: string, overwrite = false, recursive = true, preserveTimestamps = true): Promise<void> {\n /*\n If src is a directory it will copy everything inside of this directory, not the entire directory itself\n If src is a file, dest cannot be a directory\n */\n if (!recursive && (await isPathIsDir(srcPath))) {\n await fs.mkdir(dstPath)\n if (preserveTimestamps) {\n const stat = await fs.stat(srcPath)\n await fs.utimes(dstPath, stat.atime, stat.mtime)\n }\n } else {\n await fse.copy(srcPath, dstPath, { overwrite, preserveTimestamps: preserveTimestamps })\n }\n}\n\nexport function moveFiles(srcPath: string, dstPath: string, overwrite = false): Promise<void> {\n /*\n If src is a file, dest must be a file and when src is a directory, dest must be a directory\n */\n return fse.move(srcPath, dstPath, { overwrite })\n}\n\nexport async function checksumFile(filePath: string, alg: string): Promise<string> {\n const hash = crypto.createHash(alg)\n const stream = createReadStream(filePath, { highWaterMark: DEFAULT_HIGH_WATER_MARK })\n await pipeline(stream, hash)\n return hash.digest('hex')\n}\n\nexport function writeFromStream(rPath: string, stream: Readable, start: number = 0): Promise<void> {\n const dst: WriteStream = createWriteStream(rPath, { flags: start ? 'a' : 'w', start: start, highWaterMark: DEFAULT_HIGH_WATER_MARK })\n return pipeline(stream, dst)\n}\n\nexport async function writeFromStreamAndChecksum(rPath: string, stream: Readable, hasRange: number, alg: string): Promise<string> {\n const hash = crypto.createHash(alg)\n if (hasRange) {\n const src = createReadStream(rPath, { highWaterMark: DEFAULT_HIGH_WATER_MARK })\n await pipeline(src, hash, { end: false })\n }\n const dst = createWriteStream(rPath, { flags: hasRange ? 'a' : 'w', highWaterMark: DEFAULT_HIGH_WATER_MARK })\n await pipeline(\n stream,\n async function* (source) {\n for await (const chunk of source) {\n hash.update(chunk)\n yield chunk\n }\n },\n dst\n )\n hash.end()\n return hash.digest('hex')\n}\n\nexport function copyFileContent(srcPath: string, dstPath: string): Promise<void> {\n const srcStream = createReadStream(srcPath, { highWaterMark: DEFAULT_HIGH_WATER_MARK })\n return writeFromStream(dstPath, srcStream)\n}\n\nexport async function dirSize(rPath: string): Promise<[number, any]> {\n let size = 0\n const errors: Record<string, string> = {}\n for (const f of await fs.readdir(rPath, { withFileTypes: true, recursive: true })) {\n if (f.isFile()) {\n const p = path.join(f.parentPath, f.name)\n try {\n size += (await fs.stat(p)).size\n } catch (e: any) {\n errors[p] = e.message\n }\n }\n }\n return [size, errors]\n}\n\nexport async function dirListFileNames(rPath: string): Promise<string[]> {\n return (await fs.readdir(rPath)).map((path: string) => fileName(path))\n}\n\nexport async function countDirEntries(rPath: string): Promise<{ files: number; directories: number }> {\n return (await fs.readdir(rPath, { withFileTypes: true, recursive: true })).reduce(\n (acc, f: Dirent) => {\n if (f.isDirectory()) {\n acc.directories++\n } else {\n acc.files++\n }\n return acc\n },\n { files: 0, directories: 0 }\n )\n}\n\nexport async function dirHasChildren(rPath: string, mustContainsDirs = true): Promise<boolean> {\n for await (const file of await fs.opendir(rPath)) {\n if (mustContainsDirs) {\n if (file.isDirectory()) return true\n } else {\n return true\n }\n }\n return false\n}\n\nexport async function uniqueFilePathFromDir(rPath: string): Promise<string> {\n if (await isPathExists(rPath)) {\n const parentDir = path.dirname(rPath)\n const extension = path.extname(rPath)\n const nameWithoutExtension = path.basename(rPath, extension)\n let count = 1\n while (await isPathExists(path.join(parentDir, `${nameWithoutExtension} (${count})${extension}`))) {\n count++\n }\n return path.join(parentDir, `${nameWithoutExtension} (${count})${extension}`)\n }\n return rPath\n}\n\nexport async function uniqueDatedFilePath(rPath: string): Promise<{ isDir: boolean; path: string }> {\n const date = formatDateISOString(new Date())\n if (await isPathIsDir(rPath)) {\n return { isDir: true, path: `${rPath}-${date}` }\n } else {\n const extension = path.extname(rPath)\n const nameWithoutExtension = path.basename(rPath, extension)\n return { isDir: false, path: path.join(path.dirname(rPath), `${nameWithoutExtension}-${date}${extension}`) }\n }\n}\n\nexport async function checkExternalPath(rPath: string) {\n if (!(await isPathExists(rPath))) {\n throw new FileError(HttpStatus.NOT_FOUND, 'The location does not exist')\n }\n if (!(await isPathIsReadable(rPath))) {\n throw new FileError(HttpStatus.NOT_ACCEPTABLE, 'The location is not readable')\n }\n if (!(await isPathIsWriteable(rPath))) {\n throw new FileError(HttpStatus.NOT_ACCEPTABLE, 'The location is not writeable')\n }\n}\n"],"names":["checkExternalPath","checkFileName","checksumFile","copyFileContent","copyFiles","countDirEntries","createEmptyFile","dirHasChildren","dirListFileNames","dirName","dirSize","fileName","fileSize","genEtag","getMimeType","getProps","isPathExists","isPathIsDir","isPathIsReadable","isPathIsWriteable","makeDir","moveFiles","removeFiles","sanitizeName","sanitizePath","touchFile","uniqueDatedFilePath","uniqueFilePathFromDir","writeFromStream","writeFromStreamAndChecksum","fPath","path","normalize","replace","regExpPreventPathTraversal","name","fName","isValidFileName","FileError","HttpStatus","BAD_REQUEST","rPath","fse","pathExists","fs","access","constants","R_OK","W_OK","stat","isDirectory","posix","basename","dirname","size","writeFile","recursive","mkdir","isDir","extName","extname","EXTRA_MIMES_TYPE","has","get","m","mime","lookup","file","Error","stats","statSync","mtime","getTime","toString","remove","undefined","id","ino","ctime","birthtime","currentTimeStamp","utimes","srcPath","dstPath","overwrite","preserveTimestamps","atime","copy","move","filePath","alg","hash","crypto","createHash","stream","createReadStream","highWaterMark","DEFAULT_HIGH_WATER_MARK","pipeline","digest","start","dst","createWriteStream","flags","hasRange","src","end","source","chunk","update","srcStream","errors","f","readdir","withFileTypes","isFile","p","join","parentPath","e","message","map","reduce","acc","directories","files","mustContainsDirs","opendir","parentDir","extension","nameWithoutExtension","count","date","formatDateISOString","Date","NOT_FOUND","NOT_ACCEPTABLE"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../backend/src/applications/files/utils/files.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 { HttpStatus } from '@nestjs/common'\nimport { WriteStream } from 'fs'\nimport fse from 'fs-extra'\nimport mime from 'mime-types'\nimport crypto from 'node:crypto'\nimport { createReadStream, createWriteStream, Dirent, statSync } from 'node:fs'\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { Readable } from 'node:stream'\nimport { pipeline } from 'node:stream/promises'\nimport { formatDateISOString } from '../../../common/functions'\nimport { currentTimeStamp, isValidFileName, regExpPreventPathTraversal } from '../../../common/shared'\nimport { DEFAULT_CHECKSUM_ALGORITHM, DEFAULT_HIGH_WATER_MARK, EXTRA_MIMES_TYPE } from '../constants/files'\nimport type { FileDBProps } from '../interfaces/file-db-props.interface'\nimport type { FileProps } from '../interfaces/file-props.interface'\nimport { FileError } from '../models/file-error'\n\nexport function sanitizePath(fPath: string): string {\n return path.normalize(fPath).replace(regExpPreventPathTraversal, '')\n}\n\nexport function sanitizeName(name: string): string {\n return name\n .replace(/^\\s+|[. ]+$/g, '') // trimStart + trimEnd + strip trailing dots\n .replace(/[/\\\\]/g, '') // remove slashes\n .replace(/\\.\\./g, '') // remove '..'\n}\n\nexport function checkFileName(fPath: string): string {\n const fName = fileName(fPath)\n try {\n isValidFileName(fName)\n return fName\n } catch {\n throw new FileError(HttpStatus.BAD_REQUEST, 'Forbidden characters')\n }\n}\n\nexport function isPathExists(rPath: string): Promise<boolean> {\n return fse.pathExists(rPath)\n}\n\nexport async function isPathIsReadable(rPath: string): Promise<boolean> {\n try {\n await fs.access(rPath, fs.constants.R_OK)\n } catch {\n return false\n }\n return true\n}\n\nexport async function isPathIsWriteable(rPath: string): Promise<boolean> {\n try {\n await fs.access(rPath, fs.constants.W_OK)\n } catch {\n return false\n }\n return true\n}\n\nexport async function isPathIsDir(rPath: string): Promise<boolean> {\n return (await fs.stat(rPath)).isDirectory()\n}\n\nexport function fileName(fPath: string): string {\n return path.posix.basename(fPath)\n}\n\nexport function dirName(fPath: string): string {\n return path.dirname(fPath)\n}\n\nexport async function fileSize(rPath: string): Promise<number> {\n return (await fs.stat(rPath)).size\n}\n\nexport function createEmptyFile(rPath: string): Promise<void> {\n return fs.writeFile(rPath, '')\n}\n\nexport function makeDir(rPath: string, recursive?: boolean): Promise<string> {\n return fs.mkdir(rPath, { recursive: recursive })\n}\n\nexport function getMimeType(fPath: string, isDir: boolean): string {\n if (isDir) {\n return 'directory'\n }\n const extName: string = path.extname(fPath)\n if (EXTRA_MIMES_TYPE.has(extName)) {\n return EXTRA_MIMES_TYPE.get(extName)\n }\n const m = mime.lookup(extName)\n if (m) {\n return m.replace('/', '-')\n }\n return 'file'\n}\n\nexport function genEtag(file?: Pick<FileProps, 'size' | 'mtime'>, rPath?: string, weakPrefix = true): string {\n if (!file) {\n if (!rPath) throw new Error('File or path are missing')\n const stats = statSync(rPath)\n file = { size: stats.size, mtime: stats.mtime.getTime() }\n }\n const etag = `${file.size.toString(16)}-${file.mtime.toString(16)}`\n return weakPrefix ? `W/\"${etag}\"` : etag\n}\n\nexport function genUniqHashFromFileDBProps(dbFile: FileDBProps) {\n const dbFileString = `${Object.keys(dbFile)\n .sort()\n .map((k) => `${k}=${String(dbFile[k])}`)\n .join('|')}`\n return crypto.createHash(DEFAULT_CHECKSUM_ALGORITHM).update(dbFileString, 'utf-8').digest('hex')\n}\n\nexport function removeFiles(rPath: string): Promise<void> {\n // if the file does not exist, no error is thrown\n return fse.remove(rPath)\n}\n\nexport async function getProps(rPath: string, fPath?: string, isDir?: boolean): Promise<FileProps> {\n const stats = await fs.stat(rPath)\n const isDirectory = isDir === undefined ? stats.isDirectory() : isDir\n return {\n id: -stats.ino, // use negative number to avoid conflicts with existing database ids\n path: dirName(fPath !== undefined ? fPath : rPath),\n name: fileName(fPath !== undefined ? fPath : rPath),\n isDir: isDirectory,\n size: isDirectory ? 0 : stats.size,\n ctime: stats.birthtime.getTime(),\n mtime: stats.mtime.getTime(),\n mime: getMimeType(rPath, isDirectory)\n }\n}\n\nexport function touchFile(rPath: string, mtime?: number): Promise<void> {\n if (!mtime) mtime = currentTimeStamp()\n return fs.utimes(rPath, mtime, mtime)\n}\n\nexport async function copyFiles(srcPath: string, dstPath: string, overwrite = false, recursive = true, preserveTimestamps = true): Promise<void> {\n /*\n If src is a directory it will copy everything inside of this directory, not the entire directory itself\n If src is a file, dest cannot be a directory\n */\n if (!recursive && (await isPathIsDir(srcPath))) {\n await fs.mkdir(dstPath)\n if (preserveTimestamps) {\n const stat = await fs.stat(srcPath)\n await fs.utimes(dstPath, stat.atime, stat.mtime)\n }\n } else {\n await fse.copy(srcPath, dstPath, { overwrite, preserveTimestamps: preserveTimestamps })\n }\n}\n\nexport function moveFiles(srcPath: string, dstPath: string, overwrite = false): Promise<void> {\n /*\n If src is a file, dest must be a file and when src is a directory, dest must be a directory\n */\n return fse.move(srcPath, dstPath, { overwrite })\n}\n\nexport async function checksumFile(filePath: string, alg: string): Promise<string> {\n const hash = crypto.createHash(alg)\n const stream = createReadStream(filePath, { highWaterMark: DEFAULT_HIGH_WATER_MARK })\n await pipeline(stream, hash)\n return hash.digest('hex')\n}\n\nexport function writeFromStream(rPath: string, stream: Readable, start: number = 0): Promise<void> {\n const dst: WriteStream = createWriteStream(rPath, { flags: start ? 'a' : 'w', start: start, highWaterMark: DEFAULT_HIGH_WATER_MARK })\n return pipeline(stream, dst)\n}\n\nexport async function writeFromStreamAndChecksum(rPath: string, stream: Readable, hasRange: number, alg: string): Promise<string> {\n const hash = crypto.createHash(alg)\n if (hasRange) {\n const src = createReadStream(rPath, { highWaterMark: DEFAULT_HIGH_WATER_MARK })\n await pipeline(src, hash, { end: false })\n }\n const dst = createWriteStream(rPath, { flags: hasRange ? 'a' : 'w', highWaterMark: DEFAULT_HIGH_WATER_MARK })\n await pipeline(\n stream,\n async function* (source) {\n for await (const chunk of source) {\n hash.update(chunk)\n yield chunk\n }\n },\n dst\n )\n hash.end()\n return hash.digest('hex')\n}\n\nexport function copyFileContent(srcPath: string, dstPath: string): Promise<void> {\n const srcStream = createReadStream(srcPath, { highWaterMark: DEFAULT_HIGH_WATER_MARK })\n return writeFromStream(dstPath, srcStream)\n}\n\nexport async function dirSize(rPath: string): Promise<[number, any]> {\n let size = 0\n const errors: Record<string, string> = {}\n for (const f of await fs.readdir(rPath, { withFileTypes: true, recursive: true })) {\n if (f.isFile()) {\n const p = path.join(f.parentPath, f.name)\n try {\n size += (await fs.stat(p)).size\n } catch (e: any) {\n errors[p] = e.message\n }\n }\n }\n return [size, errors]\n}\n\nexport async function dirListFileNames(rPath: string): Promise<string[]> {\n return (await fs.readdir(rPath)).map((path: string) => fileName(path))\n}\n\nexport async function countDirEntries(rPath: string): Promise<{ files: number; directories: number }> {\n return (await fs.readdir(rPath, { withFileTypes: true, recursive: true })).reduce(\n (acc, f: Dirent) => {\n if (f.isDirectory()) {\n acc.directories++\n } else {\n acc.files++\n }\n return acc\n },\n { files: 0, directories: 0 }\n )\n}\n\nexport async function dirHasChildren(rPath: string, mustContainsDirs = true): Promise<boolean> {\n for await (const file of await fs.opendir(rPath)) {\n if (mustContainsDirs) {\n if (file.isDirectory()) return true\n } else {\n return true\n }\n }\n return false\n}\n\nexport async function uniqueFilePathFromDir(rPath: string): Promise<string> {\n if (await isPathExists(rPath)) {\n const parentDir = path.dirname(rPath)\n const extension = path.extname(rPath)\n const nameWithoutExtension = path.basename(rPath, extension)\n let count = 1\n while (await isPathExists(path.join(parentDir, `${nameWithoutExtension} (${count})${extension}`))) {\n count++\n }\n return path.join(parentDir, `${nameWithoutExtension} (${count})${extension}`)\n }\n return rPath\n}\n\nexport async function uniqueDatedFilePath(rPath: string): Promise<{ isDir: boolean; path: string }> {\n const date = formatDateISOString(new Date())\n if (await isPathIsDir(rPath)) {\n return { isDir: true, path: `${rPath}-${date}` }\n } else {\n const extension = path.extname(rPath)\n const nameWithoutExtension = path.basename(rPath, extension)\n return { isDir: false, path: path.join(path.dirname(rPath), `${nameWithoutExtension}-${date}${extension}`) }\n }\n}\n\nexport async function checkExternalPath(rPath: string) {\n if (!(await isPathExists(rPath))) {\n throw new FileError(HttpStatus.NOT_FOUND, 'The location does not exist')\n }\n if (!(await isPathIsReadable(rPath))) {\n throw new FileError(HttpStatus.NOT_ACCEPTABLE, 'The location is not readable')\n }\n if (!(await isPathIsWriteable(rPath))) {\n throw new FileError(HttpStatus.NOT_ACCEPTABLE, 'The location is not writeable')\n }\n}\n"],"names":["checkExternalPath","checkFileName","checksumFile","copyFileContent","copyFiles","countDirEntries","createEmptyFile","dirHasChildren","dirListFileNames","dirName","dirSize","fileName","fileSize","genEtag","genUniqHashFromFileDBProps","getMimeType","getProps","isPathExists","isPathIsDir","isPathIsReadable","isPathIsWriteable","makeDir","moveFiles","removeFiles","sanitizeName","sanitizePath","touchFile","uniqueDatedFilePath","uniqueFilePathFromDir","writeFromStream","writeFromStreamAndChecksum","fPath","path","normalize","replace","regExpPreventPathTraversal","name","fName","isValidFileName","FileError","HttpStatus","BAD_REQUEST","rPath","fse","pathExists","fs","access","constants","R_OK","W_OK","stat","isDirectory","posix","basename","dirname","size","writeFile","recursive","mkdir","isDir","extName","extname","EXTRA_MIMES_TYPE","has","get","m","mime","lookup","file","weakPrefix","Error","stats","statSync","mtime","getTime","etag","toString","dbFile","dbFileString","Object","keys","sort","map","k","String","join","crypto","createHash","DEFAULT_CHECKSUM_ALGORITHM","update","digest","remove","undefined","id","ino","ctime","birthtime","currentTimeStamp","utimes","srcPath","dstPath","overwrite","preserveTimestamps","atime","copy","move","filePath","alg","hash","stream","createReadStream","highWaterMark","DEFAULT_HIGH_WATER_MARK","pipeline","start","dst","createWriteStream","flags","hasRange","src","end","source","chunk","srcStream","errors","f","readdir","withFileTypes","isFile","p","parentPath","e","message","reduce","acc","directories","files","mustContainsDirs","opendir","parentDir","extension","nameWithoutExtension","count","date","formatDateISOString","Date","NOT_FOUND","NOT_ACCEPTABLE"],"mappings":"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"}
@@ -19,6 +19,9 @@ _export(exports, {
19
19
  get API_PUBLIC_LINK_AUTH () {
20
20
  return API_PUBLIC_LINK_AUTH;
21
21
  },
22
+ get API_PUBLIC_LINK_DOWNLOAD () {
23
+ return API_PUBLIC_LINK_DOWNLOAD;
24
+ },
22
25
  get API_PUBLIC_LINK_VALIDATION () {
23
26
  return API_PUBLIC_LINK_VALIDATION;
24
27
  },
@@ -32,10 +35,12 @@ const PUBLIC_LINKS_ROUTE = {
32
35
  LINK: 'link',
33
36
  VALIDATION: 'validation',
34
37
  ACCESS: 'access',
38
+ DOWNLOAD: 'download',
35
39
  AUTH: 'auth'
36
40
  };
37
41
  const API_PUBLIC_LINK_VALIDATION = `${PUBLIC_LINKS_ROUTE.BASE}/${PUBLIC_LINKS_ROUTE.VALIDATION}`;
38
42
  const API_PUBLIC_LINK_ACCESS = `${PUBLIC_LINKS_ROUTE.BASE}/${PUBLIC_LINKS_ROUTE.ACCESS}`;
43
+ const API_PUBLIC_LINK_DOWNLOAD = `${PUBLIC_LINKS_ROUTE.BASE}/${PUBLIC_LINKS_ROUTE.DOWNLOAD}`;
39
44
  const API_PUBLIC_LINK_AUTH = `${PUBLIC_LINKS_ROUTE.BASE}/${PUBLIC_LINKS_ROUTE.AUTH}`;
40
45
 
41
46
  //# sourceMappingURL=routes.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../backend/src/applications/links/constants/routes.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 { APP_BASE_ROUTE } from '../../applications.constants'\n\nexport const PUBLIC_LINKS_ROUTE = {\n BASE: `${APP_BASE_ROUTE}/link`,\n LINK: 'link',\n VALIDATION: 'validation',\n ACCESS: 'access',\n AUTH: 'auth'\n}\n\nexport const API_PUBLIC_LINK_VALIDATION = `${PUBLIC_LINKS_ROUTE.BASE}/${PUBLIC_LINKS_ROUTE.VALIDATION}`\nexport const API_PUBLIC_LINK_ACCESS = `${PUBLIC_LINKS_ROUTE.BASE}/${PUBLIC_LINKS_ROUTE.ACCESS}`\nexport const API_PUBLIC_LINK_AUTH = `${PUBLIC_LINKS_ROUTE.BASE}/${PUBLIC_LINKS_ROUTE.AUTH}`\n"],"names":["API_PUBLIC_LINK_ACCESS","API_PUBLIC_LINK_AUTH","API_PUBLIC_LINK_VALIDATION","PUBLIC_LINKS_ROUTE","BASE","APP_BASE_ROUTE","LINK","VALIDATION","ACCESS","AUTH"],"mappings":"AAAA;;;;CAIC;;;;;;;;;;;QAaYA;eAAAA;;QACAC;eAAAA;;QAFAC;eAAAA;;QARAC;eAAAA;;;uCAFkB;AAExB,MAAMA,qBAAqB;IAChCC,MAAM,GAAGC,qCAAc,CAAC,KAAK,CAAC;IAC9BC,MAAM;IACNC,YAAY;IACZC,QAAQ;IACRC,MAAM;AACR;AAEO,MAAMP,6BAA6B,GAAGC,mBAAmBC,IAAI,CAAC,CAAC,EAAED,mBAAmBI,UAAU,EAAE;AAChG,MAAMP,yBAAyB,GAAGG,mBAAmBC,IAAI,CAAC,CAAC,EAAED,mBAAmBK,MAAM,EAAE;AACxF,MAAMP,uBAAuB,GAAGE,mBAAmBC,IAAI,CAAC,CAAC,EAAED,mBAAmBM,IAAI,EAAE"}
1
+ {"version":3,"sources":["../../../../../backend/src/applications/links/constants/routes.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 { APP_BASE_ROUTE } from '../../applications.constants'\n\nexport const PUBLIC_LINKS_ROUTE = {\n BASE: `${APP_BASE_ROUTE}/link`,\n LINK: 'link',\n VALIDATION: 'validation',\n ACCESS: 'access',\n DOWNLOAD: 'download',\n AUTH: 'auth'\n}\n\nexport const API_PUBLIC_LINK_VALIDATION = `${PUBLIC_LINKS_ROUTE.BASE}/${PUBLIC_LINKS_ROUTE.VALIDATION}`\nexport const API_PUBLIC_LINK_ACCESS = `${PUBLIC_LINKS_ROUTE.BASE}/${PUBLIC_LINKS_ROUTE.ACCESS}`\nexport const API_PUBLIC_LINK_DOWNLOAD = `${PUBLIC_LINKS_ROUTE.BASE}/${PUBLIC_LINKS_ROUTE.DOWNLOAD}`\nexport const API_PUBLIC_LINK_AUTH = `${PUBLIC_LINKS_ROUTE.BASE}/${PUBLIC_LINKS_ROUTE.AUTH}`\n"],"names":["API_PUBLIC_LINK_ACCESS","API_PUBLIC_LINK_AUTH","API_PUBLIC_LINK_DOWNLOAD","API_PUBLIC_LINK_VALIDATION","PUBLIC_LINKS_ROUTE","BASE","APP_BASE_ROUTE","LINK","VALIDATION","ACCESS","DOWNLOAD","AUTH"],"mappings":"AAAA;;;;CAIC;;;;;;;;;;;QAcYA;eAAAA;;QAEAC;eAAAA;;QADAC;eAAAA;;QAFAC;eAAAA;;QATAC;eAAAA;;;uCAFkB;AAExB,MAAMA,qBAAqB;IAChCC,MAAM,GAAGC,qCAAc,CAAC,KAAK,CAAC;IAC9BC,MAAM;IACNC,YAAY;IACZC,QAAQ;IACRC,UAAU;IACVC,MAAM;AACR;AAEO,MAAMR,6BAA6B,GAAGC,mBAAmBC,IAAI,CAAC,CAAC,EAAED,mBAAmBI,UAAU,EAAE;AAChG,MAAMR,yBAAyB,GAAGI,mBAAmBC,IAAI,CAAC,CAAC,EAAED,mBAAmBK,MAAM,EAAE;AACxF,MAAMP,2BAA2B,GAAGE,mBAAmBC,IAAI,CAAC,CAAC,EAAED,mBAAmBM,QAAQ,EAAE;AAC5F,MAAMT,uBAAuB,GAAGG,mBAAmBC,IAAI,CAAC,CAAC,EAAED,mBAAmBO,IAAI,EAAE"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../backend/src/applications/links/interfaces/link-space.interface.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\nexport interface SpaceLink {\n share?: { name: string; alias: string; hasParent: boolean; isDir: boolean; mime: string }\n space?: { name: string; alias: string }\n owner?: { login?: string; fullName: string; avatar?: string }\n}\n"],"names":[],"mappings":"AAAA;;;;CAIC"}
1
+ {"version":3,"sources":["../../../../../backend/src/applications/links/interfaces/link-space.interface.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 type { FileEditorProvider } from '../../../configuration/config.interfaces'\n\nexport interface SpaceLink {\n share?: {\n name: string\n alias: string\n hasParent: boolean\n isDir: boolean\n mtime: number\n mime: string\n size: number\n permissions: string\n } | null\n space?: { name: string; alias: string } | null\n owner?: { login?: string; fullName: string; avatar?: string } | null\n fileEditors?: FileEditorProvider\n}\n"],"names":[],"mappings":"AAAA;;;;CAIC"}
@@ -36,16 +36,19 @@ function _ts_param(paramIndex, decorator) {
36
36
  }
37
37
  let LinksController = class LinksController {
38
38
  linkValidation(user, uuid) {
39
- return this.linksPublicManager.linkValidation(user, uuid);
39
+ return this.linksManager.linkValidation(user, uuid);
40
40
  }
41
41
  linkAccess(user, uuid, req, res) {
42
- return this.linksPublicManager.linkAccess(user, uuid, req, res);
42
+ return this.linksManager.linkAccess(user, uuid, req, res);
43
+ }
44
+ linkDownload(user, uuid, req, res) {
45
+ return this.linksManager.linkDownload(user, uuid, req, res);
43
46
  }
44
47
  linkAuthentication(user, uuid, linkPasswordDto, req, res) {
45
- return this.linksPublicManager.linkAuthentication(user, uuid, linkPasswordDto, req, res);
48
+ return this.linksManager.linkAuthentication(user, uuid, linkPasswordDto, req, res);
46
49
  }
47
- constructor(linksPublicManager){
48
- this.linksPublicManager = linksPublicManager;
50
+ constructor(linksManager){
51
+ this.linksManager = linksManager;
49
52
  }
50
53
  };
51
54
  _ts_decorate([
@@ -76,6 +79,23 @@ _ts_decorate([
76
79
  ]),
77
80
  _ts_metadata("design:returntype", typeof Promise === "undefined" ? Object : Promise)
78
81
  ], LinksController.prototype, "linkAccess", null);
82
+ _ts_decorate([
83
+ (0, _common.Get)(`${_routes.PUBLIC_LINKS_ROUTE.DOWNLOAD}/:uuid`),
84
+ _ts_param(0, (0, _userdecorator.GetUser)()),
85
+ _ts_param(1, (0, _common.Param)('uuid')),
86
+ _ts_param(2, (0, _common.Req)()),
87
+ _ts_param(3, (0, _common.Res)({
88
+ passthrough: true
89
+ })),
90
+ _ts_metadata("design:type", Function),
91
+ _ts_metadata("design:paramtypes", [
92
+ typeof _usermodel.UserModel === "undefined" ? Object : _usermodel.UserModel,
93
+ String,
94
+ typeof _fastify.FastifyRequest === "undefined" ? Object : _fastify.FastifyRequest,
95
+ typeof _fastify.FastifyReply === "undefined" ? Object : _fastify.FastifyReply
96
+ ]),
97
+ _ts_metadata("design:returntype", typeof Promise === "undefined" ? Object : Promise)
98
+ ], LinksController.prototype, "linkDownload", null);
79
99
  _ts_decorate([
80
100
  (0, _common.Post)(`${_routes.PUBLIC_LINKS_ROUTE.AUTH}/:uuid`),
81
101
  _ts_param(0, (0, _userdecorator.GetUser)()),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../backend/src/applications/links/links.controller.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 { Body, Controller, Get, Param, Post, Req, Res, StreamableFile } from '@nestjs/common'\nimport { FastifyReply, FastifyRequest } from 'fastify'\nimport { AuthTokenOptional } from '../../authentication/decorators/auth-token-optional.decorator'\nimport { LoginResponseDto } from '../../authentication/dto/login-response.dto'\nimport { GetUser } from '../users/decorators/user.decorator'\nimport { UserPasswordDto } from '../users/dto/user-properties.dto'\nimport { UserModel } from '../users/models/user.model'\nimport { PUBLIC_LINKS_ROUTE } from './constants/routes'\nimport { SpaceLink } from './interfaces/link-space.interface'\nimport { LinksManager } from './services/links-manager.service'\n\n@Controller(PUBLIC_LINKS_ROUTE.BASE)\n@AuthTokenOptional()\nexport class LinksController {\n constructor(private readonly linksPublicManager: LinksManager) {}\n\n @Get(`${PUBLIC_LINKS_ROUTE.VALIDATION}/:uuid`)\n linkValidation(\n @GetUser() user: UserModel,\n @Param('uuid') uuid: string\n ): Promise<{\n error: string | true\n ok: boolean\n link: SpaceLink\n }> {\n return this.linksPublicManager.linkValidation(user, uuid)\n }\n\n @Get(`${PUBLIC_LINKS_ROUTE.ACCESS}/:uuid`)\n linkAccess(\n @GetUser() user: UserModel,\n @Param('uuid') uuid: string,\n @Req() req: FastifyRequest,\n @Res({ passthrough: true }) res: FastifyReply\n ): Promise<StreamableFile | LoginResponseDto> {\n return this.linksPublicManager.linkAccess(user, uuid, req, res)\n }\n\n @Post(`${PUBLIC_LINKS_ROUTE.AUTH}/:uuid`)\n linkAuthentication(\n @GetUser() user: UserModel,\n @Param('uuid') uuid: string,\n @Body() linkPasswordDto: UserPasswordDto,\n @Req() req: FastifyRequest,\n @Res({ passthrough: true }) res: FastifyReply\n ): Promise<LoginResponseDto> {\n return this.linksPublicManager.linkAuthentication(user, uuid, linkPasswordDto, req, res)\n }\n}\n"],"names":["LinksController","linkValidation","user","uuid","linksPublicManager","linkAccess","req","res","linkAuthentication","linkPasswordDto","PUBLIC_LINKS_ROUTE","VALIDATION","ACCESS","passthrough","AUTH","BASE"],"mappings":"AAAA;;;;CAIC;;;;+BAeYA;;;eAAAA;;;wBAbgE;yBAChC;4CACX;+BAEV;mCACQ;2BACN;wBACS;qCAEN;;;;;;;;;;;;;;;AAItB,IAAA,AAAMA,kBAAN,MAAMA;IAIXC,eACE,AAAWC,IAAe,EAC1B,AAAeC,IAAY,EAK1B;QACD,OAAO,IAAI,CAACC,kBAAkB,CAACH,cAAc,CAACC,MAAMC;IACtD;IAGAE,WACE,AAAWH,IAAe,EAC1B,AAAeC,IAAY,EAC3B,AAAOG,GAAmB,EAC1B,AAA4BC,GAAiB,EACD;QAC5C,OAAO,IAAI,CAACH,kBAAkB,CAACC,UAAU,CAACH,MAAMC,MAAMG,KAAKC;IAC7D;IAGAC,mBACE,AAAWN,IAAe,EAC1B,AAAeC,IAAY,EAC3B,AAAQM,eAAgC,EACxC,AAAOH,GAAmB,EAC1B,AAA4BC,GAAiB,EAClB;QAC3B,OAAO,IAAI,CAACH,kBAAkB,CAACI,kBAAkB,CAACN,MAAMC,MAAMM,iBAAiBH,KAAKC;IACtF;IAjCA,YAAY,AAAiBH,kBAAgC,CAAE;aAAlCA,qBAAAA;IAAmC;AAkClE;;wBAhCUM,0BAAkB,CAACC,UAAU,CAAC,MAAM;;;;;;;;;;;wBAYpCD,0BAAkB,CAACE,MAAM,CAAC,MAAM;;;;;QAK/BC,aAAa;;;;;;;;;;;;yBAKbH,0BAAkB,CAACI,IAAI,CAAC,MAAM;;;;;;QAM9BD,aAAa;;;;;;;;;;;;;uDAjCOE"}
1
+ {"version":3,"sources":["../../../../backend/src/applications/links/links.controller.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 { Body, Controller, Get, Param, Post, Req, Res, StreamableFile } from '@nestjs/common'\nimport { FastifyReply, FastifyRequest } from 'fastify'\nimport { AuthTokenOptional } from '../../authentication/decorators/auth-token-optional.decorator'\nimport { LoginResponseDto } from '../../authentication/dto/login-response.dto'\nimport { GetUser } from '../users/decorators/user.decorator'\nimport { UserPasswordDto } from '../users/dto/user-properties.dto'\nimport { UserModel } from '../users/models/user.model'\nimport { PUBLIC_LINKS_ROUTE } from './constants/routes'\nimport { SpaceLink } from './interfaces/link-space.interface'\nimport { LinksManager } from './services/links-manager.service'\n\n@Controller(PUBLIC_LINKS_ROUTE.BASE)\n@AuthTokenOptional()\nexport class LinksController {\n constructor(private readonly linksManager: LinksManager) {}\n\n @Get(`${PUBLIC_LINKS_ROUTE.VALIDATION}/:uuid`)\n linkValidation(\n @GetUser() user: UserModel,\n @Param('uuid') uuid: string\n ): Promise<{\n error: string | true\n ok: boolean\n link: SpaceLink\n }> {\n return this.linksManager.linkValidation(user, uuid)\n }\n\n @Get(`${PUBLIC_LINKS_ROUTE.ACCESS}/:uuid`)\n linkAccess(\n @GetUser() user: UserModel,\n @Param('uuid') uuid: string,\n @Req() req: FastifyRequest,\n @Res({ passthrough: true }) res: FastifyReply\n ): Promise<LoginResponseDto | Omit<LoginResponseDto, 'token'>> {\n return this.linksManager.linkAccess(user, uuid, req, res)\n }\n\n @Get(`${PUBLIC_LINKS_ROUTE.DOWNLOAD}/:uuid`)\n linkDownload(\n @GetUser() user: UserModel,\n @Param('uuid') uuid: string,\n @Req() req: FastifyRequest,\n @Res({ passthrough: true }) res: FastifyReply\n ): Promise<StreamableFile> {\n return this.linksManager.linkDownload(user, uuid, req, res)\n }\n\n @Post(`${PUBLIC_LINKS_ROUTE.AUTH}/:uuid`)\n linkAuthentication(\n @GetUser() user: UserModel,\n @Param('uuid') uuid: string,\n @Body() linkPasswordDto: UserPasswordDto,\n @Req() req: FastifyRequest,\n @Res({ passthrough: true }) res: FastifyReply\n ): Promise<LoginResponseDto> {\n return this.linksManager.linkAuthentication(user, uuid, linkPasswordDto, req, res)\n }\n}\n"],"names":["LinksController","linkValidation","user","uuid","linksManager","linkAccess","req","res","linkDownload","linkAuthentication","linkPasswordDto","PUBLIC_LINKS_ROUTE","VALIDATION","ACCESS","passthrough","DOWNLOAD","AUTH","BASE"],"mappings":"AAAA;;;;CAIC;;;;+BAeYA;;;eAAAA;;;wBAbgE;yBAChC;4CACX;+BAEV;mCACQ;2BACN;wBACS;qCAEN;;;;;;;;;;;;;;;AAItB,IAAA,AAAMA,kBAAN,MAAMA;IAIXC,eACE,AAAWC,IAAe,EAC1B,AAAeC,IAAY,EAK1B;QACD,OAAO,IAAI,CAACC,YAAY,CAACH,cAAc,CAACC,MAAMC;IAChD;IAGAE,WACE,AAAWH,IAAe,EAC1B,AAAeC,IAAY,EAC3B,AAAOG,GAAmB,EAC1B,AAA4BC,GAAiB,EACgB;QAC7D,OAAO,IAAI,CAACH,YAAY,CAACC,UAAU,CAACH,MAAMC,MAAMG,KAAKC;IACvD;IAGAC,aACE,AAAWN,IAAe,EAC1B,AAAeC,IAAY,EAC3B,AAAOG,GAAmB,EAC1B,AAA4BC,GAAiB,EACpB;QACzB,OAAO,IAAI,CAACH,YAAY,CAACI,YAAY,CAACN,MAAMC,MAAMG,KAAKC;IACzD;IAGAE,mBACE,AAAWP,IAAe,EAC1B,AAAeC,IAAY,EAC3B,AAAQO,eAAgC,EACxC,AAAOJ,GAAmB,EAC1B,AAA4BC,GAAiB,EAClB;QAC3B,OAAO,IAAI,CAACH,YAAY,CAACK,kBAAkB,CAACP,MAAMC,MAAMO,iBAAiBJ,KAAKC;IAChF;IA3CA,YAAY,AAAiBH,YAA0B,CAAE;aAA5BA,eAAAA;IAA6B;AA4C5D;;wBA1CUO,0BAAkB,CAACC,UAAU,CAAC,MAAM;;;;;;;;;;;wBAYpCD,0BAAkB,CAACE,MAAM,CAAC,MAAM;;;;;QAK/BC,aAAa;;;;;;;;;;;;wBAKdH,0BAAkB,CAACI,QAAQ,CAAC,MAAM;;;;;QAKjCD,aAAa;;;;;;;;;;;;yBAKbH,0BAAkB,CAACK,IAAI,CAAC,MAAM;;;;;;QAM9BF,aAAa;;;;;;;;;;;;;uDA3COG"}
@@ -14,6 +14,7 @@ Object.defineProperty(exports, "LinksManager", {
14
14
  });
15
15
  const _common = require("@nestjs/common");
16
16
  const _authmanagerservice = require("../../../authentication/services/auth-manager.service");
17
+ const _configenvironment = require("../../../configuration/config.environment");
17
18
  const _filesmanagerservice = require("../../files/services/files-manager.service");
18
19
  const _spaces = require("../../spaces/constants/spaces");
19
20
  const _spacesmanagerservice = require("../../spaces/services/spaces-manager.service");
@@ -38,10 +39,16 @@ let LinksManager = class LinksManager {
38
39
  this.logger.warn(`${this.linkValidation.name} - ${uuid} : ${check}`);
39
40
  }
40
41
  const spaceLink = ok ? await this.linksQueries.spaceLink(uuid) : null;
41
- if (spaceLink?.owner?.login) {
42
- spaceLink.owner.avatar = await (0, _avatar.getAvatarBase64)(spaceLink.owner.login);
43
- // for security reasons
44
- delete spaceLink.owner.login;
42
+ if (ok) {
43
+ if (spaceLink?.owner?.login) {
44
+ spaceLink.owner.avatar = await (0, _avatar.getAvatarBase64)(spaceLink.owner.login);
45
+ // For security reasons
46
+ delete spaceLink.owner.login;
47
+ }
48
+ if (spaceLink?.share) {
49
+ // Only used when the link is a file or a directory
50
+ spaceLink.fileEditors = _configenvironment.serverConfig.fileEditors;
51
+ }
45
52
  }
46
53
  return {
47
54
  ok: ok,
@@ -56,28 +63,43 @@ let LinksManager = class LinksManager {
56
63
  throw new _common.HttpException(check, _common.HttpStatus.BAD_REQUEST);
57
64
  }
58
65
  const user = new _usermodel.UserModel(link.user);
59
- const spaceLink = await this.linksQueries.spaceLink(uuid);
60
- if (!spaceLink.space && !spaceLink.share.isDir) {
61
- // download the file (authentication has been verified before)
62
- this.logger.log(`${this.linkAccess.name} - *${user.login}* (${user.id}) downloading ${spaceLink.share.name}`);
63
- this.incrementLinkNbAccess(link);
64
- const spaceEnv = await this.spaceEnvFromLink(user, spaceLink);
65
- const sendFile = this.filesManager.sendFileFromSpace(spaceEnv, spaceLink.share.name);
66
- try {
67
- await sendFile.checks();
68
- return await sendFile.stream(req, res);
69
- } catch (e) {
70
- this.logger.error(`${this.linkAccess.name} - unable to send file : ${e}`);
71
- throw new _common.HttpException('Unable to download file', _common.HttpStatus.INTERNAL_SERVER_ERROR);
72
- }
73
- } else if (link.user.id !== identity.id) {
74
- // authenticate user to allow access to the directory
66
+ if (link.user.id !== identity.id) {
67
+ // Authenticate user to allow access to the directory
75
68
  this.logger.log(`${this.linkAccess.name} - *${user.login}* (${user.id}) is logged`);
76
69
  this.incrementLinkNbAccess(link);
77
70
  this.usersManager.updateAccesses(user, req.ip, true).catch((e)=>this.logger.error(`${this.linkAccess.name} - ${e}`));
78
71
  return this.authManager.setCookies(user, res);
79
72
  }
80
- // already authenticated
73
+ // Already authenticated
74
+ return {
75
+ user: user,
76
+ server: _configenvironment.serverConfig
77
+ };
78
+ }
79
+ async linkDownload(identity, uuid, req, res) {
80
+ const [link, check, ok] = await this.linkEnv(identity, uuid);
81
+ if (!ok) {
82
+ this.logger.warn(`${this.linkDownload.name} - *${link.user.login}* (${link.user.id}) : ${check}`);
83
+ throw new _common.HttpException(check, _common.HttpStatus.BAD_REQUEST);
84
+ }
85
+ const spaceLink = await this.linksQueries.spaceLink(uuid);
86
+ if (spaceLink.space || spaceLink.share?.isDir) {
87
+ this.logger.error(`${this.linkDownload.name} - the provided link does not reference a downloadable file`);
88
+ throw new _common.HttpException('This link does not allow file download', _common.HttpStatus.BAD_REQUEST);
89
+ }
90
+ const user = new _usermodel.UserModel(link.user);
91
+ // Download the file (authentication has been verified before)
92
+ this.logger.log(`${this.linkDownload.name} - *${user.login}* (${user.id}) downloading ${spaceLink.share.name}`);
93
+ this.incrementLinkNbAccess(link);
94
+ const spaceEnv = await this.spaceEnvFromLink(user, spaceLink);
95
+ const sendFile = this.filesManager.sendFileFromSpace(spaceEnv, spaceLink.share.name);
96
+ try {
97
+ await sendFile.checks();
98
+ return await sendFile.stream(req, res);
99
+ } catch (e) {
100
+ this.logger.error(`${this.linkDownload.name} - unable to send file : ${e}`);
101
+ throw new _common.HttpException('Unable to download file', _common.HttpStatus.INTERNAL_SERVER_ERROR);
102
+ }
81
103
  }
82
104
  async linkAuthentication(identity, uuid, linkPasswordDto, req, res) {
83
105
  const [link, check, ok] = await this.linkEnv(identity, uuid, true);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../backend/src/applications/links/services/links-manager.service.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { HttpException, HttpStatus, Injectable, Logger, StreamableFile } from '@nestjs/common'\nimport { FastifyReply, FastifyRequest } from 'fastify'\nimport { LoginResponseDto } from '../../../authentication/dto/login-response.dto'\nimport { JwtIdentityPayload } from '../../../authentication/interfaces/jwt-payload.interface'\nimport { AuthManager } from '../../../authentication/services/auth-manager.service'\nimport { FilesManager } from '../../files/services/files-manager.service'\nimport { SendFile } from '../../files/utils/send-file'\nimport { SPACE_REPOSITORY } from '../../spaces/constants/spaces'\nimport { SpaceEnv } from '../../spaces/models/space-env.model'\nimport { SpacesManager } from '../../spaces/services/spaces-manager.service'\nimport { UserPasswordDto } from '../../users/dto/user-properties.dto'\nimport { UserModel } from '../../users/models/user.model'\nimport { UsersManager } from '../../users/services/users-manager.service'\nimport { getAvatarBase64 } from '../../users/utils/avatar'\nimport { LINK_ERROR } from '../constants/links'\nimport { LinkAsUser } from '../interfaces/link-guest.interface'\nimport { SpaceLink } from '../interfaces/link-space.interface'\nimport { LinksQueries } from './links-queries.service'\n\n@Injectable()\nexport class LinksManager {\n private logger = new Logger(LinksManager.name)\n\n constructor(\n private readonly authManager: AuthManager,\n private readonly usersManager: UsersManager,\n private readonly filesManager: FilesManager,\n private readonly spacesManager: SpacesManager,\n private readonly linksQueries: LinksQueries\n ) {}\n\n async linkValidation(identity: JwtIdentityPayload, uuid: string): Promise<{ ok: boolean; error: string | true; link: SpaceLink }> {\n const [_link, check, ok] = await this.linkEnv(identity, uuid)\n if (!ok) {\n this.logger.warn(`${this.linkValidation.name} - ${uuid} : ${check}`)\n }\n const spaceLink: SpaceLink = ok ? await this.linksQueries.spaceLink(uuid) : null\n if (spaceLink?.owner?.login) {\n spaceLink.owner.avatar = await getAvatarBase64(spaceLink.owner.login)\n // for security reasons\n delete spaceLink.owner.login\n }\n return { ok: ok, error: ok ? null : check, link: spaceLink }\n }\n\n async linkAccess(identity: JwtIdentityPayload, uuid: string, req: FastifyRequest, res: FastifyReply): Promise<StreamableFile | LoginResponseDto> {\n const [link, check, ok] = await this.linkEnv(identity, uuid)\n if (!ok) {\n this.logger.warn(`${this.linkAccess.name} - *${link.user.login}* (${link.user.id}) : ${check}`)\n throw new HttpException(check as string, HttpStatus.BAD_REQUEST)\n }\n const user = new UserModel(link.user)\n const spaceLink: SpaceLink = await this.linksQueries.spaceLink(uuid)\n if (!spaceLink.space && !spaceLink.share.isDir) {\n // download the file (authentication has been verified before)\n this.logger.log(`${this.linkAccess.name} - *${user.login}* (${user.id}) downloading ${spaceLink.share.name}`)\n this.incrementLinkNbAccess(link)\n const spaceEnv: SpaceEnv = await this.spaceEnvFromLink(user, spaceLink)\n const sendFile: SendFile = this.filesManager.sendFileFromSpace(spaceEnv, spaceLink.share.name)\n try {\n await sendFile.checks()\n return await sendFile.stream(req, res)\n } catch (e) {\n this.logger.error(`${this.linkAccess.name} - unable to send file : ${e}`)\n throw new HttpException('Unable to download file', HttpStatus.INTERNAL_SERVER_ERROR)\n }\n } else if (link.user.id !== identity.id) {\n // authenticate user to allow access to the directory\n this.logger.log(`${this.linkAccess.name} - *${user.login}* (${user.id}) is logged`)\n this.incrementLinkNbAccess(link)\n this.usersManager.updateAccesses(user, req.ip, true).catch((e: Error) => this.logger.error(`${this.linkAccess.name} - ${e}`))\n return this.authManager.setCookies(user, res)\n }\n // already authenticated\n }\n\n async linkAuthentication(identity: JwtIdentityPayload, uuid: string, linkPasswordDto: UserPasswordDto, req: FastifyRequest, res: FastifyReply) {\n const [link, check, ok] = await this.linkEnv(identity, uuid, true)\n if (!ok) {\n this.logger.warn(`${this.linkAuthentication.name} - *${link.user.login}* (${link.user.id}) : ${check}`)\n throw new HttpException(check as string, HttpStatus.BAD_REQUEST)\n }\n const authSuccess: boolean = await this.usersManager.compareUserPassword(link.user.id, linkPasswordDto.password)\n const user = new UserModel(link.user)\n this.usersManager.updateAccesses(user, req.ip, authSuccess).catch((e: Error) => this.logger.error(`${this.linkAuthentication.name} - ${e}`))\n if (!authSuccess) {\n this.logger.warn(`${this.linkAuthentication.name} - *${user.login}* (${user.id}) : auth failed`)\n throw new HttpException(LINK_ERROR.UNAUTHORIZED, HttpStatus.FORBIDDEN)\n }\n // authenticate user to allow access\n this.logger.log(`${this.linkAuthentication.name} - *${user.login}* (${user.id}) is logged`)\n return this.authManager.setCookies(user, res)\n }\n\n private spaceEnvFromLink(user: UserModel, link: SpaceLink): Promise<SpaceEnv> {\n return this.spacesManager.spaceEnv(user, [\n link.space ? SPACE_REPOSITORY.FILES : SPACE_REPOSITORY.SHARES,\n link.space ? link.space.alias : link.share.alias\n ])\n }\n\n private async linkEnv(identity: JwtIdentityPayload, uuid: string, ignoreAuth: boolean = false): Promise<[LinkAsUser, string | true, boolean]> {\n const link: LinkAsUser = await this.linksQueries.linkFromUUID(uuid)\n const check: string | true = this.checkLink(identity, link, ignoreAuth)\n const ok: boolean = check === true\n return [link, check, ok]\n }\n\n private checkLink(identity: JwtIdentityPayload, link: LinkAsUser, ignoreAuth: boolean = false): string | true {\n if (!link) {\n return LINK_ERROR.NOT_FOUND\n }\n if (!link.user.isActive) {\n return LINK_ERROR.DISABLED\n }\n if (link.limitAccess !== 0 && link.nbAccess >= link.limitAccess) {\n return LINK_ERROR.EXCEEDED\n }\n if (link.expiresAt && new Date() >= link.expiresAt) {\n return LINK_ERROR.EXPIRED\n }\n if (!ignoreAuth && link.requireAuth && link.user.id !== identity.id) {\n return LINK_ERROR.UNAUTHORIZED\n }\n return true\n }\n\n private incrementLinkNbAccess(link: LinkAsUser) {\n this.linksQueries.incrementLinkNbAccess(link.uuid).catch((e: Error) => this.logger.error(`${this.incrementLinkNbAccess.name} - ${e}`))\n }\n}\n"],"names":["LinksManager","linkValidation","identity","uuid","_link","check","ok","linkEnv","logger","warn","name","spaceLink","linksQueries","owner","login","avatar","getAvatarBase64","error","link","linkAccess","req","res","user","id","HttpException","HttpStatus","BAD_REQUEST","UserModel","space","share","isDir","log","incrementLinkNbAccess","spaceEnv","spaceEnvFromLink","sendFile","filesManager","sendFileFromSpace","checks","stream","e","INTERNAL_SERVER_ERROR","usersManager","updateAccesses","ip","catch","authManager","setCookies","linkAuthentication","linkPasswordDto","authSuccess","compareUserPassword","password","LINK_ERROR","UNAUTHORIZED","FORBIDDEN","spacesManager","SPACE_REPOSITORY","FILES","SHARES","alias","ignoreAuth","linkFromUUID","checkLink","NOT_FOUND","isActive","DISABLED","limitAccess","nbAccess","EXCEEDED","expiresAt","Date","EXPIRED","requireAuth","Logger"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../backend/src/applications/links/services/links-manager.service.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { HttpException, HttpStatus, Injectable, Logger, StreamableFile } from '@nestjs/common'\nimport { FastifyReply, FastifyRequest } from 'fastify'\nimport { LoginResponseDto } from '../../../authentication/dto/login-response.dto'\nimport { JwtIdentityPayload } from '../../../authentication/interfaces/jwt-payload.interface'\nimport { AuthManager } from '../../../authentication/services/auth-manager.service'\nimport { serverConfig } from '../../../configuration/config.environment'\nimport { FilesManager } from '../../files/services/files-manager.service'\nimport { SendFile } from '../../files/utils/send-file'\nimport { SPACE_REPOSITORY } from '../../spaces/constants/spaces'\nimport { SpaceEnv } from '../../spaces/models/space-env.model'\nimport { SpacesManager } from '../../spaces/services/spaces-manager.service'\nimport { UserPasswordDto } from '../../users/dto/user-properties.dto'\nimport { UserModel } from '../../users/models/user.model'\nimport { UsersManager } from '../../users/services/users-manager.service'\nimport { getAvatarBase64 } from '../../users/utils/avatar'\nimport { LINK_ERROR } from '../constants/links'\nimport { LinkAsUser } from '../interfaces/link-guest.interface'\nimport { SpaceLink } from '../interfaces/link-space.interface'\nimport { LinksQueries } from './links-queries.service'\n\n@Injectable()\nexport class LinksManager {\n private logger = new Logger(LinksManager.name)\n\n constructor(\n private readonly authManager: AuthManager,\n private readonly usersManager: UsersManager,\n private readonly filesManager: FilesManager,\n private readonly spacesManager: SpacesManager,\n private readonly linksQueries: LinksQueries\n ) {}\n\n async linkValidation(identity: JwtIdentityPayload, uuid: string): Promise<{ ok: boolean; error: string | true; link: SpaceLink }> {\n const [_link, check, ok] = await this.linkEnv(identity, uuid)\n if (!ok) {\n this.logger.warn(`${this.linkValidation.name} - ${uuid} : ${check}`)\n }\n const spaceLink: SpaceLink = ok ? await this.linksQueries.spaceLink(uuid) : null\n if (ok) {\n if (spaceLink?.owner?.login) {\n spaceLink.owner.avatar = await getAvatarBase64(spaceLink.owner.login)\n // For security reasons\n delete spaceLink.owner.login\n }\n if (spaceLink?.share) {\n // Only used when the link is a file or a directory\n spaceLink.fileEditors = serverConfig.fileEditors\n }\n }\n return { ok: ok, error: ok ? null : check, link: spaceLink }\n }\n\n async linkAccess(\n identity: JwtIdentityPayload,\n uuid: string,\n req: FastifyRequest,\n res: FastifyReply\n ): Promise<LoginResponseDto | Omit<LoginResponseDto, 'token'>> {\n const [link, check, ok] = await this.linkEnv(identity, uuid)\n if (!ok) {\n this.logger.warn(`${this.linkAccess.name} - *${link.user.login}* (${link.user.id}) : ${check}`)\n throw new HttpException(check as string, HttpStatus.BAD_REQUEST)\n }\n const user = new UserModel(link.user)\n if (link.user.id !== identity.id) {\n // Authenticate user to allow access to the directory\n this.logger.log(`${this.linkAccess.name} - *${user.login}* (${user.id}) is logged`)\n this.incrementLinkNbAccess(link)\n this.usersManager.updateAccesses(user, req.ip, true).catch((e: Error) => this.logger.error(`${this.linkAccess.name} - ${e}`))\n return this.authManager.setCookies(user, res)\n }\n // Already authenticated\n return { user: user, server: serverConfig }\n }\n\n async linkDownload(identity: JwtIdentityPayload, uuid: string, req: FastifyRequest, res: FastifyReply): Promise<StreamableFile> {\n const [link, check, ok] = await this.linkEnv(identity, uuid)\n if (!ok) {\n this.logger.warn(`${this.linkDownload.name} - *${link.user.login}* (${link.user.id}) : ${check}`)\n throw new HttpException(check as string, HttpStatus.BAD_REQUEST)\n }\n const spaceLink: SpaceLink = await this.linksQueries.spaceLink(uuid)\n if (spaceLink.space || spaceLink.share?.isDir) {\n this.logger.error(`${this.linkDownload.name} - the provided link does not reference a downloadable file`)\n throw new HttpException('This link does not allow file download', HttpStatus.BAD_REQUEST)\n }\n const user = new UserModel(link.user)\n // Download the file (authentication has been verified before)\n this.logger.log(`${this.linkDownload.name} - *${user.login}* (${user.id}) downloading ${spaceLink.share.name}`)\n this.incrementLinkNbAccess(link)\n const spaceEnv: SpaceEnv = await this.spaceEnvFromLink(user, spaceLink)\n const sendFile: SendFile = this.filesManager.sendFileFromSpace(spaceEnv, spaceLink.share.name)\n try {\n await sendFile.checks()\n return await sendFile.stream(req, res)\n } catch (e) {\n this.logger.error(`${this.linkDownload.name} - unable to send file : ${e}`)\n throw new HttpException('Unable to download file', HttpStatus.INTERNAL_SERVER_ERROR)\n }\n }\n\n async linkAuthentication(identity: JwtIdentityPayload, uuid: string, linkPasswordDto: UserPasswordDto, req: FastifyRequest, res: FastifyReply) {\n const [link, check, ok] = await this.linkEnv(identity, uuid, true)\n if (!ok) {\n this.logger.warn(`${this.linkAuthentication.name} - *${link.user.login}* (${link.user.id}) : ${check}`)\n throw new HttpException(check as string, HttpStatus.BAD_REQUEST)\n }\n const authSuccess: boolean = await this.usersManager.compareUserPassword(link.user.id, linkPasswordDto.password)\n const user = new UserModel(link.user)\n this.usersManager.updateAccesses(user, req.ip, authSuccess).catch((e: Error) => this.logger.error(`${this.linkAuthentication.name} - ${e}`))\n if (!authSuccess) {\n this.logger.warn(`${this.linkAuthentication.name} - *${user.login}* (${user.id}) : auth failed`)\n throw new HttpException(LINK_ERROR.UNAUTHORIZED, HttpStatus.FORBIDDEN)\n }\n // authenticate user to allow access\n this.logger.log(`${this.linkAuthentication.name} - *${user.login}* (${user.id}) is logged`)\n return this.authManager.setCookies(user, res)\n }\n\n private spaceEnvFromLink(user: UserModel, link: SpaceLink): Promise<SpaceEnv> {\n return this.spacesManager.spaceEnv(user, [\n link.space ? SPACE_REPOSITORY.FILES : SPACE_REPOSITORY.SHARES,\n link.space ? link.space.alias : link.share.alias\n ])\n }\n\n private async linkEnv(identity: JwtIdentityPayload, uuid: string, ignoreAuth: boolean = false): Promise<[LinkAsUser, string | true, boolean]> {\n const link: LinkAsUser = await this.linksQueries.linkFromUUID(uuid)\n const check: string | true = this.checkLink(identity, link, ignoreAuth)\n const ok: boolean = check === true\n return [link, check, ok]\n }\n\n private checkLink(identity: JwtIdentityPayload, link: LinkAsUser, ignoreAuth: boolean = false): string | true {\n if (!link) {\n return LINK_ERROR.NOT_FOUND\n }\n if (!link.user.isActive) {\n return LINK_ERROR.DISABLED\n }\n if (link.limitAccess !== 0 && link.nbAccess >= link.limitAccess) {\n return LINK_ERROR.EXCEEDED\n }\n if (link.expiresAt && new Date() >= link.expiresAt) {\n return LINK_ERROR.EXPIRED\n }\n if (!ignoreAuth && link.requireAuth && link.user.id !== identity.id) {\n return LINK_ERROR.UNAUTHORIZED\n }\n return true\n }\n\n private incrementLinkNbAccess(link: LinkAsUser) {\n this.linksQueries.incrementLinkNbAccess(link.uuid).catch((e: Error) => this.logger.error(`${this.incrementLinkNbAccess.name} - ${e}`))\n }\n}\n"],"names":["LinksManager","linkValidation","identity","uuid","_link","check","ok","linkEnv","logger","warn","name","spaceLink","linksQueries","owner","login","avatar","getAvatarBase64","share","fileEditors","serverConfig","error","link","linkAccess","req","res","user","id","HttpException","HttpStatus","BAD_REQUEST","UserModel","log","incrementLinkNbAccess","usersManager","updateAccesses","ip","catch","e","authManager","setCookies","server","linkDownload","space","isDir","spaceEnv","spaceEnvFromLink","sendFile","filesManager","sendFileFromSpace","checks","stream","INTERNAL_SERVER_ERROR","linkAuthentication","linkPasswordDto","authSuccess","compareUserPassword","password","LINK_ERROR","UNAUTHORIZED","FORBIDDEN","spacesManager","SPACE_REPOSITORY","FILES","SHARES","alias","ignoreAuth","linkFromUUID","checkLink","NOT_FOUND","isActive","DISABLED","limitAccess","nbAccess","EXCEEDED","expiresAt","Date","EXPIRED","requireAuth","Logger"],"mappings":"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"}
@@ -265,7 +265,7 @@ describe(_linksmanagerservice.LinksManager.name, ()=>{
265
265
  // cover: incrementLinkNbAccess.catch(...) should log an error when the query rejects
266
266
  const logErrorSpy = jest.spyOn(service.logger, 'error').mockImplementation(()=>undefined);
267
267
  linksQueriesMock.incrementLinkNbAccess.mockRejectedValueOnce(new Error('increment boom'));
268
- const result = await service.linkAccess(identity, link.uuid, req, res);
268
+ const result = await service.linkDownload(identity, link.uuid, req, res);
269
269
  expect(result).toBe(streamable);
270
270
  expect(filesManagerMock.sendFileFromSpace).toHaveBeenCalled();
271
271
  expect(linksQueriesMock.incrementLinkNbAccess).toHaveBeenCalledWith(link.uuid);
@@ -359,7 +359,7 @@ describe(_linksmanagerservice.LinksManager.name, ()=>{
359
359
  };
360
360
  linksQueriesMock.spaceLink.mockResolvedValueOnce(spaceLink);
361
361
  const result = await service.linkAccess(sameUserIdentity, link.uuid, req, res);
362
- expect(result).toBeUndefined();
362
+ expect(result.user.id).toEqual(baseLink.user.id);
363
363
  expect(authManagerMock.setCookies).not.toHaveBeenCalled();
364
364
  expect(linksQueriesMock.incrementLinkNbAccess).not.toHaveBeenCalled();
365
365
  });
@@ -377,13 +377,14 @@ describe(_linksmanagerservice.LinksManager.name, ()=>{
377
377
  alias: 'share'
378
378
  }
379
379
  };
380
+ linksQueriesMock.spaceLink.mockReset();
380
381
  linksQueriesMock.spaceLink.mockResolvedValueOnce(spaceLink);
381
382
  spacesManagerMock.spaceEnv.mockResolvedValueOnce({});
382
383
  filesManagerMock.sendFileFromSpace.mockReturnValueOnce({
383
384
  checks: jest.fn().mockRejectedValueOnce(new Error('disk error')),
384
385
  stream: jest.fn()
385
386
  });
386
- await expect(service.linkAccess(identity, link.uuid, req, res)).rejects.toMatchObject({
387
+ await expect(service.linkDownload(identity, link.uuid, req, res)).rejects.toMatchObject({
387
388
  status: 500
388
389
  });
389
390
  expect(linksQueriesMock.incrementLinkNbAccess).toHaveBeenCalledWith(link.uuid);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../backend/src/applications/links/services/links-manager.service.spec.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { HttpService } from '@nestjs/axios'\nimport { ConfigService } from '@nestjs/config'\nimport { JwtService } from '@nestjs/jwt'\nimport { Test, TestingModule } from '@nestjs/testing'\nimport { AuthManager } from '../../../authentication/services/auth-manager.service'\nimport { Cache } from '../../../infrastructure/cache/services/cache.service'\nimport { ContextManager } from '../../../infrastructure/context/services/context-manager.service'\nimport { DB_TOKEN_PROVIDER } from '../../../infrastructure/database/constants'\nimport { FilesLockManager } from '../../files/services/files-lock-manager.service'\nimport { FilesManager } from '../../files/services/files-manager.service'\nimport { FilesQueries } from '../../files/services/files-queries.service'\nimport { NotificationsManager } from '../../notifications/services/notifications-manager.service'\nimport { SharesManager } from '../../shares/services/shares-manager.service'\nimport { SharesQueries } from '../../shares/services/shares-queries.service'\nimport { SpacesManager } from '../../spaces/services/spaces-manager.service'\nimport { SpacesQueries } from '../../spaces/services/spaces-queries.service'\nimport { UserModel } from '../../users/models/user.model'\nimport { AdminUsersManager } from '../../users/services/admin-users-manager.service'\nimport { AdminUsersQueries } from '../../users/services/admin-users-queries.service'\nimport { UsersManager } from '../../users/services/users-manager.service'\nimport { UsersQueries } from '../../users/services/users-queries.service'\nimport { getAvatarBase64 } from '../../users/utils/avatar'\nimport { LinksManager } from './links-manager.service'\nimport { LinksQueries } from './links-queries.service'\n\njest.mock('../../users/utils/avatar', () => ({\n getAvatarBase64: jest.fn()\n}))\n\ndescribe(LinksManager.name, () => {\n let service: LinksManager\n let linksQueriesMock: jest.Mocked<LinksQueries>\n let usersManagerMock: jest.Mocked<UsersManager>\n let filesManagerMock: jest.Mocked<FilesManager>\n let spacesManagerMock: jest.Mocked<SpacesManager>\n let authManagerMock: jest.Mocked<AuthManager>\n\n const identity: any = { id: 42, login: 'visitor' }\n const baseLink: any = {\n uuid: 'uuid-123',\n user: { id: 7, login: 'john', isActive: true },\n requireAuth: false,\n limitAccess: 0,\n nbAccess: 0,\n expiresAt: null\n }\n\n beforeAll(async () => {\n linksQueriesMock = {\n linkFromUUID: jest.fn(),\n spaceLink: jest.fn(),\n incrementLinkNbAccess: jest.fn().mockResolvedValue(undefined)\n } as any\n\n usersManagerMock = {\n compareUserPassword: jest.fn(),\n updateAccesses: jest.fn()\n } as any\n\n filesManagerMock = {\n sendFileFromSpace: jest.fn()\n } as any\n\n spacesManagerMock = {\n spaceEnv: jest.fn()\n } as any\n\n authManagerMock = {\n setCookies: jest.fn()\n } as any\n\n const module: TestingModule = await Test.createTestingModule({\n providers: [\n {\n provide: DB_TOKEN_PROVIDER,\n useValue: {}\n },\n {\n provide: Cache,\n useValue: {}\n },\n { provide: ContextManager, useValue: {} },\n {\n provide: NotificationsManager,\n useValue: {}\n },\n { provide: HttpService, useValue: {} },\n { provide: FilesLockManager, useValue: {} },\n { provide: ConfigService, useValue: {} },\n { provide: JwtService, useValue: {} },\n { provide: AuthManager, useValue: authManagerMock },\n { provide: UsersManager, useValue: usersManagerMock },\n { provide: UsersQueries, useValue: {} },\n { provide: AdminUsersManager, useValue: {} },\n { provide: AdminUsersQueries, useValue: {} },\n { provide: FilesQueries, useValue: {} },\n { provide: FilesManager, useValue: filesManagerMock },\n { provide: SpacesQueries, useValue: {} },\n { provide: SpacesManager, useValue: spacesManagerMock },\n { provide: SharesQueries, useValue: {} },\n { provide: SharesManager, useValue: {} },\n { provide: LinksQueries, useValue: linksQueriesMock },\n LinksManager\n ]\n }).compile()\n\n module.useLogger(['fatal'])\n service = module.get<LinksManager>(LinksManager)\n })\n\n beforeEach(() => {\n jest.clearAllMocks()\n })\n\n it('should be defined', () => {\n expect(service).toBeDefined()\n })\n\n describe('linkValidation', () => {\n it('returns ok with sanitized owner and avatar when link is valid', async () => {\n const link = { ...baseLink }\n linksQueriesMock.linkFromUUID.mockResolvedValueOnce(link)\n const spaceLink = {\n owner: { login: 'jane' },\n share: { isDir: true, alias: 's-alias', name: 'Docs' },\n space: null\n } as any\n linksQueriesMock.spaceLink.mockResolvedValueOnce(spaceLink)\n ;(getAvatarBase64 as jest.Mock).mockResolvedValueOnce('base64-xxx')\n\n const res = await service.linkValidation(identity, link.uuid)\n\n expect(res.ok).toBe(true)\n expect(res.error).toBeNull()\n expect(res.link).toBe(spaceLink)\n expect((spaceLink.owner as any).login).toBeUndefined()\n expect(linksQueriesMock.spaceLink).toHaveBeenCalledWith(link.uuid)\n expect(getAvatarBase64).toHaveBeenCalledWith('jane')\n\n // extra coverage: directly assert private checkLink with default ignoreAuth=false\n const directCheck = (service as any).checkLink(identity, link)\n expect(directCheck).toBe(true)\n })\n\n it('returns error and null link when uuid is not found', async () => {\n linksQueriesMock.linkFromUUID.mockResolvedValueOnce(null)\n\n const res = await service.linkValidation(identity, 'missing-uuid')\n\n expect(res.ok).toBe(false)\n expect(res.error).toBeDefined()\n expect(res.link).toBeNull()\n })\n\n it('returns unauthorized when auth is required and identity differs', async () => {\n const link = { ...baseLink, requireAuth: true }\n linksQueriesMock.linkFromUUID.mockResolvedValueOnce(link)\n\n const res = await service.linkValidation(identity, link.uuid)\n\n expect(res.ok).toBe(false)\n expect(String(res.error).toLowerCase()).toContain('unauthorized')\n expect(res.link).toBeNull()\n\n // extra coverage: directly assert private checkLink with default ignoreAuth=false\n const directCheck = (service as any).checkLink(identity, link)\n expect(String(directCheck).toLowerCase()).toContain('unauthorized')\n })\n\n it('returns exceeded when nbAccess >= limitAccess', async () => {\n const link = { ...baseLink, limitAccess: 5, nbAccess: 5 }\n linksQueriesMock.linkFromUUID.mockResolvedValueOnce(link)\n\n const res = await service.linkValidation(identity, link.uuid)\n\n expect(res.ok).toBe(false)\n expect(String(res.error).toLowerCase()).toContain('exceeded')\n })\n })\n\n describe('linkAccess', () => {\n const req: any = { ip: '127.0.0.1' }\n const res: any = {}\n\n it('streams a file when link targets a single file', async () => {\n const link = { ...baseLink, limitAccess: 10 }\n linksQueriesMock.linkFromUUID.mockResolvedValueOnce(link)\n const spaceLink = {\n space: null,\n share: { isDir: false, name: 'file.txt', alias: 'share-alias' }\n } as any\n linksQueriesMock.spaceLink.mockResolvedValueOnce(spaceLink)\n\n spacesManagerMock.spaceEnv.mockResolvedValueOnce({} as any)\n const streamable: any = { some: 'stream' }\n filesManagerMock.sendFileFromSpace.mockReturnValueOnce({\n checks: jest.fn().mockResolvedValueOnce(undefined),\n stream: jest.fn().mockResolvedValueOnce(streamable)\n } as any)\n\n // cover: incrementLinkNbAccess.catch(...) should log an error when the query rejects\n const logErrorSpy = jest.spyOn((service as any).logger, 'error').mockImplementation(() => undefined as any)\n linksQueriesMock.incrementLinkNbAccess.mockRejectedValueOnce(new Error('increment boom'))\n\n const result = await service.linkAccess(identity, link.uuid, req, res)\n\n expect(result).toBe(streamable)\n expect(filesManagerMock.sendFileFromSpace).toHaveBeenCalled()\n expect(linksQueriesMock.incrementLinkNbAccess).toHaveBeenCalledWith(link.uuid)\n // Assert repository selection for a share file (space falsy)\n // should call spacesManager.spaceEnv with SHARES and share alias when link.space is falsy\n expect(spacesManagerMock.spaceEnv).toHaveBeenCalledWith(expect.anything(), ['shares', 'share-alias'])\n // should log error when increment fails\n expect(logErrorSpy).toHaveBeenCalled()\n })\n\n it('authenticates and returns cookies when link targets a directory and user is different', async () => {\n const link = { ...baseLink, requireAuth: false, user: { id: 7, login: 'john', isActive: true } }\n linksQueriesMock.linkFromUUID.mockResolvedValueOnce(link)\n const spaceLink = {\n space: { alias: 'files', name: 'My Space' },\n share: { isDir: true, name: 'ignored', alias: 'ignored' }\n } as any\n linksQueriesMock.spaceLink.mockResolvedValueOnce(spaceLink)\n\n const loginDto: any = { token: 'jwt' }\n authManagerMock.setCookies.mockResolvedValueOnce(loginDto)\n\n // cover: usersManager.updateAccesses.catch(...) should log an error when updateAccesses rejects\n const logErrorSpy = jest.spyOn((service as any).logger, 'error').mockImplementation(() => undefined as any)\n usersManagerMock.updateAccesses.mockRejectedValueOnce(new Error('updateAccesses boom'))\n\n const result = await service.linkAccess(identity, link.uuid, req, res)\n\n expect(result).toBe(loginDto)\n expect(usersManagerMock.updateAccesses).toHaveBeenCalledWith(expect.anything(), req.ip, true)\n expect(authManagerMock.setCookies).toHaveBeenCalled()\n // additionally cover the \"space truthy\" branch by calling the private helper directly\n // should call spacesManager.spaceEnv with FILES and space alias when link.space is truthy\n await (service as any).spaceEnvFromLink(new UserModel(link.user as any), spaceLink as any)\n expect(spacesManagerMock.spaceEnv).toHaveBeenCalledWith(expect.anything(), ['files', 'files'])\n // should log error when updateAccesses fails\n expect(logErrorSpy).toHaveBeenCalled()\n })\n\n it('throws BAD_REQUEST when link is invalid', async () => {\n const disabledLink = { ...baseLink, user: { id: 7, login: 'john', isActive: false } }\n linksQueriesMock.linkFromUUID.mockResolvedValueOnce(disabledLink)\n\n await expect(service.linkAccess(identity, disabledLink.uuid, req, res)).rejects.toMatchObject({\n status: 400\n })\n })\n\n it('returns undefined for already authenticated directory access (same user) and does not set cookies or increment', async () => {\n const sameUserIdentity = { id: baseLink.user.id, login: 'john' }\n const link = { ...baseLink, requireAuth: true }\n linksQueriesMock.linkFromUUID.mockResolvedValueOnce(link)\n const spaceLink = {\n space: { alias: 'files', name: 'Space' },\n share: { isDir: true, name: 'dir', alias: 'share' }\n } as any\n linksQueriesMock.spaceLink.mockResolvedValueOnce(spaceLink)\n\n const result = await service.linkAccess(sameUserIdentity as any, link.uuid, req, res)\n\n expect(result).toBeUndefined()\n expect(authManagerMock.setCookies).not.toHaveBeenCalled()\n expect(linksQueriesMock.incrementLinkNbAccess).not.toHaveBeenCalled()\n })\n\n it('throws INTERNAL_SERVER_ERROR when file checks fail during streaming', async () => {\n const link = { ...baseLink, limitAccess: 10 }\n linksQueriesMock.linkFromUUID.mockResolvedValueOnce(link)\n const spaceLink = {\n space: null,\n share: { isDir: false, name: 'bad.txt', alias: 'share' }\n } as any\n linksQueriesMock.spaceLink.mockResolvedValueOnce(spaceLink)\n\n spacesManagerMock.spaceEnv.mockResolvedValueOnce({} as any)\n filesManagerMock.sendFileFromSpace.mockReturnValueOnce({\n checks: jest.fn().mockRejectedValueOnce(new Error('disk error')),\n stream: jest.fn()\n } as any)\n\n await expect(service.linkAccess(identity, link.uuid, req, res)).rejects.toMatchObject({\n status: 500\n })\n expect(linksQueriesMock.incrementLinkNbAccess).toHaveBeenCalledWith(link.uuid)\n })\n })\n\n describe('linkAuthentication', () => {\n const req: any = { ip: '10.0.0.1' }\n const res: any = {}\n\n it('returns cookies when password is correct', async () => {\n const link = { ...baseLink, requireAuth: true }\n linksQueriesMock.linkFromUUID.mockResolvedValueOnce(link)\n usersManagerMock.compareUserPassword.mockResolvedValueOnce(true)\n // usersManagerMock.updateAccesses.mockResolvedValueOnce(undefined) // removed to ensure rejection is hit\n const loginDto: any = { token: 'abc' }\n authManagerMock.setCookies.mockResolvedValueOnce(loginDto)\n\n // cover: usersManager.updateAccesses.catch(...) should log an error when updateAccesses rejects (success flow)\n const logErrorSpy = jest.spyOn((service as any).logger, 'error').mockImplementation(() => undefined as any)\n usersManagerMock.updateAccesses.mockRejectedValueOnce(new Error('updateAccesses auth success boom'))\n\n const result = await service.linkAuthentication(identity, link.uuid, { password: 'secret' } as any, req, res)\n\n expect(result).toBe(loginDto)\n expect(usersManagerMock.compareUserPassword).toHaveBeenCalledWith(link.user.id, 'secret')\n expect(usersManagerMock.updateAccesses).toHaveBeenCalledWith(expect.anything(), req.ip, true)\n expect(authManagerMock.setCookies).toHaveBeenCalled()\n // should log error when updateAccesses fails in successful authentication\n expect(logErrorSpy).toHaveBeenCalled()\n })\n\n it('throws FORBIDDEN when password is incorrect', async () => {\n const link = { ...baseLink, requireAuth: true }\n linksQueriesMock.linkFromUUID.mockResolvedValueOnce(link)\n usersManagerMock.compareUserPassword.mockResolvedValueOnce(false)\n usersManagerMock.updateAccesses.mockResolvedValueOnce(undefined)\n\n await expect(service.linkAuthentication(identity, link.uuid, { password: 'bad' } as any, req, res)).rejects.toMatchObject({ status: 403 })\n })\n\n it('throws BAD_REQUEST when link is invalid (e.g., expired)', async () => {\n const expired = { ...baseLink, expiresAt: new Date(Date.now() - 60_000) }\n linksQueriesMock.linkFromUUID.mockResolvedValueOnce(expired)\n\n await expect(service.linkAuthentication(identity, expired.uuid, { password: 'whatever' } as any, req, res)).rejects.toMatchObject({\n status: 400\n })\n })\n })\n})\n"],"names":["jest","mock","getAvatarBase64","fn","describe","LinksManager","name","service","linksQueriesMock","usersManagerMock","filesManagerMock","spacesManagerMock","authManagerMock","identity","id","login","baseLink","uuid","user","isActive","requireAuth","limitAccess","nbAccess","expiresAt","beforeAll","linkFromUUID","spaceLink","incrementLinkNbAccess","mockResolvedValue","undefined","compareUserPassword","updateAccesses","sendFileFromSpace","spaceEnv","setCookies","module","Test","createTestingModule","providers","provide","DB_TOKEN_PROVIDER","useValue","Cache","ContextManager","NotificationsManager","HttpService","FilesLockManager","ConfigService","JwtService","AuthManager","UsersManager","UsersQueries","AdminUsersManager","AdminUsersQueries","FilesQueries","FilesManager","SpacesQueries","SpacesManager","SharesQueries","SharesManager","LinksQueries","compile","useLogger","get","beforeEach","clearAllMocks","it","expect","toBeDefined","link","mockResolvedValueOnce","owner","share","isDir","alias","space","res","linkValidation","ok","toBe","error","toBeNull","toBeUndefined","toHaveBeenCalledWith","directCheck","checkLink","String","toLowerCase","toContain","req","ip","streamable","some","mockReturnValueOnce","checks","stream","logErrorSpy","spyOn","logger","mockImplementation","mockRejectedValueOnce","Error","result","linkAccess","toHaveBeenCalled","anything","loginDto","token","spaceEnvFromLink","UserModel","disabledLink","rejects","toMatchObject","status","sameUserIdentity","not","linkAuthentication","password","expired","Date","now"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../backend/src/applications/links/services/links-manager.service.spec.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { HttpService } from '@nestjs/axios'\nimport { ConfigService } from '@nestjs/config'\nimport { JwtService } from '@nestjs/jwt'\nimport { Test, TestingModule } from '@nestjs/testing'\nimport { AuthManager } from '../../../authentication/services/auth-manager.service'\nimport { Cache } from '../../../infrastructure/cache/services/cache.service'\nimport { ContextManager } from '../../../infrastructure/context/services/context-manager.service'\nimport { DB_TOKEN_PROVIDER } from '../../../infrastructure/database/constants'\nimport { FilesLockManager } from '../../files/services/files-lock-manager.service'\nimport { FilesManager } from '../../files/services/files-manager.service'\nimport { FilesQueries } from '../../files/services/files-queries.service'\nimport { NotificationsManager } from '../../notifications/services/notifications-manager.service'\nimport { SharesManager } from '../../shares/services/shares-manager.service'\nimport { SharesQueries } from '../../shares/services/shares-queries.service'\nimport { SpacesManager } from '../../spaces/services/spaces-manager.service'\nimport { SpacesQueries } from '../../spaces/services/spaces-queries.service'\nimport { UserModel } from '../../users/models/user.model'\nimport { AdminUsersManager } from '../../users/services/admin-users-manager.service'\nimport { AdminUsersQueries } from '../../users/services/admin-users-queries.service'\nimport { UsersManager } from '../../users/services/users-manager.service'\nimport { UsersQueries } from '../../users/services/users-queries.service'\nimport { getAvatarBase64 } from '../../users/utils/avatar'\nimport { LinksManager } from './links-manager.service'\nimport { LinksQueries } from './links-queries.service'\n\njest.mock('../../users/utils/avatar', () => ({\n getAvatarBase64: jest.fn()\n}))\n\ndescribe(LinksManager.name, () => {\n let service: LinksManager\n let linksQueriesMock: jest.Mocked<LinksQueries>\n let usersManagerMock: jest.Mocked<UsersManager>\n let filesManagerMock: jest.Mocked<FilesManager>\n let spacesManagerMock: jest.Mocked<SpacesManager>\n let authManagerMock: jest.Mocked<AuthManager>\n\n const identity: any = { id: 42, login: 'visitor' }\n const baseLink: any = {\n uuid: 'uuid-123',\n user: { id: 7, login: 'john', isActive: true },\n requireAuth: false,\n limitAccess: 0,\n nbAccess: 0,\n expiresAt: null\n }\n\n beforeAll(async () => {\n linksQueriesMock = {\n linkFromUUID: jest.fn(),\n spaceLink: jest.fn(),\n incrementLinkNbAccess: jest.fn().mockResolvedValue(undefined)\n } as any\n\n usersManagerMock = {\n compareUserPassword: jest.fn(),\n updateAccesses: jest.fn()\n } as any\n\n filesManagerMock = {\n sendFileFromSpace: jest.fn()\n } as any\n\n spacesManagerMock = {\n spaceEnv: jest.fn()\n } as any\n\n authManagerMock = {\n setCookies: jest.fn()\n } as any\n\n const module: TestingModule = await Test.createTestingModule({\n providers: [\n {\n provide: DB_TOKEN_PROVIDER,\n useValue: {}\n },\n {\n provide: Cache,\n useValue: {}\n },\n { provide: ContextManager, useValue: {} },\n {\n provide: NotificationsManager,\n useValue: {}\n },\n { provide: HttpService, useValue: {} },\n { provide: FilesLockManager, useValue: {} },\n { provide: ConfigService, useValue: {} },\n { provide: JwtService, useValue: {} },\n { provide: AuthManager, useValue: authManagerMock },\n { provide: UsersManager, useValue: usersManagerMock },\n { provide: UsersQueries, useValue: {} },\n { provide: AdminUsersManager, useValue: {} },\n { provide: AdminUsersQueries, useValue: {} },\n { provide: FilesQueries, useValue: {} },\n { provide: FilesManager, useValue: filesManagerMock },\n { provide: SpacesQueries, useValue: {} },\n { provide: SpacesManager, useValue: spacesManagerMock },\n { provide: SharesQueries, useValue: {} },\n { provide: SharesManager, useValue: {} },\n { provide: LinksQueries, useValue: linksQueriesMock },\n LinksManager\n ]\n }).compile()\n\n module.useLogger(['fatal'])\n service = module.get<LinksManager>(LinksManager)\n })\n\n beforeEach(() => {\n jest.clearAllMocks()\n })\n\n it('should be defined', () => {\n expect(service).toBeDefined()\n })\n\n describe('linkValidation', () => {\n it('returns ok with sanitized owner and avatar when link is valid', async () => {\n const link = { ...baseLink }\n linksQueriesMock.linkFromUUID.mockResolvedValueOnce(link)\n const spaceLink = {\n owner: { login: 'jane' },\n share: { isDir: true, alias: 's-alias', name: 'Docs' },\n space: null\n } as any\n linksQueriesMock.spaceLink.mockResolvedValueOnce(spaceLink)\n ;(getAvatarBase64 as jest.Mock).mockResolvedValueOnce('base64-xxx')\n\n const res = await service.linkValidation(identity, link.uuid)\n\n expect(res.ok).toBe(true)\n expect(res.error).toBeNull()\n expect(res.link).toBe(spaceLink)\n expect((spaceLink.owner as any).login).toBeUndefined()\n expect(linksQueriesMock.spaceLink).toHaveBeenCalledWith(link.uuid)\n expect(getAvatarBase64).toHaveBeenCalledWith('jane')\n\n // extra coverage: directly assert private checkLink with default ignoreAuth=false\n const directCheck = (service as any).checkLink(identity, link)\n expect(directCheck).toBe(true)\n })\n\n it('returns error and null link when uuid is not found', async () => {\n linksQueriesMock.linkFromUUID.mockResolvedValueOnce(null)\n\n const res = await service.linkValidation(identity, 'missing-uuid')\n\n expect(res.ok).toBe(false)\n expect(res.error).toBeDefined()\n expect(res.link).toBeNull()\n })\n\n it('returns unauthorized when auth is required and identity differs', async () => {\n const link = { ...baseLink, requireAuth: true }\n linksQueriesMock.linkFromUUID.mockResolvedValueOnce(link)\n\n const res = await service.linkValidation(identity, link.uuid)\n\n expect(res.ok).toBe(false)\n expect(String(res.error).toLowerCase()).toContain('unauthorized')\n expect(res.link).toBeNull()\n\n // extra coverage: directly assert private checkLink with default ignoreAuth=false\n const directCheck = (service as any).checkLink(identity, link)\n expect(String(directCheck).toLowerCase()).toContain('unauthorized')\n })\n\n it('returns exceeded when nbAccess >= limitAccess', async () => {\n const link = { ...baseLink, limitAccess: 5, nbAccess: 5 }\n linksQueriesMock.linkFromUUID.mockResolvedValueOnce(link)\n\n const res = await service.linkValidation(identity, link.uuid)\n\n expect(res.ok).toBe(false)\n expect(String(res.error).toLowerCase()).toContain('exceeded')\n })\n })\n\n describe('linkAccess', () => {\n const req: any = { ip: '127.0.0.1' }\n const res: any = {}\n\n it('streams a file when link targets a single file', async () => {\n const link = { ...baseLink, limitAccess: 10 }\n linksQueriesMock.linkFromUUID.mockResolvedValueOnce(link)\n const spaceLink = {\n space: null,\n share: { isDir: false, name: 'file.txt', alias: 'share-alias' }\n } as any\n linksQueriesMock.spaceLink.mockResolvedValueOnce(spaceLink)\n spacesManagerMock.spaceEnv.mockResolvedValueOnce({} as any)\n const streamable: any = { some: 'stream' }\n filesManagerMock.sendFileFromSpace.mockReturnValueOnce({\n checks: jest.fn().mockResolvedValueOnce(undefined),\n stream: jest.fn().mockResolvedValueOnce(streamable)\n } as any)\n\n // cover: incrementLinkNbAccess.catch(...) should log an error when the query rejects\n const logErrorSpy = jest.spyOn((service as any).logger, 'error').mockImplementation(() => undefined as any)\n linksQueriesMock.incrementLinkNbAccess.mockRejectedValueOnce(new Error('increment boom'))\n\n const result = await service.linkDownload(identity, link.uuid, req, res)\n\n expect(result).toBe(streamable)\n expect(filesManagerMock.sendFileFromSpace).toHaveBeenCalled()\n expect(linksQueriesMock.incrementLinkNbAccess).toHaveBeenCalledWith(link.uuid)\n // Assert repository selection for a share file (space falsy)\n // should call spacesManager.spaceEnv with SHARES and share alias when link.space is falsy\n expect(spacesManagerMock.spaceEnv).toHaveBeenCalledWith(expect.anything(), ['shares', 'share-alias'])\n // should log error when increment fails\n expect(logErrorSpy).toHaveBeenCalled()\n })\n\n it('authenticates and returns cookies when link targets a directory and user is different', async () => {\n const link = { ...baseLink, requireAuth: false, user: { id: 7, login: 'john', isActive: true } }\n linksQueriesMock.linkFromUUID.mockResolvedValueOnce(link)\n const spaceLink = {\n space: { alias: 'files', name: 'My Space' },\n share: { isDir: true, name: 'ignored', alias: 'ignored' }\n } as any\n linksQueriesMock.spaceLink.mockResolvedValueOnce(spaceLink)\n\n const loginDto: any = { token: 'jwt' }\n authManagerMock.setCookies.mockResolvedValueOnce(loginDto)\n\n // cover: usersManager.updateAccesses.catch(...) should log an error when updateAccesses rejects\n const logErrorSpy = jest.spyOn((service as any).logger, 'error').mockImplementation(() => undefined as any)\n usersManagerMock.updateAccesses.mockRejectedValueOnce(new Error('updateAccesses boom'))\n\n const result = await service.linkAccess(identity, link.uuid, req, res)\n\n expect(result).toBe(loginDto)\n expect(usersManagerMock.updateAccesses).toHaveBeenCalledWith(expect.anything(), req.ip, true)\n expect(authManagerMock.setCookies).toHaveBeenCalled()\n // additionally cover the \"space truthy\" branch by calling the private helper directly\n // should call spacesManager.spaceEnv with FILES and space alias when link.space is truthy\n await (service as any).spaceEnvFromLink(new UserModel(link.user as any), spaceLink as any)\n expect(spacesManagerMock.spaceEnv).toHaveBeenCalledWith(expect.anything(), ['files', 'files'])\n // should log error when updateAccesses fails\n expect(logErrorSpy).toHaveBeenCalled()\n })\n\n it('throws BAD_REQUEST when link is invalid', async () => {\n const disabledLink = { ...baseLink, user: { id: 7, login: 'john', isActive: false } }\n linksQueriesMock.linkFromUUID.mockResolvedValueOnce(disabledLink)\n\n await expect(service.linkAccess(identity, disabledLink.uuid, req, res)).rejects.toMatchObject({\n status: 400\n })\n })\n\n it('returns undefined for already authenticated directory access (same user) and does not set cookies or increment', async () => {\n const sameUserIdentity = { id: baseLink.user.id, login: 'john' }\n const link = { ...baseLink, requireAuth: true }\n linksQueriesMock.linkFromUUID.mockResolvedValueOnce(link)\n const spaceLink = {\n space: { alias: 'files', name: 'Space' },\n share: { isDir: true, name: 'dir', alias: 'share' }\n } as any\n linksQueriesMock.spaceLink.mockResolvedValueOnce(spaceLink)\n\n const result = await service.linkAccess(sameUserIdentity as any, link.uuid, req, res)\n\n expect(result.user.id).toEqual(baseLink.user.id)\n expect(authManagerMock.setCookies).not.toHaveBeenCalled()\n expect(linksQueriesMock.incrementLinkNbAccess).not.toHaveBeenCalled()\n })\n\n it('throws INTERNAL_SERVER_ERROR when file checks fail during streaming', async () => {\n const link = { ...baseLink, limitAccess: 10 }\n linksQueriesMock.linkFromUUID.mockResolvedValueOnce(link)\n const spaceLink = {\n space: null,\n share: { isDir: false, name: 'bad.txt', alias: 'share' }\n } as any\n linksQueriesMock.spaceLink.mockReset()\n linksQueriesMock.spaceLink.mockResolvedValueOnce(spaceLink)\n\n spacesManagerMock.spaceEnv.mockResolvedValueOnce({} as any)\n filesManagerMock.sendFileFromSpace.mockReturnValueOnce({\n checks: jest.fn().mockRejectedValueOnce(new Error('disk error')),\n stream: jest.fn()\n } as any)\n\n await expect(service.linkDownload(identity, link.uuid, req, res)).rejects.toMatchObject({\n status: 500\n })\n expect(linksQueriesMock.incrementLinkNbAccess).toHaveBeenCalledWith(link.uuid)\n })\n })\n\n describe('linkAuthentication', () => {\n const req: any = { ip: '10.0.0.1' }\n const res: any = {}\n\n it('returns cookies when password is correct', async () => {\n const link = { ...baseLink, requireAuth: true }\n linksQueriesMock.linkFromUUID.mockResolvedValueOnce(link)\n usersManagerMock.compareUserPassword.mockResolvedValueOnce(true)\n // usersManagerMock.updateAccesses.mockResolvedValueOnce(undefined) // removed to ensure rejection is hit\n const loginDto: any = { token: 'abc' }\n authManagerMock.setCookies.mockResolvedValueOnce(loginDto)\n\n // cover: usersManager.updateAccesses.catch(...) should log an error when updateAccesses rejects (success flow)\n const logErrorSpy = jest.spyOn((service as any).logger, 'error').mockImplementation(() => undefined as any)\n usersManagerMock.updateAccesses.mockRejectedValueOnce(new Error('updateAccesses auth success boom'))\n\n const result = await service.linkAuthentication(identity, link.uuid, { password: 'secret' } as any, req, res)\n\n expect(result).toBe(loginDto)\n expect(usersManagerMock.compareUserPassword).toHaveBeenCalledWith(link.user.id, 'secret')\n expect(usersManagerMock.updateAccesses).toHaveBeenCalledWith(expect.anything(), req.ip, true)\n expect(authManagerMock.setCookies).toHaveBeenCalled()\n // should log error when updateAccesses fails in successful authentication\n expect(logErrorSpy).toHaveBeenCalled()\n })\n\n it('throws FORBIDDEN when password is incorrect', async () => {\n const link = { ...baseLink, requireAuth: true }\n linksQueriesMock.linkFromUUID.mockResolvedValueOnce(link)\n usersManagerMock.compareUserPassword.mockResolvedValueOnce(false)\n usersManagerMock.updateAccesses.mockResolvedValueOnce(undefined)\n\n await expect(service.linkAuthentication(identity, link.uuid, { password: 'bad' } as any, req, res)).rejects.toMatchObject({ status: 403 })\n })\n\n it('throws BAD_REQUEST when link is invalid (e.g., expired)', async () => {\n const expired = { ...baseLink, expiresAt: new Date(Date.now() - 60_000) }\n linksQueriesMock.linkFromUUID.mockResolvedValueOnce(expired)\n\n await expect(service.linkAuthentication(identity, expired.uuid, { password: 'whatever' } as any, req, res)).rejects.toMatchObject({\n status: 400\n })\n })\n })\n})\n"],"names":["jest","mock","getAvatarBase64","fn","describe","LinksManager","name","service","linksQueriesMock","usersManagerMock","filesManagerMock","spacesManagerMock","authManagerMock","identity","id","login","baseLink","uuid","user","isActive","requireAuth","limitAccess","nbAccess","expiresAt","beforeAll","linkFromUUID","spaceLink","incrementLinkNbAccess","mockResolvedValue","undefined","compareUserPassword","updateAccesses","sendFileFromSpace","spaceEnv","setCookies","module","Test","createTestingModule","providers","provide","DB_TOKEN_PROVIDER","useValue","Cache","ContextManager","NotificationsManager","HttpService","FilesLockManager","ConfigService","JwtService","AuthManager","UsersManager","UsersQueries","AdminUsersManager","AdminUsersQueries","FilesQueries","FilesManager","SpacesQueries","SpacesManager","SharesQueries","SharesManager","LinksQueries","compile","useLogger","get","beforeEach","clearAllMocks","it","expect","toBeDefined","link","mockResolvedValueOnce","owner","share","isDir","alias","space","res","linkValidation","ok","toBe","error","toBeNull","toBeUndefined","toHaveBeenCalledWith","directCheck","checkLink","String","toLowerCase","toContain","req","ip","streamable","some","mockReturnValueOnce","checks","stream","logErrorSpy","spyOn","logger","mockImplementation","mockRejectedValueOnce","Error","result","linkDownload","toHaveBeenCalled","anything","loginDto","token","linkAccess","spaceEnvFromLink","UserModel","disabledLink","rejects","toMatchObject","status","sameUserIdentity","toEqual","not","mockReset","linkAuthentication","password","expired","Date","now"],"mappings":"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"}
@@ -124,7 +124,10 @@ let LinksQueries = class LinksQueries {
124
124
  alias: _sharesschema.shares.alias,
125
125
  hasParent: (0, _drizzleorm.isNotNull)(_sharesschema.shares.parentId).mapWith(Boolean),
126
126
  isDir: (0, _drizzleorm.sql)`IF (${(0, _drizzleorm.isNotNull)(_sharesschema.shares.externalPath)} OR ${(0, _drizzleorm.isNotNull)(shareSpaceRoot.externalPath)}, 1 ,${_filesschema.files.isDir})`.mapWith(Boolean),
127
- mime: _filesschema.files.mime
127
+ mime: _filesschema.files.mime,
128
+ mtime: _filesschema.files.mtime,
129
+ size: _filesschema.files.size,
130
+ permissions: _sharesmembersschema.sharesMembers.permissions
128
131
  },
129
132
  space: {
130
133
  name: _spacesschema.spaces.name,
@@ -135,7 +138,11 @@ let LinksQueries = class LinksQueries {
135
138
  fullName: (0, _usersschema.userFullNameSQL)(shareOwner)
136
139
  }
137
140
  }).from(_linksschema.links).leftJoin(_sharesmembersschema.sharesMembers, (0, _drizzleorm.eq)(_sharesmembersschema.sharesMembers.linkId, _linksschema.links.id)).leftJoin(_sharesschema.shares, (0, _drizzleorm.eq)(_sharesschema.shares.id, _sharesmembersschema.sharesMembers.shareId)).leftJoin(shareOwner, (0, _drizzleorm.eq)(shareOwner.id, _sharesschema.shares.ownerId)).leftJoin(_spacesmembersschema.spacesMembers, (0, _drizzleorm.eq)(_spacesmembersschema.spacesMembers.linkId, _linksschema.links.id)).leftJoin(_spacesschema.spaces, (0, _drizzleorm.eq)(_spacesschema.spaces.id, _spacesmembersschema.spacesMembers.spaceId)).leftJoin(shareSpaceRoot, (0, _drizzleorm.and)((0, _drizzleorm.isNull)(_sharesschema.shares.externalPath), (0, _drizzleorm.isNull)(_sharesschema.shares.fileId), (0, _drizzleorm.eq)(shareSpaceRoot.id, _sharesschema.shares.spaceRootId))).leftJoin(_filesschema.files, (0, _drizzleorm.or)((0, _drizzleorm.and)((0, _drizzleorm.isNotNull)(_sharesschema.shares.fileId), (0, _drizzleorm.eq)(_filesschema.files.id, _sharesschema.shares.fileId)), (0, _drizzleorm.and)((0, _drizzleorm.isNull)(_sharesschema.shares.externalPath), (0, _drizzleorm.isNotNull)(shareSpaceRoot.fileId), (0, _drizzleorm.eq)(_filesschema.files.id, shareSpaceRoot.fileId)))).where((0, _drizzleorm.eq)(_linksschema.links.uuid, uuid)).limit(1);
138
- return r;
141
+ return {
142
+ ...r,
143
+ space: r.space?.name ? r.space : null,
144
+ share: r.share?.name ? r.share : null
145
+ };
139
146
  }
140
147
  async incrementLinkNbAccess(uuid) {
141
148
  await this.db.update(_linksschema.links).set({