@sync-in/server 2.0.0 → 2.1.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 (232) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +38 -28
  3. package/environment/environment.dist.yaml +4 -0
  4. package/package.json +17 -17
  5. package/server/applications/files/utils/doc-textify/adapters/pdf.js +1 -1
  6. package/server/applications/files/utils/doc-textify/adapters/pdf.js.map +1 -1
  7. package/server/applications/files/utils/files.js +37 -24
  8. package/server/applications/files/utils/files.js.map +1 -1
  9. package/server/applications/notifications/i18n/index.js +5 -0
  10. package/server/applications/notifications/i18n/index.js.map +1 -1
  11. package/server/applications/notifications/i18n/nl.js +54 -0
  12. package/server/applications/notifications/i18n/nl.js.map +1 -0
  13. package/server/applications/sync/services/sync-clients-manager.service.js +1 -1
  14. package/server/applications/sync/services/sync-clients-manager.service.js.map +1 -1
  15. package/server/authentication/providers/oidc/auth-oidc.config.js +5 -0
  16. package/server/authentication/providers/oidc/auth-oidc.config.js.map +1 -1
  17. package/server/authentication/providers/oidc/auth-provider-oidc.service.js +10 -7
  18. package/server/authentication/providers/oidc/auth-provider-oidc.service.js.map +1 -1
  19. package/server/authentication/providers/oidc/auth-provider-oidc.service.spec.js +16 -0
  20. package/server/authentication/providers/oidc/auth-provider-oidc.service.spec.js.map +1 -1
  21. package/server/common/i18n.js +1 -0
  22. package/server/common/i18n.js.map +1 -1
  23. package/server/infrastructure/database/database.module.js +7 -0
  24. package/server/infrastructure/database/database.module.js.map +1 -1
  25. package/server/infrastructure/database/scripts/check-db.js +20 -0
  26. package/server/infrastructure/database/scripts/check-db.js.map +1 -0
  27. package/static/3rdpartylicenses.txt +391 -391
  28. package/static/assets/pdfjs/build/pdf.mjs +512 -417
  29. package/static/assets/pdfjs/build/pdf.mjs.map +1 -1
  30. package/static/assets/pdfjs/build/pdf.sandbox.mjs +390 -4
  31. package/static/assets/pdfjs/build/pdf.sandbox.mjs.map +1 -1
  32. package/static/assets/pdfjs/build/pdf.worker.mjs +2378 -401
  33. package/static/assets/pdfjs/build/pdf.worker.mjs.map +1 -1
  34. package/static/assets/pdfjs/version +1 -1
  35. package/static/assets/pdfjs/web/debugger.mjs +6 -15
  36. package/static/assets/pdfjs/web/locale/be/viewer.ftl +78 -0
  37. package/static/assets/pdfjs/web/locale/cs/viewer.ftl +82 -0
  38. package/static/assets/pdfjs/web/locale/cy/viewer.ftl +90 -0
  39. package/static/assets/pdfjs/web/locale/de/viewer.ftl +75 -0
  40. package/static/assets/pdfjs/web/locale/dsb/viewer.ftl +82 -0
  41. package/static/assets/pdfjs/web/locale/el/viewer.ftl +74 -0
  42. package/static/assets/pdfjs/web/locale/en-CA/viewer.ftl +74 -0
  43. package/static/assets/pdfjs/web/locale/en-GB/viewer.ftl +81 -0
  44. package/static/assets/pdfjs/web/locale/en-US/viewer.ftl +14 -14
  45. package/static/assets/pdfjs/web/locale/eo/viewer.ftl +74 -0
  46. package/static/assets/pdfjs/web/locale/es-AR/viewer.ftl +74 -0
  47. package/static/assets/pdfjs/web/locale/es-CL/viewer.ftl +74 -0
  48. package/static/assets/pdfjs/web/locale/es-ES/viewer.ftl +74 -0
  49. package/static/assets/pdfjs/web/locale/es-MX/viewer.ftl +75 -0
  50. package/static/assets/pdfjs/web/locale/eu/viewer.ftl +74 -0
  51. package/static/assets/pdfjs/web/locale/fi/viewer.ftl +74 -0
  52. package/static/assets/pdfjs/web/locale/fr/viewer.ftl +81 -0
  53. package/static/assets/pdfjs/web/locale/fy-NL/viewer.ftl +74 -0
  54. package/static/assets/pdfjs/web/locale/gn/viewer.ftl +77 -0
  55. package/static/assets/pdfjs/web/locale/he/viewer.ftl +81 -0
  56. package/static/assets/pdfjs/web/locale/hsb/viewer.ftl +87 -0
  57. package/static/assets/pdfjs/web/locale/hu/viewer.ftl +74 -0
  58. package/static/assets/pdfjs/web/locale/hy-AM/viewer.ftl +10 -10
  59. package/static/assets/pdfjs/web/locale/ia/viewer.ftl +75 -0
  60. package/static/assets/pdfjs/web/locale/it/viewer.ftl +81 -0
  61. package/static/assets/pdfjs/web/locale/ja/viewer.ftl +58 -0
  62. package/static/assets/pdfjs/web/locale/ka/viewer.ftl +74 -0
  63. package/static/assets/pdfjs/web/locale/kk/viewer.ftl +81 -0
  64. package/static/assets/pdfjs/web/locale/km/viewer.ftl +44 -0
  65. package/static/assets/pdfjs/web/locale/ko/viewer.ftl +65 -0
  66. package/static/assets/pdfjs/web/locale/nb-NO/viewer.ftl +74 -0
  67. package/static/assets/pdfjs/web/locale/nl/viewer.ftl +81 -0
  68. package/static/assets/pdfjs/web/locale/nn-NO/viewer.ftl +75 -0
  69. package/static/assets/pdfjs/web/locale/pa-IN/viewer.ftl +74 -0
  70. package/static/assets/pdfjs/web/locale/pl/viewer.ftl +78 -0
  71. package/static/assets/pdfjs/web/locale/pt-BR/viewer.ftl +74 -0
  72. package/static/assets/pdfjs/web/locale/ro/viewer.ftl +81 -3
  73. package/static/assets/pdfjs/web/locale/ru/viewer.ftl +85 -0
  74. package/static/assets/pdfjs/web/locale/sk/viewer.ftl +83 -1
  75. package/static/assets/pdfjs/web/locale/sl/viewer.ftl +82 -0
  76. package/static/assets/pdfjs/web/locale/sv-SE/viewer.ftl +81 -0
  77. package/static/assets/pdfjs/web/locale/tg/viewer.ftl +74 -0
  78. package/static/assets/pdfjs/web/locale/th/viewer.ftl +58 -0
  79. package/static/assets/pdfjs/web/locale/tr/viewer.ftl +74 -0
  80. package/static/assets/pdfjs/web/locale/uk/viewer.ftl +144 -0
  81. package/static/assets/pdfjs/web/locale/vi/viewer.ftl +65 -0
  82. package/static/assets/pdfjs/web/locale/zh-CN/viewer.ftl +61 -3
  83. package/static/assets/pdfjs/web/locale/zh-TW/viewer.ftl +65 -0
  84. package/static/assets/pdfjs/web/viewer.css +292 -152
  85. package/static/assets/pdfjs/web/viewer.html +14 -21
  86. package/static/assets/pdfjs/web/viewer.mjs +782 -327
  87. package/static/assets/pdfjs/web/viewer.mjs.map +1 -1
  88. package/static/assets/pdfjs/web/wasm/jbig2.wasm +0 -0
  89. package/static/{chunk-7WOPGQXB.js → chunk-25XD7GNL.js} +1 -1
  90. package/static/{chunk-E5WI5725.js → chunk-2W4Z5VTY.js} +1 -1
  91. package/static/chunk-2XNPCG4F.js +1 -0
  92. package/static/chunk-3B2RHO2H.js +1 -0
  93. package/static/{chunk-UMDRE4S7.js → chunk-3DNAFXGN.js} +1 -1
  94. package/static/{chunk-OANZITPM.js → chunk-3EPN4WD7.js} +1 -1
  95. package/static/chunk-4L2WBHB6.js +1 -0
  96. package/static/chunk-4RYZNAIZ.js +1 -0
  97. package/static/chunk-5C77PEFA.js +1 -0
  98. package/static/chunk-5JA3DZWQ.js +1 -0
  99. package/static/chunk-5MSTJGPE.js +1 -0
  100. package/static/{chunk-VSBFNFOM.js → chunk-5O7KTREI.js} +1 -1
  101. package/static/chunk-6KORLSUZ.js +1 -0
  102. package/static/chunk-6LIHQVPW.js +2 -0
  103. package/static/{chunk-2CKLZ3FM.js → chunk-72BXGTUG.js} +1 -1
  104. package/static/chunk-7423QJQN.js +1 -0
  105. package/static/chunk-77HB4DE6.js +1 -0
  106. package/static/{chunk-RWAAC3A4.js → chunk-7PVTMTWH.js} +1 -1
  107. package/static/{chunk-IWWBV6EM.js → chunk-7UIOW5AD.js} +1 -1
  108. package/static/chunk-7YMONFZZ.js +1 -0
  109. package/static/chunk-AUPNSQGJ.js +1 -0
  110. package/static/chunk-AYF6ZI6L.js +5 -0
  111. package/static/chunk-BEESW4BF.js +1 -0
  112. package/static/{chunk-2R6IBBPZ.js → chunk-BI3VI6XG.js} +1 -1
  113. package/static/chunk-BMGKB3JK.js +4 -0
  114. package/static/chunk-CFXAKPPB.js +563 -0
  115. package/static/chunk-CGYZG6CB.js +2 -0
  116. package/static/{chunk-K25E7YGG.js → chunk-CHAYERHQ.js} +1 -1
  117. package/static/chunk-CLNKPQMZ.js +1 -0
  118. package/static/{chunk-ISV3BO6R.js → chunk-CVLSRA2W.js} +1 -1
  119. package/static/chunk-D373HBMN.js +1 -0
  120. package/static/{chunk-MTRXBVWZ.js → chunk-D43PHZEY.js} +1 -1
  121. package/static/chunk-D44RAVFK.js +1 -0
  122. package/static/chunk-DFVVEGWC.js +1 -0
  123. package/static/chunk-E43LXI27.js +1 -0
  124. package/static/{chunk-GQFMWVFD.js → chunk-E5M4ZDID.js} +1 -1
  125. package/static/chunk-F7JS3YWP.js +2 -0
  126. package/static/chunk-FRWEEOOM.js +1 -0
  127. package/static/chunk-HIZGSP7R.js +1 -0
  128. package/static/chunk-HX6ZWXQD.js +1 -0
  129. package/static/chunk-HZ6LLXLE.js +1 -0
  130. package/static/{chunk-RLL634K4.js → chunk-IFHFPV5W.js} +1 -1
  131. package/static/{chunk-KJD3KFF3.js → chunk-JICKUOQ6.js} +1 -1
  132. package/static/chunk-KY2BW2LP.js +1 -0
  133. package/static/{chunk-CWYHOPOP.js → chunk-KYA3HPDX.js} +1 -1
  134. package/static/{chunk-RWCNTCU5.js → chunk-KYLATNX5.js} +1 -1
  135. package/static/chunk-LCZNSV4Z.js +1 -0
  136. package/static/{chunk-GVNTC564.js → chunk-MA6DYTMQ.js} +1 -1
  137. package/static/{chunk-KZS7CTNR.js → chunk-MXH5XYBD.js} +1 -1
  138. package/static/{chunk-NMTBMHUL.js → chunk-MXIJGSDQ.js} +1 -1
  139. package/static/{chunk-RS2OFKWP.js → chunk-NE75E3GD.js} +1 -1
  140. package/static/{chunk-MZQK6LNV.js → chunk-NJKIHMOH.js} +1 -1
  141. package/static/chunk-NMDGST45.js +1 -0
  142. package/static/{chunk-CU76ATCF.js → chunk-OF7P4LTI.js} +1 -1
  143. package/static/chunk-OK5K5CYJ.js +13 -0
  144. package/static/chunk-OZ6LXMUH.js +1 -0
  145. package/static/chunk-PFY6YUPS.js +2 -0
  146. package/static/chunk-PQ2YFCVM.js +1 -0
  147. package/static/{chunk-Y4AUYQTG.js → chunk-QPX34S6P.js} +4 -4
  148. package/static/{chunk-27ATUHBH.js → chunk-QQO7S2VY.js} +1 -1
  149. package/static/chunk-RANZZPRG.js +1 -0
  150. package/static/{chunk-WN4WXCVK.js → chunk-RNQB2QJB.js} +1 -1
  151. package/static/{chunk-Q3EGCMF5.js → chunk-S7QYAKG5.js} +1 -1
  152. package/static/chunk-SCEWOW5C.js +2 -0
  153. package/static/chunk-SQ2KIOJ7.js +1 -0
  154. package/static/{chunk-QVRVFYJH.js → chunk-SW5GCGOI.js} +1 -1
  155. package/static/chunk-SWCYWB2T.js +1 -0
  156. package/static/{chunk-4FIGEBNL.js → chunk-SWZ3L2LK.js} +1 -1
  157. package/static/{chunk-TWCGKSYE.js → chunk-TDK62XEQ.js} +1 -1
  158. package/static/chunk-TH4L5H6P.js +1 -0
  159. package/static/chunk-U3ZGYOLA.js +1 -0
  160. package/static/chunk-U6V4Y234.js +1 -0
  161. package/static/chunk-U7KNV64D.js +2 -0
  162. package/static/chunk-UC66HJV4.js +1 -0
  163. package/static/{chunk-7GWW6MJO.js → chunk-V4HAIERK.js} +1 -1
  164. package/static/chunk-V55LDRV7.js +1 -0
  165. package/static/{chunk-5CZOSAMZ.js → chunk-WF6D7CQ4.js} +1 -1
  166. package/static/chunk-WJPVHM7T.js +1 -0
  167. package/static/chunk-WM4XBDC2.js +1 -0
  168. package/static/chunk-X2EKQYMZ.js +1 -0
  169. package/static/{chunk-L5IHUVXL.js → chunk-XEDABMM5.js} +1 -1
  170. package/static/{chunk-OGE4SAHU.js → chunk-Y3YKVK3K.js} +1 -1
  171. package/static/{chunk-QIGUDEZF.js → chunk-YEOAMYRK.js} +1 -1
  172. package/static/{chunk-A4UGPSWX.js → chunk-ZLTL7I7D.js} +1 -1
  173. package/static/chunk-ZYNBYM7I.js +3 -0
  174. package/static/index.html +2 -2
  175. package/static/main-KFUSRCP5.js +5 -0
  176. package/static/media/inter-cyrillic-ext-wght-normal-IYF56FF6.woff2 +0 -0
  177. package/static/media/inter-cyrillic-wght-normal-JEOLYBOO.woff2 +0 -0
  178. package/static/media/inter-greek-ext-wght-normal-EOVOK2B5.woff2 +0 -0
  179. package/static/media/inter-greek-wght-normal-IRE366VL.woff2 +0 -0
  180. package/static/media/inter-latin-ext-wght-normal-HA22NDSG.woff2 +0 -0
  181. package/static/media/inter-latin-wght-normal-NRMW37G5.woff2 +0 -0
  182. package/static/media/inter-vietnamese-wght-normal-CE5GGD3W.woff2 +0 -0
  183. package/static/styles-66AEF62D.css +1 -0
  184. package/static/chunk-22TZP6HW.js +0 -1
  185. package/static/chunk-2QZPX7LO.js +0 -1
  186. package/static/chunk-4P3JABAP.js +0 -13
  187. package/static/chunk-677WUBCT.js +0 -1
  188. package/static/chunk-74CAHBFM.js +0 -1
  189. package/static/chunk-AHO37FKW.js +0 -1
  190. package/static/chunk-AQCXMKP3.js +0 -1
  191. package/static/chunk-B6PDYCRO.js +0 -3
  192. package/static/chunk-FC5HTKVM.js +0 -1
  193. package/static/chunk-FOSM7EYI.js +0 -1
  194. package/static/chunk-GAZO25PI.js +0 -1
  195. package/static/chunk-GB7ABR5N.js +0 -1
  196. package/static/chunk-GEHFKZQ5.js +0 -2
  197. package/static/chunk-HGL3NYP2.js +0 -2
  198. package/static/chunk-HLIWPWRA.js +0 -1
  199. package/static/chunk-HNYB3M4S.js +0 -1
  200. package/static/chunk-HUXAUQMN.js +0 -1
  201. package/static/chunk-I2XA6PPK.js +0 -1
  202. package/static/chunk-JV3AGU5B.js +0 -1
  203. package/static/chunk-K46PUTZB.js +0 -1
  204. package/static/chunk-KERFLJ56.js +0 -1
  205. package/static/chunk-KPKSI23S.js +0 -1
  206. package/static/chunk-L7RRX2M3.js +0 -1
  207. package/static/chunk-LGWJ2WKU.js +0 -1
  208. package/static/chunk-LUSVISM6.js +0 -1
  209. package/static/chunk-MLC7JK2H.js +0 -2
  210. package/static/chunk-MOHNYW2A.js +0 -1
  211. package/static/chunk-NCDUOVMW.js +0 -1
  212. package/static/chunk-NGUAJIGI.js +0 -1
  213. package/static/chunk-NIPP6JDI.js +0 -1
  214. package/static/chunk-O4XXMZFX.js +0 -4
  215. package/static/chunk-OI3ME22C.js +0 -1
  216. package/static/chunk-QF2NSHZA.js +0 -1
  217. package/static/chunk-QJVC3SRJ.js +0 -562
  218. package/static/chunk-QKN6LAAA.js +0 -1
  219. package/static/chunk-QRFESU5O.js +0 -2
  220. package/static/chunk-RFJIPIOK.js +0 -2
  221. package/static/chunk-S5Y64DDS.js +0 -1
  222. package/static/chunk-SLG5KDU6.js +0 -1
  223. package/static/chunk-TJ4CVFEL.js +0 -1
  224. package/static/chunk-VRIOLRYR.js +0 -5
  225. package/static/chunk-VS4O2XDP.js +0 -1
  226. package/static/chunk-WX7RXW7K.js +0 -1
  227. package/static/chunk-XC4POKR3.js +0 -2
  228. package/static/chunk-Y67J3BOL.js +0 -1
  229. package/static/chunk-YMIXHRJQ.js +0 -1
  230. package/static/chunk-ZUNKFAKP.js +0 -1
  231. package/static/main-QN4UCOC5.js +0 -5
  232. package/static/styles-46GLIE7Y.css +0 -1
package/CHANGELOG.md CHANGED
@@ -1,4 +1,22 @@
1
1
 
2
+ ## [2.1.0](https://github.com/Sync-in/server/compare/v2.0.0...v2.1.0) (2026-03-13)
3
+
4
+
5
+ ### Features
6
+
7
+ * **frontend** refresh UI ([#127](https://github.com/Sync-in/server/pull/127))
8
+ * **backend:auth:** add toggle for security.supportPKCE in OIDC provider ([d90cbf7](https://github.com/Sync-in/server/commit/d90cbf73e63336865c7aee91f3d8c7e727522cc1))
9
+ * **docker:** add FORCE_PERMISSIONS variable to set permissions on data files ([1eb57d6](https://github.com/Sync-in/server/commit/1eb57d60d1937be3261b0f4a3aad3082092d40a2))
10
+ * **frontend:i18n:** add nl ([4c3a0cb](https://github.com/Sync-in/server/commit/4c3a0cb8695d6387259ee48273b66faba938f8ce))
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * **backend:database:** ensure MySQL connection uses UTC timezone ([e7d2ed9](https://github.com/Sync-in/server/commit/e7d2ed9d2a09ad8374f61f6d33b7fec60592e428))
16
+ * **backend:files:** avoid buffer copy and ensure PDF document cleanup ([f28c71b](https://github.com/Sync-in/server/commit/f28c71bdf53ba524d6745746b805cd741776324f))
17
+ * **backend:files:** skip unreadable directories when walking for size and entry counts ([6b0a6a7](https://github.com/Sync-in/server/commit/6b0a6a70e70425ae2d0df2fdbb3b19c41ac8bd95))
18
+ * **frontend:recents:** move user avatar tooltip container to body to fix overlap with card ([5029911](https://github.com/Sync-in/server/commit/50299116b627817233021069a641a4514258f37b))
19
+
2
20
  ## [2.0.0](https://github.com/Sync-in/server/compare/v1.11.0...v2.0.0) (2026-02-10)
3
21
 
4
22
 
package/README.md CHANGED
@@ -22,11 +22,12 @@ _Welcome to the Sync-in server repository!_
22
22
  <a href="https://discord.gg/qhJyzwaymT" target="_blank"><img src="https://img.shields.io/discord/1391081837849346088?logo=discord&label=Discord" alt="Discord"/></a>
23
23
  <a href="https://deepwiki.com/Sync-in/server"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
24
24
 
25
- The **Sync-in Server** is designed to run on your own infrastructure, it gives you **full control over your data** while offering a modern,
26
- intuitive interface for both internal and external users.
25
+ **The Sync-in Server** runs on your own infrastructure, giving you **full ownership of your files and documents** while providing a modern and
26
+ intuitive collaboration platform. It combines collaborative spaces, secure file sharing, and fine-grained access control with strong security
27
+ foundations.
27
28
 
28
- With features like **collaborative spaces**, **secure file sharing**, **granular permission management**,
29
- Sync-in fits seamlessly into any environment from small teams to large enterprises, public institutions, or privacy-conscious individuals.
29
+ Built around open standards and interoperability, Sync-in supports multi-editor document collaboration, seamless client integration, and flexible
30
+ deployment models, making it suitable for self-hosted environments, organizations, and privacy-focused users.
30
31
 
31
32
  <picture>
32
33
  <source srcset="https://raw.githubusercontent.com/Sync-in/assets/main/server-dark.png" media="(prefers-color-scheme: dark)" />
@@ -38,30 +39,37 @@ Sync-in fits seamlessly into any environment — from small teams to large enter
38
39
  ## 🚀 Features
39
40
 
40
41
  - 🖥️ Modern, Fast, and High-Performance Interface
41
- - Sleek and intuitive UI for a seamless user experience
42
- - Optimized for speed and efficiency
43
- - 🔒 Security & Data Ownership
44
- - Full control over data security and compliance
45
- - Designed to protect sensitive documents and prevent unauthorized access
46
- - **Multi-Factor Authentication (MFA)**: TOTP (authenticator apps), recovery codes, app passwords
42
+ - Intuitive and responsive UI designed for productivity
43
+ - Optimized experience across devices
44
+ - 🆔 Modern Identity & Authentication
45
+ - OpenID Connect (OIDC) support for federated authentication and Single Sign-On (SSO)
46
+ - LDAP integration for enterprise and organizational environments
47
+ - Multi-Factor Authentication (MFA), recovery codes, and application passwords
48
+ - Unified authentication across Web, Desktop, and CLI clients
47
49
  - 🔑 Advanced User Access Control
48
- - **Spaces & Shares**: Organize files with fine-grained access permissions
49
- - Role-based permission system ensuring secure file management
50
- - 🤝 Collaboration
51
- - **Collabora Online & OnlyOffice Integration**: Real-time document editing and collaboration
52
- - **Activity Tracking**: Commenting, notifications, and file history for seamless teamwork
50
+ - Spaces & Shares with fine-grained permissions
51
+ - Role-based access control for secure collaboration
52
+ - 🤝 Collaborative Editing
53
+ - Collabora Online and OnlyOffice integration
54
+ - Multi-editor support with automatic editor selection
55
+ - Comments, notifications, and file activity tracking
53
56
  - 🔎 Powerful Full-Text Search
54
- - **Deep content search** for easy retrieval of files and documents
55
- - Supports various file formats for comprehensive indexing
56
- - 📂 Document Management & Restrictions
57
- - **Quota & Lock Management**: Control file storage and prevent unwanted modifications
58
- - **Secure Spaces**: Ensure documents are shared in a protected environment
57
+ - Deep document content indexing
58
+ - Multi-format search support
59
+ - 📂 Document Management & File Protection
60
+ - Storage quotas and file locking
61
+ - Secure and controlled sharing environments
62
+ - 🌍 Sovereign & Self-Hosted by Design
63
+ - Full control over infrastructure and data
64
+ - Deployable on-premise or private cloud
65
+ - Built on open standards and interoperable technologies
59
66
  - 🔗 WebDAV Access
60
- - Fully compatible with **WebDAV** for remote file access and synchronization
67
+ - Native WebDAV support for remote file access and synchronization
68
+ - Integration with file explorers and third-party tools
61
69
  - 🖥️ [Desktop Client](https://github.com/Sync-in/desktop)
62
- - Full-featured **desktop application** for enhanced productivity
63
- - Supports **file synchronization** across devices
64
- - Allows **connection to multiple servers** hosting the solution
70
+ - Full-featured desktop application for enhanced productivity
71
+ - Cross-device file synchronization
72
+ - Multi-server support
65
73
 
66
74
  ---
67
75
 
@@ -74,15 +82,16 @@ If you find it useful, you can:
74
82
  - 🐛 Report issues and suggest improvements
75
83
  - 🤝 Contribute code, translations, or documentation
76
84
  - 💬 Join the community on :
77
- - [GitHub Discussions](https://github.com/Sync-in/server/discussions)
78
- - [Discord](https://discord.gg/qhJyzwaymT)
85
+ - [GitHub Discussions](https://github.com/Sync-in/server/discussions)
86
+ - [Discord](https://discord.gg/qhJyzwaymT)
79
87
  - 💖 Support the project !
80
- - [GitHub Sponsors](https://github.com/sponsors/Sync-in)
81
- - [Other ways to support](https://sync-in.com/support)
88
+ - [GitHub Sponsors](https://github.com/sponsors/Sync-in)
89
+ - [Other ways to support](https://sync-in.com/support)
82
90
 
83
91
  ---
84
92
 
85
93
  ## 🤝 Contributing
94
+
86
95
  Before submitting your pull request, please confirm the following:
87
96
 
88
97
  - ✅ I have read and followed the [contribution guide](CONTRIBUTING.md).
@@ -91,6 +100,7 @@ Before submitting your pull request, please confirm the following:
91
100
  ---
92
101
 
93
102
  ## 📜 License
103
+
94
104
  This project is licensed under the **GNU Affero General Public License (AGPL-3.0-or-later)**.
95
105
  See [LICENSE](LICENSE) for the full text.
96
106
 
@@ -258,6 +258,10 @@ auth:
258
258
  # Common scopes: openid (required), email, profile, groups, roles
259
259
  # default: `openid email profile`
260
260
  scope: openid email profile
261
+ # supportPKCE: Enable PKCE (Proof Key for Code Exchange) in the authorization code flow.
262
+ # When true, PKCE is used if supported by the OIDC provider.
263
+ # default: true
264
+ supportPKCE: true
261
265
  # OAuth 2.0 / OIDC client authentication method used at the token endpoint.
262
266
  # Possible values:
263
267
  # - client_secret_basic (DEFAULT): HTTP Basic auth using client_id and client_secret.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sync-in/server",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "The secure, open-source platform for file storage, sharing, collaboration, and syncing",
5
5
  "author": {
6
6
  "name": "Johan Legrand",
@@ -80,47 +80,47 @@
80
80
  "@knaadh/nestjs-drizzle-mysql2": "1.2.0",
81
81
  "@lukeed/ms": "2.0.2",
82
82
  "@nestjs/axios": "4.0.1",
83
- "@nestjs/common": "11.1.13",
83
+ "@nestjs/common": "11.1.16",
84
84
  "@nestjs/config": "4.0.3",
85
- "@nestjs/core": "11.1.13",
85
+ "@nestjs/core": "11.1.16",
86
86
  "@nestjs/jwt": "11.0.2",
87
87
  "@nestjs/passport": "11.0.5",
88
- "@nestjs/platform-fastify": "11.1.13",
89
- "@nestjs/platform-socket.io": "11.1.13",
88
+ "@nestjs/platform-fastify": "11.1.16",
89
+ "@nestjs/platform-socket.io": "11.1.16",
90
90
  "@nestjs/schedule": "6.1.1",
91
- "@nestjs/websockets": "11.1.13",
91
+ "@nestjs/websockets": "11.1.16",
92
92
  "@socket.io/cluster-adapter": "0.3.0",
93
93
  "@socket.io/redis-adapter": "8.3.0",
94
94
  "archiver": "7.0.1",
95
95
  "bcryptjs": "3.0.3",
96
96
  "class-transformer": "0.5.1",
97
- "class-validator": "0.14.3",
97
+ "class-validator": "0.14.4",
98
98
  "deepmerge": "4.3.1",
99
99
  "drizzle-kit": "0.31.9",
100
100
  "drizzle-orm": "0.45.1",
101
- "fast-xml-parser": "5.3.5",
102
- "fs-extra": "11.3.3",
101
+ "fast-xml-parser": "5.5.5",
102
+ "fs-extra": "11.3.4",
103
103
  "html-to-text": "9.0.5",
104
104
  "js-yaml": "4.1.1",
105
- "ldapts": "8.1.6",
105
+ "ldapts": "8.1.7",
106
106
  "mime-types": "3.0.2",
107
- "mysql2": "3.16.3",
108
- "nestjs-pino": "4.5.0",
109
- "nodemailer": "8.0.1",
107
+ "mysql2": "3.19.1",
108
+ "nestjs-pino": "4.6.0",
109
+ "nodemailer": "8.0.2",
110
110
  "openid-client": "6.8.2",
111
111
  "passport-jwt": "4.0.1",
112
112
  "passport-local": "1.0.0",
113
113
  "passport": "0.7.0",
114
114
  "pino-pretty": "13.1.3",
115
115
  "qrcode-generator": "2.0.4",
116
- "redis": "5.10.0",
117
- "sax": "1.4.4",
116
+ "redis": "5.11.0",
117
+ "sax": "1.5.0",
118
118
  "sharp": "0.34.5",
119
119
  "socket.io": "4.8.3",
120
- "tar": "7.5.7",
120
+ "tar": "7.5.11",
121
121
  "text-to-svg": "3.1.5",
122
122
  "time2fa": "1.4.2",
123
123
  "unpdf": "1.4.0",
124
- "yauzl": "3.2.0"
124
+ "yauzl": "3.2.1"
125
125
  }
126
126
  }
@@ -22,7 +22,7 @@ async function parsePdf(filePath, options) {
22
22
  const buffer = await (0, _promises.readFile)(filePath);
23
23
  try {
24
24
  // Load the document, allowing system fonts as fallback
25
- const doc = await (0, _unpdf.getDocumentProxy)(new Uint8Array(buffer), {
25
+ doc = await (0, _unpdf.getDocumentProxy)(new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength), {
26
26
  disableFontFace: true,
27
27
  verbosity: 0
28
28
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../backend/src/applications/files/utils/doc-textify/adapters/pdf.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises'\nimport { getDocumentProxy } from 'unpdf'\nimport type { PDFDocumentProxy } from 'unpdf/pdfjs'\nimport type { DocTextifyOptions } from '../interfaces/doc-textify.interfaces'\n\n// Type guard to filter true text items\ninterface TextItem {\n str: string\n transform: [number, number, number, number, number, number]\n}\nfunction isTextItem(item: any): item is TextItem {\n return typeof item.str === 'string' && Array.isArray(item.transform)\n}\n\nconst ignorePdfBadFormat = new Set([0x0000, 0x0001])\n\n/** Parse PDF files */\nexport async function parsePdf(filePath: string, options: DocTextifyOptions): Promise<string> {\n let doc: PDFDocumentProxy\n const buffer = await readFile(filePath)\n\n try {\n // Load the document, allowing system fonts as fallback\n const doc = await getDocumentProxy(new Uint8Array(buffer), {\n disableFontFace: true,\n verbosity: 0\n })\n const fragments: string[] = []\n let lastY: number | undefined = undefined\n\n for (let pageNum = 1; pageNum <= doc.numPages; pageNum++) {\n const page = await doc.getPage(pageNum)\n const { items } = await page.getTextContent()\n\n for (const item of items) {\n // Skip non-text items\n if (!isTextItem(item)) continue\n\n const currentY = item.transform[5]\n if (lastY !== undefined && currentY !== lastY) {\n fragments.push(options.newlineDelimiter)\n }\n\n fragments.push(item.str)\n lastY = currentY\n }\n page.cleanup()\n }\n\n const content = fragments.join('')\n if (ignorePdfBadFormat.has(content.charCodeAt(0))) {\n return ''\n }\n return content\n } catch (e) {\n if (options.outputErrorToConsole) {\n console.error('Error parsing PDF:', e)\n }\n throw e\n } finally {\n doc?.destroy().catch((e: Error) => console.error(e))\n }\n}\n"],"names":["parsePdf","isTextItem","item","str","Array","isArray","transform","ignorePdfBadFormat","Set","filePath","options","doc","buffer","readFile","getDocumentProxy","Uint8Array","disableFontFace","verbosity","fragments","lastY","undefined","pageNum","numPages","page","getPage","items","getTextContent","currentY","push","newlineDelimiter","cleanup","content","join","has","charCodeAt","e","outputErrorToConsole","console","error","destroy","catch"],"mappings":";;;;+BAiBsBA;;;eAAAA;;;0BAjBG;uBACQ;AASjC,SAASC,WAAWC,IAAS;IAC3B,OAAO,OAAOA,KAAKC,GAAG,KAAK,YAAYC,MAAMC,OAAO,CAACH,KAAKI,SAAS;AACrE;AAEA,MAAMC,qBAAqB,IAAIC,IAAI;IAAC;IAAQ;CAAO;AAG5C,eAAeR,SAASS,QAAgB,EAAEC,OAA0B;IACzE,IAAIC;IACJ,MAAMC,SAAS,MAAMC,IAAAA,kBAAQ,EAACJ;IAE9B,IAAI;QACF,uDAAuD;QACvD,MAAME,MAAM,MAAMG,IAAAA,uBAAgB,EAAC,IAAIC,WAAWH,SAAS;YACzDI,iBAAiB;YACjBC,WAAW;QACb;QACA,MAAMC,YAAsB,EAAE;QAC9B,IAAIC,QAA4BC;QAEhC,IAAK,IAAIC,UAAU,GAAGA,WAAWV,IAAIW,QAAQ,EAAED,UAAW;YACxD,MAAME,OAAO,MAAMZ,IAAIa,OAAO,CAACH;YAC/B,MAAM,EAAEI,KAAK,EAAE,GAAG,MAAMF,KAAKG,cAAc;YAE3C,KAAK,MAAMxB,QAAQuB,MAAO;gBACxB,sBAAsB;gBACtB,IAAI,CAACxB,WAAWC,OAAO;gBAEvB,MAAMyB,WAAWzB,KAAKI,SAAS,CAAC,EAAE;gBAClC,IAAIa,UAAUC,aAAaO,aAAaR,OAAO;oBAC7CD,UAAUU,IAAI,CAAClB,QAAQmB,gBAAgB;gBACzC;gBAEAX,UAAUU,IAAI,CAAC1B,KAAKC,GAAG;gBACvBgB,QAAQQ;YACV;YACAJ,KAAKO,OAAO;QACd;QAEA,MAAMC,UAAUb,UAAUc,IAAI,CAAC;QAC/B,IAAIzB,mBAAmB0B,GAAG,CAACF,QAAQG,UAAU,CAAC,KAAK;YACjD,OAAO;QACT;QACA,OAAOH;IACT,EAAE,OAAOI,GAAG;QACV,IAAIzB,QAAQ0B,oBAAoB,EAAE;YAChCC,QAAQC,KAAK,CAAC,sBAAsBH;QACtC;QACA,MAAMA;IACR,SAAU;QACRxB,KAAK4B,UAAUC,MAAM,CAACL,IAAaE,QAAQC,KAAK,CAACH;IACnD;AACF"}
1
+ {"version":3,"sources":["../../../../../../../backend/src/applications/files/utils/doc-textify/adapters/pdf.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises'\nimport { getDocumentProxy } from 'unpdf'\nimport type { PDFDocumentProxy } from 'unpdf/pdfjs'\nimport type { DocTextifyOptions } from '../interfaces/doc-textify.interfaces'\n\n// Type guard to filter true text items\ninterface TextItem {\n str: string\n transform: [number, number, number, number, number, number]\n}\nfunction isTextItem(item: any): item is TextItem {\n return typeof item.str === 'string' && Array.isArray(item.transform)\n}\n\nconst ignorePdfBadFormat = new Set([0x0000, 0x0001])\n\n/** Parse PDF files */\nexport async function parsePdf(filePath: string, options: DocTextifyOptions): Promise<string> {\n let doc: PDFDocumentProxy | undefined\n const buffer = await readFile(filePath)\n\n try {\n // Load the document, allowing system fonts as fallback\n doc = await getDocumentProxy(new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength), {\n disableFontFace: true,\n verbosity: 0\n })\n const fragments: string[] = []\n let lastY: number | undefined = undefined\n\n for (let pageNum = 1; pageNum <= doc.numPages; pageNum++) {\n const page = await doc.getPage(pageNum)\n const { items } = await page.getTextContent()\n\n for (const item of items) {\n // Skip non-text items\n if (!isTextItem(item)) continue\n\n const currentY = item.transform[5]\n if (lastY !== undefined && currentY !== lastY) {\n fragments.push(options.newlineDelimiter)\n }\n\n fragments.push(item.str)\n lastY = currentY\n }\n page.cleanup()\n }\n\n const content = fragments.join('')\n if (ignorePdfBadFormat.has(content.charCodeAt(0))) {\n return ''\n }\n return content\n } catch (e) {\n if (options.outputErrorToConsole) {\n console.error('Error parsing PDF:', e)\n }\n throw e\n } finally {\n doc?.destroy().catch((e: Error) => console.error(e))\n }\n}\n"],"names":["parsePdf","isTextItem","item","str","Array","isArray","transform","ignorePdfBadFormat","Set","filePath","options","doc","buffer","readFile","getDocumentProxy","Uint8Array","byteOffset","byteLength","disableFontFace","verbosity","fragments","lastY","undefined","pageNum","numPages","page","getPage","items","getTextContent","currentY","push","newlineDelimiter","cleanup","content","join","has","charCodeAt","e","outputErrorToConsole","console","error","destroy","catch"],"mappings":";;;;+BAiBsBA;;;eAAAA;;;0BAjBG;uBACQ;AASjC,SAASC,WAAWC,IAAS;IAC3B,OAAO,OAAOA,KAAKC,GAAG,KAAK,YAAYC,MAAMC,OAAO,CAACH,KAAKI,SAAS;AACrE;AAEA,MAAMC,qBAAqB,IAAIC,IAAI;IAAC;IAAQ;CAAO;AAG5C,eAAeR,SAASS,QAAgB,EAAEC,OAA0B;IACzE,IAAIC;IACJ,MAAMC,SAAS,MAAMC,IAAAA,kBAAQ,EAACJ;IAE9B,IAAI;QACF,uDAAuD;QACvDE,MAAM,MAAMG,IAAAA,uBAAgB,EAAC,IAAIC,WAAWH,OAAOA,MAAM,EAAEA,OAAOI,UAAU,EAAEJ,OAAOK,UAAU,GAAG;YAChGC,iBAAiB;YACjBC,WAAW;QACb;QACA,MAAMC,YAAsB,EAAE;QAC9B,IAAIC,QAA4BC;QAEhC,IAAK,IAAIC,UAAU,GAAGA,WAAWZ,IAAIa,QAAQ,EAAED,UAAW;YACxD,MAAME,OAAO,MAAMd,IAAIe,OAAO,CAACH;YAC/B,MAAM,EAAEI,KAAK,EAAE,GAAG,MAAMF,KAAKG,cAAc;YAE3C,KAAK,MAAM1B,QAAQyB,MAAO;gBACxB,sBAAsB;gBACtB,IAAI,CAAC1B,WAAWC,OAAO;gBAEvB,MAAM2B,WAAW3B,KAAKI,SAAS,CAAC,EAAE;gBAClC,IAAIe,UAAUC,aAAaO,aAAaR,OAAO;oBAC7CD,UAAUU,IAAI,CAACpB,QAAQqB,gBAAgB;gBACzC;gBAEAX,UAAUU,IAAI,CAAC5B,KAAKC,GAAG;gBACvBkB,QAAQQ;YACV;YACAJ,KAAKO,OAAO;QACd;QAEA,MAAMC,UAAUb,UAAUc,IAAI,CAAC;QAC/B,IAAI3B,mBAAmB4B,GAAG,CAACF,QAAQG,UAAU,CAAC,KAAK;YACjD,OAAO;QACT;QACA,OAAOH;IACT,EAAE,OAAOI,GAAG;QACV,IAAI3B,QAAQ4B,oBAAoB,EAAE;YAChCC,QAAQC,KAAK,CAAC,sBAAsBH;QACtC;QACA,MAAMA;IACR,SAAU;QACR1B,KAAK8B,UAAUC,MAAM,CAACL,IAAaE,QAAQC,KAAK,CAACH;IACnD;AACF"}
@@ -298,22 +298,36 @@ function copyFileContent(srcPath, dstPath) {
298
298
  });
299
299
  return writeFromStream(dstPath, srcStream);
300
300
  }
301
+ async function walkDir(rPath, onEntry, errors) {
302
+ let entries;
303
+ try {
304
+ entries = await _promises.default.readdir(rPath, {
305
+ withFileTypes: true
306
+ });
307
+ } catch (e) {
308
+ if (!errors) throw e;
309
+ errors[rPath] = e.message;
310
+ return;
311
+ }
312
+ for (const entry of entries){
313
+ const entryPath = _nodepath.default.join(rPath, entry.name);
314
+ await onEntry(entry, entryPath);
315
+ if (entry.isDirectory()) {
316
+ await walkDir(entryPath, onEntry, errors);
317
+ }
318
+ }
319
+ }
301
320
  async function dirSize(rPath) {
302
321
  let size = 0;
303
322
  const errors = {};
304
- for (const f of (await _promises.default.readdir(rPath, {
305
- withFileTypes: true,
306
- recursive: true
307
- }))){
308
- if (f.isFile()) {
309
- const p = _nodepath.default.join(f.parentPath, f.name);
310
- try {
311
- size += (await _promises.default.stat(p)).size;
312
- } catch (e) {
313
- errors[p] = e.message;
314
- }
323
+ await walkDir(rPath, async (entry, entryPath)=>{
324
+ if (!entry.isFile()) return;
325
+ try {
326
+ size += (await _promises.default.stat(entryPath)).size;
327
+ } catch (e) {
328
+ errors[entryPath] = e.message;
315
329
  }
316
- }
330
+ }, errors);
317
331
  return [
318
332
  size,
319
333
  errors
@@ -323,20 +337,19 @@ async function dirListFileNames(rPath) {
323
337
  return (await _promises.default.readdir(rPath)).map((path)=>fileName(path));
324
338
  }
325
339
  async function countDirEntries(rPath) {
326
- return (await _promises.default.readdir(rPath, {
327
- withFileTypes: true,
328
- recursive: true
329
- })).reduce((acc, f)=>{
330
- if (f.isDirectory()) {
331
- acc.directories++;
332
- } else {
333
- acc.files++;
334
- }
335
- return acc;
336
- }, {
340
+ const entriesCount = {
337
341
  files: 0,
338
342
  directories: 0
339
- });
343
+ };
344
+ const ignoredErrors = {};
345
+ await walkDir(rPath, (entry)=>{
346
+ if (entry.isDirectory()) {
347
+ entriesCount.directories++;
348
+ } else {
349
+ entriesCount.files++;
350
+ }
351
+ }, ignoredErrors);
352
+ return entriesCount;
340
353
  }
341
354
  async function dirHasChildren(rPath, mustContainsDirs = true) {
342
355
  for await (const file of (await _promises.default.opendir(rPath))){
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../backend/src/applications/files/utils/files.ts"],"sourcesContent":["import { 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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"}
1
+ {"version":3,"sources":["../../../../../backend/src/applications/files/utils/files.ts"],"sourcesContent":["import { 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\nasync function walkDir(\n rPath: string,\n onEntry: (entry: Dirent, entryPath: string) => Promise<void> | void,\n errors?: Record<string, string>\n): Promise<void> {\n let entries: Dirent[]\n\n try {\n entries = await fs.readdir(rPath, { withFileTypes: true })\n } catch (e: any) {\n if (!errors) throw e\n errors[rPath] = e.message\n return\n }\n\n for (const entry of entries) {\n const entryPath = path.join(rPath, entry.name)\n await onEntry(entry, entryPath)\n if (entry.isDirectory()) {\n await walkDir(entryPath, onEntry, errors)\n }\n }\n}\n\nexport async function dirSize(rPath: string): Promise<[number, any]> {\n let size = 0\n const errors: Record<string, string> = {}\n\n await walkDir(\n rPath,\n async (entry, entryPath) => {\n if (!entry.isFile()) return\n try {\n size += (await fs.stat(entryPath)).size\n } catch (e: any) {\n errors[entryPath] = e.message\n }\n },\n errors\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 const entriesCount = { files: 0, directories: 0 }\n const ignoredErrors: Record<string, string> = {}\n\n await walkDir(\n rPath,\n (entry: Dirent) => {\n if (entry.isDirectory()) {\n entriesCount.directories++\n } else {\n entriesCount.files++\n }\n },\n ignoredErrors\n )\n\n return entriesCount\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","walkDir","onEntry","errors","entries","readdir","withFileTypes","e","message","entry","entryPath","isFile","entriesCount","files","directories","ignoredErrors","mustContainsDirs","opendir","parentDir","extension","nameWithoutExtension","count","date","formatDateISOString","Date","NOT_FOUND","NOT_ACCEPTABLE"],"mappings":"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"}
@@ -23,6 +23,7 @@ const _hi = require("./hi");
23
23
  const _it = require("./it");
24
24
  const _ja = require("./ja");
25
25
  const _ko = require("./ko");
26
+ const _nl = require("./nl");
26
27
  const _pl = require("./pl");
27
28
  const _pt = require("./pt");
28
29
  const _pt_br = require("./pt_br");
@@ -58,6 +59,10 @@ const translations = new Map([
58
59
  'ko',
59
60
  _ko.ko
60
61
  ],
62
+ [
63
+ 'nl',
64
+ _nl.nl
65
+ ],
61
66
  [
62
67
  'pl',
63
68
  _pl.pl
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../backend/src/applications/notifications/i18n/index.ts"],"sourcesContent":["import { i18nLocale } from '../../../common/i18n'\nimport { de } from './de'\nimport { es } from './es'\nimport { fr } from './fr'\nimport { hi } from './hi'\nimport { it } from './it'\nimport { ja } from './ja'\nimport { ko } from './ko'\nimport { pl } from './pl'\nimport { pt } from './pt'\nimport { pt_BR } from './pt_br'\nimport { ru } from './ru'\nimport { tr } from './tr'\nimport { zh } from './zh'\n\nexport const translations = new Map<i18nLocale, Record<string, string>>([\n ['de', de],\n ['es', es],\n ['fr', fr],\n ['hi', hi],\n ['it', it],\n ['ja', ja],\n ['ko', ko],\n ['pl', pl],\n ['pt', pt],\n ['pt-BR', pt_BR],\n ['ru', ru],\n ['tr', tr],\n ['zh', zh]\n])\n\nexport function translateObject(language: i18nLocale, obj: Record<string, string>): Record<string, string> {\n if (!language || !translations.has(language)) return obj\n const tr: Record<string, string> = translations.get(language)\n if (!tr) return obj\n for (const key in obj) {\n const v = obj[key]\n const t = tr[v]\n if (t) obj[key] = t\n }\n return obj\n}\n"],"names":["translateObject","translations","Map","de","es","fr","hi","it","ja","ko","pl","pt","pt_BR","ru","tr","zh","language","obj","has","get","key","v","t"],"mappings":";;;;;;;;;;;QA+BgBA;eAAAA;;QAhBHC;eAAAA;;;oBAdM;oBACA;oBACA;oBACA;oBACA;oBACA;oBACA;oBACA;oBACA;uBACG;oBACH;oBACA;oBACA;AAEZ,MAAMA,eAAe,IAAIC,IAAwC;IACtE;QAAC;QAAMC,MAAE;KAAC;IACV;QAAC;QAAMC,MAAE;KAAC;IACV;QAAC;QAAMC,MAAE;KAAC;IACV;QAAC;QAAMC,MAAE;KAAC;IACV;QAAC;QAAMC,MAAE;KAAC;IACV;QAAC;QAAMC,MAAE;KAAC;IACV;QAAC;QAAMC,MAAE;KAAC;IACV;QAAC;QAAMC,MAAE;KAAC;IACV;QAAC;QAAMC,MAAE;KAAC;IACV;QAAC;QAASC,YAAK;KAAC;IAChB;QAAC;QAAMC,MAAE;KAAC;IACV;QAAC;QAAMC,MAAE;KAAC;IACV;QAAC;QAAMC,MAAE;KAAC;CACX;AAEM,SAASf,gBAAgBgB,QAAoB,EAAEC,GAA2B;IAC/E,IAAI,CAACD,YAAY,CAACf,aAAaiB,GAAG,CAACF,WAAW,OAAOC;IACrD,MAAMH,KAA6Bb,aAAakB,GAAG,CAACH;IACpD,IAAI,CAACF,IAAI,OAAOG;IAChB,IAAK,MAAMG,OAAOH,IAAK;QACrB,MAAMI,IAAIJ,GAAG,CAACG,IAAI;QAClB,MAAME,IAAIR,EAAE,CAACO,EAAE;QACf,IAAIC,GAAGL,GAAG,CAACG,IAAI,GAAGE;IACpB;IACA,OAAOL;AACT"}
1
+ {"version":3,"sources":["../../../../../backend/src/applications/notifications/i18n/index.ts"],"sourcesContent":["import { i18nLocale } from '../../../common/i18n'\nimport { de } from './de'\nimport { es } from './es'\nimport { fr } from './fr'\nimport { hi } from './hi'\nimport { it } from './it'\nimport { ja } from './ja'\nimport { ko } from './ko'\nimport { nl } from './nl'\nimport { pl } from './pl'\nimport { pt } from './pt'\nimport { pt_BR } from './pt_br'\nimport { ru } from './ru'\nimport { tr } from './tr'\nimport { zh } from './zh'\n\nexport const translations = new Map<i18nLocale, Record<string, string>>([\n ['de', de],\n ['es', es],\n ['fr', fr],\n ['hi', hi],\n ['it', it],\n ['ja', ja],\n ['ko', ko],\n ['nl', nl],\n ['pl', pl],\n ['pt', pt],\n ['pt-BR', pt_BR],\n ['ru', ru],\n ['tr', tr],\n ['zh', zh]\n])\n\nexport function translateObject(language: i18nLocale, obj: Record<string, string>): Record<string, string> {\n if (!language || !translations.has(language)) return obj\n const tr: Record<string, string> = translations.get(language)\n if (!tr) return obj\n for (const key in obj) {\n const v = obj[key]\n const t = tr[v]\n if (t) obj[key] = t\n }\n return obj\n}\n"],"names":["translateObject","translations","Map","de","es","fr","hi","it","ja","ko","nl","pl","pt","pt_BR","ru","tr","zh","language","obj","has","get","key","v","t"],"mappings":";;;;;;;;;;;QAiCgBA;eAAAA;;QAjBHC;eAAAA;;;oBAfM;oBACA;oBACA;oBACA;oBACA;oBACA;oBACA;oBACA;oBACA;oBACA;uBACG;oBACH;oBACA;oBACA;AAEZ,MAAMA,eAAe,IAAIC,IAAwC;IACtE;QAAC;QAAMC,MAAE;KAAC;IACV;QAAC;QAAMC,MAAE;KAAC;IACV;QAAC;QAAMC,MAAE;KAAC;IACV;QAAC;QAAMC,MAAE;KAAC;IACV;QAAC;QAAMC,MAAE;KAAC;IACV;QAAC;QAAMC,MAAE;KAAC;IACV;QAAC;QAAMC,MAAE;KAAC;IACV;QAAC;QAAMC,MAAE;KAAC;IACV;QAAC;QAAMC,MAAE;KAAC;IACV;QAAC;QAAMC,MAAE;KAAC;IACV;QAAC;QAASC,YAAK;KAAC;IAChB;QAAC;QAAMC,MAAE;KAAC;IACV;QAAC;QAAMC,MAAE;KAAC;IACV;QAAC;QAAMC,MAAE;KAAC;CACX;AAEM,SAAShB,gBAAgBiB,QAAoB,EAAEC,GAA2B;IAC/E,IAAI,CAACD,YAAY,CAAChB,aAAakB,GAAG,CAACF,WAAW,OAAOC;IACrD,MAAMH,KAA6Bd,aAAamB,GAAG,CAACH;IACpD,IAAI,CAACF,IAAI,OAAOG;IAChB,IAAK,MAAMG,OAAOH,IAAK;QACrB,MAAMI,IAAIJ,GAAG,CAACG,IAAI;QAClB,MAAME,IAAIR,EAAE,CAACO,EAAE;QACf,IAAIC,GAAGL,GAAG,CAACG,IAAI,GAAGE;IACpB;IACA,OAAOL;AACT"}
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "nl", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return nl;
9
+ }
10
+ });
11
+ const nl = {
12
+ 'If you no longer wish to receive notifications, change your preferences directly from your user space.': 'Als u geen meldingen meer wilt ontvangen, wijzig dan uw voorkeuren rechtstreeks vanuit uw gebruikersruimte.',
13
+ 'Access it from': 'Open dit via',
14
+ Comment: 'Opmerking',
15
+ commented: 'heeft een opmerking geplaatst',
16
+ 'You receive this notification if you are the owner of the file or if you have also commented on this file': 'U ontvangt deze melding omdat u de eigenaar van het bestand bent of omdat u bij dit bestand een opmerking heeft geplaatst',
17
+ Space: 'Ruimte',
18
+ 'from the space': 'vanuit de ruimte',
19
+ 'to the space': 'naar de ruimte',
20
+ 'Access your spaces from': 'Open uw ruimtes via',
21
+ 'Access this space from': 'Open deze ruimte via',
22
+ 'You now have access to the space': 'U heeft nu toegang tot de ruimte',
23
+ 'You no longer have access to the space': 'U heeft geen toegang meer tot de ruimte',
24
+ 'This space has been permanently deleted': 'Deze ruimte is definitief verwijderd',
25
+ anchored: 'heeft gekoppeld',
26
+ unanchored: 'heeft ontkoppeld',
27
+ Share: 'Share',
28
+ 'shared with you': 'heeft met u gedeeld',
29
+ 'no longer share with you': 'deelt niet langer met u',
30
+ 'You now have access to the share': 'U hebt nu toegang tot de share',
31
+ 'You no longer have access to the share': 'U hebt geen toegang meer tot de share',
32
+ 'You are no longer a member of the parent share, your child share has been deleted': 'U bent geen lid meer van de bovenliggende share; uw onderliggende share is verwijderd',
33
+ 'Access your shares from': 'Open uw shares via',
34
+ 'Access password': 'Toegangswachtwoord',
35
+ Sync: 'Synchronisatie',
36
+ 'Access your syncs from': 'Open uw synchronisaties via',
37
+ 'You are no longer synchronizing': 'U synchroniseert niet meer',
38
+ 'Unlock Request': 'Verzoek tot ontgrendeling',
39
+ 'You receive this notification because you have a lock on this file.': 'U ontvangt deze melding omdat u dit bestand hebt vergrendeld.',
40
+ 'sends you a request to unlock the file': 'stuurt u een verzoek om het bestand te ontgrendelen',
41
+ 'Security notification': 'Beveiligingsmelding',
42
+ 'Your account has been locked after several unsuccessful authentication attempts': 'Uw account is vergrendeld na meerdere mislukte pogingen tot authenticatie',
43
+ 'This security notification concerns your Sync-in account. Please contact an administrator to perform the analysis and unlock your account.': 'Deze beveiligingsmelding betreft uw Sync-in-account. Neem contact op met een beheerder om dit te laten onderzoeken en uw account te ontgrendelen.',
44
+ 'Two-factor authentication (2FA) on your account has been disabled': 'Twee-factor-authenticatie (2FA) op uw account is uitgeschakeld',
45
+ 'Two-factor authentication (2FA) on your account has been enabled': 'Twee-factor-authenticatie (2FA) op uw account is ingeschakeld',
46
+ 'You received this notification because the security of your Sync-in account has changed. If you think this was a mistake, please review your security settings or contact your administrator.': 'U heeft deze melding ontvangen omdat de beveiliging van uw Sync-in-account is gewijzigd. Als u denkt dat dit een vergissing is, controleer dan uw beveiligingsinstellingen of neem contact op met uw beheerder.',
47
+ 'Address IP': 'IP-adres',
48
+ Browser: 'Browser',
49
+ 'New Version Available': 'Nieuwe versie beschikbaar',
50
+ 'You receive this notification because you are the administrator of this server.': 'U ontvangt deze melding omdat u de beheerder bent van deze server.',
51
+ 'A new update is available': 'Er is een update beschikbaar'
52
+ };
53
+
54
+ //# sourceMappingURL=nl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../backend/src/applications/notifications/i18n/nl.ts"],"sourcesContent":["export const nl = {\n 'If you no longer wish to receive notifications, change your preferences directly from your user space.':\n 'Als u geen meldingen meer wilt ontvangen, wijzig dan uw voorkeuren rechtstreeks vanuit uw gebruikersruimte.',\n 'Access it from': 'Open dit via',\n Comment: 'Opmerking',\n commented: 'heeft een opmerking geplaatst',\n 'You receive this notification if you are the owner of the file or if you have also commented on this file':\n 'U ontvangt deze melding omdat u de eigenaar van het bestand bent of omdat u bij dit bestand een opmerking heeft geplaatst',\n Space: 'Ruimte',\n 'from the space': 'vanuit de ruimte',\n 'to the space': 'naar de ruimte',\n 'Access your spaces from': 'Open uw ruimtes via',\n 'Access this space from': 'Open deze ruimte via',\n 'You now have access to the space': 'U heeft nu toegang tot de ruimte',\n 'You no longer have access to the space': 'U heeft geen toegang meer tot de ruimte',\n 'This space has been permanently deleted': 'Deze ruimte is definitief verwijderd',\n anchored: 'heeft gekoppeld',\n unanchored: 'heeft ontkoppeld',\n Share: 'Share',\n 'shared with you': 'heeft met u gedeeld',\n 'no longer share with you': 'deelt niet langer met u',\n 'You now have access to the share': 'U hebt nu toegang tot de share',\n 'You no longer have access to the share': 'U hebt geen toegang meer tot de share',\n 'You are no longer a member of the parent share, your child share has been deleted':\n 'U bent geen lid meer van de bovenliggende share; uw onderliggende share is verwijderd',\n 'Access your shares from': 'Open uw shares via',\n 'Access password': 'Toegangswachtwoord',\n Sync: 'Synchronisatie',\n 'Access your syncs from': 'Open uw synchronisaties via',\n 'You are no longer synchronizing': 'U synchroniseert niet meer',\n 'Unlock Request': 'Verzoek tot ontgrendeling',\n 'You receive this notification because you have a lock on this file.': 'U ontvangt deze melding omdat u dit bestand hebt vergrendeld.',\n 'sends you a request to unlock the file': 'stuurt u een verzoek om het bestand te ontgrendelen',\n 'Security notification': 'Beveiligingsmelding',\n 'Your account has been locked after several unsuccessful authentication attempts':\n 'Uw account is vergrendeld na meerdere mislukte pogingen tot authenticatie',\n 'This security notification concerns your Sync-in account. Please contact an administrator to perform the analysis and unlock your account.':\n 'Deze beveiligingsmelding betreft uw Sync-in-account. Neem contact op met een beheerder om dit te laten onderzoeken en uw account te ontgrendelen.',\n 'Two-factor authentication (2FA) on your account has been disabled': 'Twee-factor-authenticatie (2FA) op uw account is uitgeschakeld',\n 'Two-factor authentication (2FA) on your account has been enabled': 'Twee-factor-authenticatie (2FA) op uw account is ingeschakeld',\n 'You received this notification because the security of your Sync-in account has changed. If you think this was a mistake, please review your security settings or contact your administrator.':\n 'U heeft deze melding ontvangen omdat de beveiliging van uw Sync-in-account is gewijzigd. Als u denkt dat dit een vergissing is, controleer dan uw beveiligingsinstellingen of neem contact op met uw beheerder.',\n 'Address IP': 'IP-adres',\n Browser: 'Browser',\n 'New Version Available': 'Nieuwe versie beschikbaar',\n 'You receive this notification because you are the administrator of this server.':\n 'U ontvangt deze melding omdat u de beheerder bent van deze server.',\n 'A new update is available': 'Er is een update beschikbaar'\n} as const\n"],"names":["nl","Comment","commented","Space","anchored","unanchored","Share","Sync","Browser"],"mappings":";;;;+BAAaA;;;eAAAA;;;AAAN,MAAMA,KAAK;IAChB,0GACE;IACF,kBAAkB;IAClBC,SAAS;IACTC,WAAW;IACX,6GACE;IACFC,OAAO;IACP,kBAAkB;IAClB,gBAAgB;IAChB,2BAA2B;IAC3B,0BAA0B;IAC1B,oCAAoC;IACpC,0CAA0C;IAC1C,2CAA2C;IAC3CC,UAAU;IACVC,YAAY;IACZC,OAAO;IACP,mBAAmB;IACnB,4BAA4B;IAC5B,oCAAoC;IACpC,0CAA0C;IAC1C,qFACE;IACF,2BAA2B;IAC3B,mBAAmB;IACnBC,MAAM;IACN,0BAA0B;IAC1B,mCAAmC;IACnC,kBAAkB;IAClB,uEAAuE;IACvE,0CAA0C;IAC1C,yBAAyB;IACzB,mFACE;IACF,8IACE;IACF,qEAAqE;IACrE,oEAAoE;IACpE,iMACE;IACF,cAAc;IACdC,SAAS;IACT,yBAAyB;IACzB,mFACE;IACF,6BAA6B;AAC/B"}
@@ -59,7 +59,7 @@ let SyncClientsManager = class SyncClientsManager {
59
59
  tag: this.register.name,
60
60
  msg: `user *${user.login}* (${user.id}) does not have permission : ${_user.USER_PERMISSION.DESKTOP_APP}`
61
61
  });
62
- throw new _common.HttpException('Missing permission', _common.HttpStatus.FORBIDDEN);
62
+ throw new _common.HttpException('Desktop app permission required', _common.HttpStatus.FORBIDDEN);
63
63
  }
64
64
  if (_configenvironment.configuration.auth.mfa.totp.enabled && user.twoFaEnabled) {
65
65
  // Checking TOTP code and recovery code