@sync-in/server 1.5.2 → 1.6.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 (228) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +1 -0
  3. package/environment/environment.dist.min.yaml +1 -0
  4. package/environment/environment.dist.yaml +88 -30
  5. package/migrations/0002_sleepy_korath.sql +1 -0
  6. package/migrations/meta/0002_snapshot.json +2424 -0
  7. package/migrations/meta/_journal.json +7 -0
  8. package/package.json +6 -4
  9. package/server/app.bootstrap.js +1 -1
  10. package/server/app.bootstrap.js.map +1 -1
  11. package/server/applications/files/services/files-manager.service.js +1 -2
  12. package/server/applications/files/services/files-manager.service.js.map +1 -1
  13. package/server/applications/files/services/files-only-office-manager.service.js +5 -6
  14. package/server/applications/files/services/files-only-office-manager.service.js.map +1 -1
  15. package/server/applications/files/utils/files.js +6 -4
  16. package/server/applications/files/utils/files.js.map +1 -1
  17. package/server/applications/links/links.controller.js +2 -2
  18. package/server/applications/links/links.controller.js.map +1 -1
  19. package/server/applications/links/services/links-manager.service.js +2 -1
  20. package/server/applications/links/services/links-manager.service.js.map +1 -1
  21. package/server/applications/links/services/links-manager.service.spec.js +6 -3
  22. package/server/applications/links/services/links-manager.service.spec.js.map +1 -1
  23. package/server/applications/notifications/constants/notifications.js +9 -0
  24. package/server/applications/notifications/constants/notifications.js.map +1 -1
  25. package/server/applications/notifications/i18n/fr.js +10 -1
  26. package/server/applications/notifications/i18n/fr.js.map +1 -1
  27. package/server/applications/notifications/interfaces/notification-properties.interface.js.map +1 -1
  28. package/server/applications/notifications/mails/models.js +41 -3
  29. package/server/applications/notifications/mails/models.js.map +1 -1
  30. package/server/applications/notifications/mails/templates.js +1 -1
  31. package/server/applications/notifications/mails/templates.js.map +1 -1
  32. package/server/applications/notifications/schemas/notifications.schema.js +2 -1
  33. package/server/applications/notifications/schemas/notifications.schema.js.map +1 -1
  34. package/server/applications/notifications/services/notifications-manager.service.js +16 -13
  35. package/server/applications/notifications/services/notifications-manager.service.js.map +1 -1
  36. package/server/applications/notifications/services/notifications-manager.service.spec.js +9 -8
  37. package/server/applications/notifications/services/notifications-manager.service.spec.js.map +1 -1
  38. package/server/applications/notifications/services/notifications-queries.service.js +1 -1
  39. package/server/applications/notifications/services/notifications-queries.service.js.map +1 -1
  40. package/server/applications/shares/services/shares-manager.service.js +3 -2
  41. package/server/applications/shares/services/shares-manager.service.js.map +1 -1
  42. package/server/applications/sync/constants/auth.js +2 -2
  43. package/server/applications/sync/constants/auth.js.map +1 -1
  44. package/server/applications/sync/dtos/sync-client-registration.dto.js +5 -0
  45. package/server/applications/sync/dtos/sync-client-registration.dto.js.map +1 -1
  46. package/server/applications/sync/dtos/sync-operations.dto.js +1 -2
  47. package/server/applications/sync/dtos/sync-operations.dto.js.map +1 -1
  48. package/server/applications/sync/schemas/sync-clients.schema.js +2 -1
  49. package/server/applications/sync/schemas/sync-clients.schema.js.map +1 -1
  50. package/server/applications/sync/schemas/sync-paths.schema.js +2 -1
  51. package/server/applications/sync/schemas/sync-paths.schema.js.map +1 -1
  52. package/server/applications/sync/services/sync-clients-manager.service.js +28 -20
  53. package/server/applications/sync/services/sync-clients-manager.service.js.map +1 -1
  54. package/server/applications/sync/services/sync-clients-manager.service.spec.js +24 -18
  55. package/server/applications/sync/services/sync-clients-manager.service.spec.js.map +1 -1
  56. package/server/applications/sync/services/sync-queries.service.js +5 -5
  57. package/server/applications/sync/services/sync-queries.service.js.map +1 -1
  58. package/server/applications/users/admin-users.controller.js +48 -37
  59. package/server/applications/users/admin-users.controller.js.map +1 -1
  60. package/server/applications/users/admin-users.controller.spec.js +15 -0
  61. package/server/applications/users/admin-users.controller.spec.js.map +1 -1
  62. package/server/applications/users/constants/routes.js +5 -0
  63. package/server/applications/users/constants/routes.js.map +1 -1
  64. package/server/applications/users/constants/user.js +8 -0
  65. package/server/applications/users/constants/user.js.map +1 -1
  66. package/server/applications/users/dto/delete-user.dto.js +5 -23
  67. package/server/applications/users/dto/delete-user.dto.js.map +1 -1
  68. package/server/applications/users/dto/user-properties.dto.js +38 -3
  69. package/server/applications/users/dto/user-properties.dto.js.map +1 -1
  70. package/server/applications/users/interfaces/admin-user.interface.js.map +1 -1
  71. package/server/applications/users/interfaces/user-secrets.interface.js +10 -0
  72. package/server/applications/users/interfaces/user-secrets.interface.js.map +1 -0
  73. package/server/applications/users/models/user.model.js +84 -50
  74. package/server/applications/users/models/user.model.js.map +1 -1
  75. package/server/applications/users/schemas/user.interface.js.map +1 -1
  76. package/server/applications/users/schemas/users.schema.js +2 -0
  77. package/server/applications/users/schemas/users.schema.js.map +1 -1
  78. package/server/applications/users/services/admin-users-manager.service.js +7 -19
  79. package/server/applications/users/services/admin-users-manager.service.js.map +1 -1
  80. package/server/applications/users/services/admin-users-manager.service.spec.js +7 -26
  81. package/server/applications/users/services/admin-users-manager.service.spec.js.map +1 -1
  82. package/server/applications/users/services/admin-users-queries.service.js +1 -0
  83. package/server/applications/users/services/admin-users-queries.service.js.map +1 -1
  84. package/server/applications/users/services/users-manager.service.js +138 -28
  85. package/server/applications/users/services/users-manager.service.js.map +1 -1
  86. package/server/applications/users/services/users-manager.service.spec.js +11 -9
  87. package/server/applications/users/services/users-manager.service.spec.js.map +1 -1
  88. package/server/applications/users/services/users-queries.service.js +63 -57
  89. package/server/applications/users/services/users-queries.service.js.map +1 -1
  90. package/server/applications/users/users.controller.js +48 -1
  91. package/server/applications/users/users.controller.js.map +1 -1
  92. package/server/applications/users/users.controller.spec.js +8 -1
  93. package/server/applications/users/users.controller.spec.js.map +1 -1
  94. package/server/applications/users/users.e2e-spec.js +2 -1
  95. package/server/applications/users/users.e2e-spec.js.map +1 -1
  96. package/server/applications/users/utils/avatar.js +48 -0
  97. package/server/applications/users/utils/avatar.js.map +1 -0
  98. package/server/authentication/auth.config.js +85 -26
  99. package/server/authentication/auth.config.js.map +1 -1
  100. package/server/authentication/auth.controller.js +117 -9
  101. package/server/authentication/auth.controller.js.map +1 -1
  102. package/server/authentication/auth.controller.spec.js +16 -1
  103. package/server/authentication/auth.controller.spec.js.map +1 -1
  104. package/server/authentication/auth.e2e-spec.js +4 -3
  105. package/server/authentication/auth.e2e-spec.js.map +1 -1
  106. package/server/authentication/auth.module.js +4 -1
  107. package/server/authentication/auth.module.js.map +1 -1
  108. package/server/authentication/constants/auth.js +37 -4
  109. package/server/authentication/constants/auth.js.map +1 -1
  110. package/server/authentication/constants/routes.js +21 -0
  111. package/server/authentication/constants/routes.js.map +1 -1
  112. package/server/authentication/constants/scope.js +20 -0
  113. package/server/authentication/constants/scope.js.map +1 -0
  114. package/server/authentication/dto/login-response.dto.js +27 -4
  115. package/server/authentication/dto/login-response.dto.js.map +1 -1
  116. package/server/authentication/dto/token-response.dto.js +5 -0
  117. package/server/authentication/dto/token-response.dto.js.map +1 -1
  118. package/server/{applications/users/dto/user-password.dto.js → authentication/dto/two-fa-verify.dto.js} +27 -9
  119. package/server/authentication/dto/two-fa-verify.dto.js.map +1 -0
  120. package/server/authentication/guards/auth-basic.strategy.js +6 -5
  121. package/server/authentication/guards/auth-basic.strategy.js.map +1 -1
  122. package/server/authentication/guards/auth-token-access.strategy.js +3 -2
  123. package/server/authentication/guards/auth-token-access.strategy.js.map +1 -1
  124. package/server/authentication/guards/auth-token-refresh.strategy.js +3 -2
  125. package/server/authentication/guards/auth-token-refresh.strategy.js.map +1 -1
  126. package/server/authentication/guards/auth-two-fa-guard.js +81 -0
  127. package/server/authentication/guards/auth-two-fa-guard.js.map +1 -0
  128. package/server/authentication/interfaces/jwt-payload.interface.js +5 -0
  129. package/server/authentication/interfaces/jwt-payload.interface.js.map +1 -1
  130. package/server/authentication/interfaces/token.interface.js +2 -0
  131. package/server/authentication/interfaces/token.interface.js.map +1 -1
  132. package/server/authentication/interfaces/two-fa-setup.interface.js +10 -0
  133. package/server/authentication/interfaces/two-fa-setup.interface.js.map +1 -0
  134. package/server/authentication/models/auth-method.js.map +1 -1
  135. package/server/authentication/services/auth-manager.service.js +72 -49
  136. package/server/authentication/services/auth-manager.service.js.map +1 -1
  137. package/server/authentication/services/auth-methods/auth-method-database.service.js +3 -3
  138. package/server/authentication/services/auth-methods/auth-method-database.service.js.map +1 -1
  139. package/server/authentication/services/auth-methods/auth-method-database.service.spec.js +5 -0
  140. package/server/authentication/services/auth-methods/auth-method-database.service.spec.js.map +1 -1
  141. package/server/authentication/services/auth-methods/auth-method-ldap.service.js +100 -27
  142. package/server/authentication/services/auth-methods/auth-method-ldap.service.js.map +1 -1
  143. package/server/authentication/services/auth-methods/auth-method-ldap.service.spec.js +11 -12
  144. package/server/authentication/services/auth-methods/auth-method-ldap.service.spec.js.map +1 -1
  145. package/server/authentication/services/auth-methods/auth-method-two-fa.service.js +251 -0
  146. package/server/authentication/services/auth-methods/auth-method-two-fa.service.js.map +1 -0
  147. package/server/authentication/services/auth-methods/auth-method-two-fa.service.spec.js +41 -0
  148. package/server/authentication/services/auth-methods/auth-method-two-fa.service.spec.js.map +1 -0
  149. package/server/authentication/utils/crypt-secret.js +68 -0
  150. package/server/authentication/utils/crypt-secret.js.map +1 -0
  151. package/server/common/functions.js +18 -2
  152. package/server/common/functions.js.map +1 -1
  153. package/server/common/qrcode.js +34 -0
  154. package/server/common/qrcode.js.map +1 -0
  155. package/server/common/shared.js +18 -0
  156. package/server/common/shared.js.map +1 -1
  157. package/server/configuration/config.environment.js +23 -6
  158. package/server/configuration/config.environment.js.map +1 -1
  159. package/server/configuration/config.interfaces.js +10 -0
  160. package/server/configuration/config.interfaces.js.map +1 -0
  161. package/server/configuration/config.loader.js.map +1 -1
  162. package/server/configuration/config.validation.js +13 -13
  163. package/server/configuration/config.validation.js.map +1 -1
  164. package/server/infrastructure/cache/adapters/mysql-cache.adapter.js +6 -6
  165. package/server/infrastructure/cache/adapters/mysql-cache.adapter.js.map +1 -1
  166. package/server/infrastructure/cache/schemas/mysql-cache.schema.js +2 -1
  167. package/server/infrastructure/cache/schemas/mysql-cache.schema.js.map +1 -1
  168. package/server/infrastructure/cache/services/cache.service.js.map +1 -1
  169. package/server/infrastructure/database/columns.js +39 -0
  170. package/server/infrastructure/database/columns.js.map +1 -0
  171. package/server/infrastructure/database/database.config.js +0 -1
  172. package/server/infrastructure/database/database.config.js.map +1 -1
  173. package/server/infrastructure/mailer/interfaces/mail.interface.js.map +1 -1
  174. package/server/infrastructure/mailer/mailer.config.js +12 -0
  175. package/server/infrastructure/mailer/mailer.config.js.map +1 -1
  176. package/server/infrastructure/mailer/mailer.service.js +2 -1
  177. package/server/infrastructure/mailer/mailer.service.js.map +1 -1
  178. package/static/assets/mimes/text-x-c.svg +1 -0
  179. package/static/chunk-2TZUZMCM.js +4 -0
  180. package/static/chunk-2XJ5Z2GZ.js +1 -0
  181. package/static/{chunk-7VRUZRJG.js → chunk-5M4YJZUB.js} +2 -2
  182. package/static/{chunk-MRSWNAVB.js → chunk-5ZGQYTS2.js} +1 -1
  183. package/static/chunk-6BFNMDUD.js +1 -0
  184. package/static/chunk-6IRL673W.js +559 -0
  185. package/static/{chunk-2R6HHGUR.js → chunk-ABGR5AYC.js} +1 -1
  186. package/static/chunk-CN27VAGB.js +1 -0
  187. package/static/{chunk-MVO4WZLK.js → chunk-DNMO47SY.js} +1 -1
  188. package/static/{chunk-MGGT6MIJ.js → chunk-EI4PVI2W.js} +1 -1
  189. package/static/chunk-ET6QDNNM.js +1 -0
  190. package/static/{chunk-L6MU6S2V.js → chunk-G2TKYYWK.js} +1 -1
  191. package/static/chunk-G3FOG2QB.js +1 -0
  192. package/static/{chunk-MCLQFZ3S.js → chunk-GCUWGVYT.js} +1 -1
  193. package/static/{chunk-RSS6GYNE.js → chunk-HME7LAEY.js} +1 -1
  194. package/static/chunk-IEUANP3Q.js +1 -0
  195. package/static/{chunk-VJRTMDEJ.js → chunk-IIFHIIC6.js} +1 -1
  196. package/static/{chunk-YJMN3B4N.js → chunk-KPZ7FEMO.js} +1 -1
  197. package/static/{chunk-JYXLQRHG.js → chunk-M57NVD4V.js} +1 -1
  198. package/static/chunk-NN3VQOS7.js +1 -0
  199. package/static/chunk-NW3CTYUW.js +1 -0
  200. package/static/{chunk-6OJZWYRZ.js → chunk-O3ANXCPE.js} +1 -1
  201. package/static/{chunk-ZC5NIT55.js → chunk-QFOMEU3T.js} +1 -1
  202. package/static/{chunk-BIUNUYZ5.js → chunk-RKNTQYMU.js} +1 -1
  203. package/static/{chunk-VUI3KV7V.js → chunk-UQ4TRQCE.js} +1 -1
  204. package/static/{chunk-WI7FOANP.js → chunk-WINILGQN.js} +1 -1
  205. package/static/{chunk-NE4NDO45.js → chunk-X7MFVDBY.js} +1 -1
  206. package/static/chunk-XCBLEI2E.js +1 -0
  207. package/static/{chunk-CRQNEHTX.js → chunk-XLWCV4HI.js} +1 -1
  208. package/static/chunk-XPIYOZBX.js +4 -0
  209. package/static/{chunk-LLWSLOSX.js → chunk-YD74UCFG.js} +1 -1
  210. package/static/{chunk-IZL7JPTS.js → chunk-YDFVKH2D.js} +1 -1
  211. package/static/{chunk-SPTF6FSM.js → chunk-YVJDYSDE.js} +1 -1
  212. package/static/index.html +2 -2
  213. package/static/main-QNBKYA6L.js +9 -0
  214. package/static/{styles-FYUSO6OJ.css → styles-A5VYX3CE.css} +1 -1
  215. package/server/applications/users/dto/user-password.dto.js.map +0 -1
  216. package/static/chunk-4U5A2DEP.js +0 -4
  217. package/static/chunk-54EAZ2UD.js +0 -1
  218. package/static/chunk-7ZRXJONB.js +0 -1
  219. package/static/chunk-F2J2IIJE.js +0 -1
  220. package/static/chunk-FNFGUIQH.js +0 -4
  221. package/static/chunk-GGLK52CG.js +0 -1
  222. package/static/chunk-HW2H3ISM.js +0 -559
  223. package/static/chunk-HX6BBYVD.js +0 -1
  224. package/static/chunk-JF7S3UYQ.js +0 -1
  225. package/static/chunk-KSHPKI4G.js +0 -1
  226. package/static/chunk-VPJ2V27B.js +0 -1
  227. package/static/chunk-ZXS4V7J2.js +0 -1
  228. package/static/main-FFIWFD2F.js +0 -7
@@ -18,27 +18,44 @@ _export(exports, {
18
18
  },
19
19
  get exportConfiguration () {
20
20
  return exportConfiguration;
21
+ },
22
+ get serverConfig () {
23
+ return serverConfig;
21
24
  }
22
25
  });
23
26
  const _nodepath = require("node:path");
24
27
  const _auth = require("../authentication/constants/auth");
28
+ const _tokeninterface = require("../authentication/interfaces/token.interface");
25
29
  const _functions = require("../common/functions");
26
30
  const _configloader = require("./config.loader");
27
31
  const _configvalidation = require("./config.validation");
28
32
  const configuration = loadConfiguration();
33
+ const serverConfig = {
34
+ twoFaEnabled: configuration.auth.mfa.totp.enabled,
35
+ mailServerEnabled: !!configuration.mail?.host
36
+ };
29
37
  const exportConfiguration = (reload = false)=>reload ? loadConfiguration() : configuration;
30
38
  function loadConfiguration() {
31
39
  const config = (0, _configloader.configLoader)();
32
40
  // AUTHENTICATION
33
- // CSRF & WS settings
34
- config.auth.token.csrf = {
35
- ...config.auth.token.refresh,
41
+ // CSRF & WS & 2FA settings
42
+ config.auth.token[_tokeninterface.TOKEN_TYPE.CSRF] = {
43
+ ...config.auth.token[_tokeninterface.TOKEN_TYPE.REFRESH],
36
44
  name: _auth.CSRF_KEY
37
45
  };
38
- config.auth.token.ws = {
39
- ...config.auth.token.refresh,
46
+ config.auth.token[_tokeninterface.TOKEN_TYPE.WS] = {
47
+ ...config.auth.token[_tokeninterface.TOKEN_TYPE.REFRESH],
40
48
  name: _auth.WS_KEY
41
49
  };
50
+ config.auth.token[_tokeninterface.TOKEN_TYPE.ACCESS_2FA] = {
51
+ ...config.auth.token[_tokeninterface.TOKEN_TYPE.ACCESS],
52
+ name: _auth.ACCESS_KEY,
53
+ expiration: _auth.TWO_FA_VERIFY_EXPIRATION
54
+ };
55
+ config.auth.token[_tokeninterface.TOKEN_TYPE.CSRF_2FA] = {
56
+ ...config.auth.token[_tokeninterface.TOKEN_TYPE.CSRF],
57
+ expiration: _auth.TWO_FA_VERIFY_EXPIRATION
58
+ };
42
59
  // APPLICATIONS CONFIGURATION
43
60
  // SPACES & FILES
44
61
  if (!config.applications.files.dataPath) {
@@ -47,7 +64,7 @@ function loadConfiguration() {
47
64
  config.applications.files.usersPath = (0, _nodepath.join)(config.applications.files.dataPath, 'users');
48
65
  config.applications.files.spacesPath = (0, _nodepath.join)(config.applications.files.dataPath, 'spaces');
49
66
  config.applications.files.tmpPath = (0, _nodepath.join)(config.applications.files.dataPath, 'tmp');
50
- return (0, _functions.transformAndValidate)(_configvalidation.Configuration, config, {
67
+ return (0, _functions.transformAndValidate)(_configvalidation.GlobalConfig, config, {
51
68
  exposeDefaultValues: true
52
69
  }, {
53
70
  skipMissingProperties: false
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../backend/src/configuration/config.environment.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 { join } from 'node:path'\nimport { CSRF_KEY, WS_KEY } from '../authentication/constants/auth'\nimport { transformAndValidate } from '../common/functions'\nimport { configLoader } from './config.loader'\nimport { Configuration } from './config.validation'\n\nexport const configuration: Configuration = loadConfiguration()\nexport const exportConfiguration: (reload?: boolean) => Configuration = (reload = false) => (reload ? loadConfiguration() : configuration)\n\nfunction loadConfiguration(): Configuration {\n const config: Configuration = configLoader()\n // AUTHENTICATION\n // CSRF & WS settings\n config.auth.token.csrf = { ...config.auth.token.refresh, name: CSRF_KEY }\n config.auth.token.ws = { ...config.auth.token.refresh, name: WS_KEY }\n // APPLICATIONS CONFIGURATION\n // SPACES & FILES\n if (!config.applications.files.dataPath) {\n throw new Error('dataPath is not defined in environment.yaml')\n }\n config.applications.files.usersPath = join(config.applications.files.dataPath, 'users')\n config.applications.files.spacesPath = join(config.applications.files.dataPath, 'spaces')\n config.applications.files.tmpPath = join(config.applications.files.dataPath, 'tmp')\n return transformAndValidate(Configuration, config, { exposeDefaultValues: true }, { skipMissingProperties: false })\n}\n"],"names":["configuration","exportConfiguration","loadConfiguration","reload","config","configLoader","auth","token","csrf","refresh","name","CSRF_KEY","ws","WS_KEY","applications","files","dataPath","Error","usersPath","join","spacesPath","tmpPath","transformAndValidate","Configuration","exposeDefaultValues","skipMissingProperties"],"mappings":"AAAA;;;;CAIC;;;;;;;;;;;QAQYA;eAAAA;;QACAC;eAAAA;;;0BAPQ;sBACY;2BACI;8BACR;kCACC;AAEvB,MAAMD,gBAA+BE;AACrC,MAAMD,sBAA2D,CAACE,SAAS,KAAK,GAAMA,SAASD,sBAAsBF;AAE5H,SAASE;IACP,MAAME,SAAwBC,IAAAA,0BAAY;IAC1C,iBAAiB;IACjB,qBAAqB;IACrBD,OAAOE,IAAI,CAACC,KAAK,CAACC,IAAI,GAAG;QAAE,GAAGJ,OAAOE,IAAI,CAACC,KAAK,CAACE,OAAO;QAAEC,MAAMC,cAAQ;IAAC;IACxEP,OAAOE,IAAI,CAACC,KAAK,CAACK,EAAE,GAAG;QAAE,GAAGR,OAAOE,IAAI,CAACC,KAAK,CAACE,OAAO;QAAEC,MAAMG,YAAM;IAAC;IACpE,6BAA6B;IAC7B,iBAAiB;IACjB,IAAI,CAACT,OAAOU,YAAY,CAACC,KAAK,CAACC,QAAQ,EAAE;QACvC,MAAM,IAAIC,MAAM;IAClB;IACAb,OAAOU,YAAY,CAACC,KAAK,CAACG,SAAS,GAAGC,IAAAA,cAAI,EAACf,OAAOU,YAAY,CAACC,KAAK,CAACC,QAAQ,EAAE;IAC/EZ,OAAOU,YAAY,CAACC,KAAK,CAACK,UAAU,GAAGD,IAAAA,cAAI,EAACf,OAAOU,YAAY,CAACC,KAAK,CAACC,QAAQ,EAAE;IAChFZ,OAAOU,YAAY,CAACC,KAAK,CAACM,OAAO,GAAGF,IAAAA,cAAI,EAACf,OAAOU,YAAY,CAACC,KAAK,CAACC,QAAQ,EAAE;IAC7E,OAAOM,IAAAA,+BAAoB,EAACC,+BAAa,EAAEnB,QAAQ;QAAEoB,qBAAqB;IAAK,GAAG;QAAEC,uBAAuB;IAAM;AACnH"}
1
+ {"version":3,"sources":["../../../backend/src/configuration/config.environment.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 { join } from 'node:path'\nimport { AuthTokenAccessConfig, AuthTokenRefreshConfig } from '../authentication/auth.config'\nimport { ACCESS_KEY, CSRF_KEY, TWO_FA_VERIFY_EXPIRATION, WS_KEY } from '../authentication/constants/auth'\nimport { TOKEN_TYPE } from '../authentication/interfaces/token.interface'\nimport { transformAndValidate } from '../common/functions'\nimport { ServerConfig } from './config.interfaces'\nimport { configLoader } from './config.loader'\nimport { GlobalConfig } from './config.validation'\n\nexport const configuration: GlobalConfig = loadConfiguration()\nexport const serverConfig: ServerConfig = { twoFaEnabled: configuration.auth.mfa.totp.enabled, mailServerEnabled: !!configuration.mail?.host }\nexport const exportConfiguration: (reload?: boolean) => GlobalConfig = (reload = false) => (reload ? loadConfiguration() : configuration)\n\nfunction loadConfiguration(): GlobalConfig {\n const config: GlobalConfig = configLoader()\n // AUTHENTICATION\n // CSRF & WS & 2FA settings\n config.auth.token[TOKEN_TYPE.CSRF] = { ...config.auth.token[TOKEN_TYPE.REFRESH], name: CSRF_KEY } satisfies AuthTokenRefreshConfig\n config.auth.token[TOKEN_TYPE.WS] = { ...config.auth.token[TOKEN_TYPE.REFRESH], name: WS_KEY } satisfies AuthTokenRefreshConfig\n config.auth.token[TOKEN_TYPE.ACCESS_2FA] = {\n ...config.auth.token[TOKEN_TYPE.ACCESS],\n name: ACCESS_KEY,\n expiration: TWO_FA_VERIFY_EXPIRATION\n } satisfies AuthTokenAccessConfig\n config.auth.token[TOKEN_TYPE.CSRF_2FA] = {\n ...config.auth.token[TOKEN_TYPE.CSRF],\n expiration: TWO_FA_VERIFY_EXPIRATION\n } satisfies AuthTokenAccessConfig\n // APPLICATIONS CONFIGURATION\n // SPACES & FILES\n if (!config.applications.files.dataPath) {\n throw new Error('dataPath is not defined in environment.yaml')\n }\n config.applications.files.usersPath = join(config.applications.files.dataPath, 'users')\n config.applications.files.spacesPath = join(config.applications.files.dataPath, 'spaces')\n config.applications.files.tmpPath = join(config.applications.files.dataPath, 'tmp')\n return transformAndValidate(GlobalConfig, config, { exposeDefaultValues: true }, { skipMissingProperties: false })\n}\n"],"names":["configuration","exportConfiguration","serverConfig","loadConfiguration","twoFaEnabled","auth","mfa","totp","enabled","mailServerEnabled","mail","host","reload","config","configLoader","token","TOKEN_TYPE","CSRF","REFRESH","name","CSRF_KEY","WS","WS_KEY","ACCESS_2FA","ACCESS","ACCESS_KEY","expiration","TWO_FA_VERIFY_EXPIRATION","CSRF_2FA","applications","files","dataPath","Error","usersPath","join","spacesPath","tmpPath","transformAndValidate","GlobalConfig","exposeDefaultValues","skipMissingProperties"],"mappings":"AAAA;;;;CAIC;;;;;;;;;;;QAWYA;eAAAA;;QAEAC;eAAAA;;QADAC;eAAAA;;;0BAVQ;sBAEkD;gCAC5C;2BACU;8BAER;kCACA;AAEtB,MAAMF,gBAA8BG;AACpC,MAAMD,eAA6B;IAAEE,cAAcJ,cAAcK,IAAI,CAACC,GAAG,CAACC,IAAI,CAACC,OAAO;IAAEC,mBAAmB,CAAC,CAACT,cAAcU,IAAI,EAAEC;AAAK;AACtI,MAAMV,sBAA0D,CAACW,SAAS,KAAK,GAAMA,SAAST,sBAAsBH;AAE3H,SAASG;IACP,MAAMU,SAAuBC,IAAAA,0BAAY;IACzC,iBAAiB;IACjB,2BAA2B;IAC3BD,OAAOR,IAAI,CAACU,KAAK,CAACC,0BAAU,CAACC,IAAI,CAAC,GAAG;QAAE,GAAGJ,OAAOR,IAAI,CAACU,KAAK,CAACC,0BAAU,CAACE,OAAO,CAAC;QAAEC,MAAMC,cAAQ;IAAC;IAChGP,OAAOR,IAAI,CAACU,KAAK,CAACC,0BAAU,CAACK,EAAE,CAAC,GAAG;QAAE,GAAGR,OAAOR,IAAI,CAACU,KAAK,CAACC,0BAAU,CAACE,OAAO,CAAC;QAAEC,MAAMG,YAAM;IAAC;IAC5FT,OAAOR,IAAI,CAACU,KAAK,CAACC,0BAAU,CAACO,UAAU,CAAC,GAAG;QACzC,GAAGV,OAAOR,IAAI,CAACU,KAAK,CAACC,0BAAU,CAACQ,MAAM,CAAC;QACvCL,MAAMM,gBAAU;QAChBC,YAAYC,8BAAwB;IACtC;IACAd,OAAOR,IAAI,CAACU,KAAK,CAACC,0BAAU,CAACY,QAAQ,CAAC,GAAG;QACvC,GAAGf,OAAOR,IAAI,CAACU,KAAK,CAACC,0BAAU,CAACC,IAAI,CAAC;QACrCS,YAAYC,8BAAwB;IACtC;IACA,6BAA6B;IAC7B,iBAAiB;IACjB,IAAI,CAACd,OAAOgB,YAAY,CAACC,KAAK,CAACC,QAAQ,EAAE;QACvC,MAAM,IAAIC,MAAM;IAClB;IACAnB,OAAOgB,YAAY,CAACC,KAAK,CAACG,SAAS,GAAGC,IAAAA,cAAI,EAACrB,OAAOgB,YAAY,CAACC,KAAK,CAACC,QAAQ,EAAE;IAC/ElB,OAAOgB,YAAY,CAACC,KAAK,CAACK,UAAU,GAAGD,IAAAA,cAAI,EAACrB,OAAOgB,YAAY,CAACC,KAAK,CAACC,QAAQ,EAAE;IAChFlB,OAAOgB,YAAY,CAACC,KAAK,CAACM,OAAO,GAAGF,IAAAA,cAAI,EAACrB,OAAOgB,YAAY,CAACC,KAAK,CAACC,QAAQ,EAAE;IAC7E,OAAOM,IAAAA,+BAAoB,EAACC,8BAAY,EAAEzB,QAAQ;QAAE0B,qBAAqB;IAAK,GAAG;QAAEC,uBAAuB;IAAM;AAClH"}
@@ -0,0 +1,10 @@
1
+ /*
2
+ * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>
3
+ * This file is part of Sync-in | The open source file sync and share solution
4
+ * See the LICENSE file for licensing details
5
+ */ "use strict";
6
+ Object.defineProperty(exports, "__esModule", {
7
+ value: true
8
+ });
9
+
10
+ //# sourceMappingURL=config.interfaces.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../backend/src/configuration/config.interfaces.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 ServerConfig {\n twoFaEnabled: boolean\n mailServerEnabled: boolean\n}\n"],"names":[],"mappings":"AAAA;;;;CAIC"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../backend/src/configuration/config.loader.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 deepmerge from 'deepmerge'\nimport * as yaml from 'js-yaml'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport {\n DEFAULT_LOG_FILE_PATH,\n ENVIRONMENT_DIST_FILE_NAME,\n ENVIRONMENT_DIST_PATH,\n ENVIRONMENT_FILE_NAME,\n ENVIRONMENT_PATH,\n ENVIRONMENT_PREFIX\n} from './config.constants'\nimport type { Configuration } from './config.validation'\n\nexport function configLoader(): any {\n let config: Partial<Configuration> = loadEnvFile(ENVIRONMENT_PATH, ENVIRONMENT_FILE_NAME)\n\n if (hasEnvConfig()) {\n // If any environment vars are found, parse the config model and apply those settings\n const envConfig = getEnvOverrides(loadEnvFile(ENVIRONMENT_DIST_PATH, ENVIRONMENT_DIST_FILE_NAME, true))\n config = deepmerge(config, envConfig)\n }\n\n if (Object.keys(config).length === 0) {\n throw new Error(`Missing configuration: \"${ENVIRONMENT_FILE_NAME}\" not found, or no variables beginning with \"${ENVIRONMENT_PREFIX}\" are set.`)\n }\n\n if (config.logger?.stdout === false) {\n // ensure log directory exists\n const logFilePath = config.logger.filePath || DEFAULT_LOG_FILE_PATH\n const dirLogPath = path.dirname(logFilePath)\n if (!fs.existsSync(dirLogPath)) {\n fs.mkdirSync(dirLogPath, { recursive: true })\n }\n console.log(`Logging to file → ${logFilePath}`)\n }\n\n return config\n}\n\nfunction buildPathsUp(basePath: string, fileName: string, levels = 4): string[] {\n // Generates candidate file paths, optionally walking up from __dirname to a given depth.\n return Array.from({ length: levels + 1 }, (_, i) => path.resolve(basePath, ...Array(i).fill('..'), fileName))\n}\n\nfunction loadEnvFile(envPath: string, envFileName: string, throwIfMissing = false): any {\n const candidates = [envPath, envFileName, ...buildPathsUp(__dirname, envPath), ...buildPathsUp(__dirname, envFileName)]\n for (const envFilePath of candidates) {\n if (fs.existsSync(envFilePath) && fs.lstatSync(envFilePath).isFile()) {\n return yaml.load(fs.readFileSync(envFilePath, 'utf8'))\n }\n }\n if (throwIfMissing) {\n throw new Error(`${envFileName} not found`)\n }\n return {}\n}\n\nfunction hasEnvConfig(): boolean {\n return Object.keys(process.env).some((key) => key.startsWith(ENVIRONMENT_PREFIX))\n}\n\n/**\n * Parse a raw env-string into boolean, number or leave as string.\n */\nfunction parseEnvValue(value: string): any {\n // remove first and last quote if exists\n value = value.replace(/^\"(.*)\"$/, '$1')\n if (value === 'true') return true\n if (value === 'false') return false\n if (!isNaN(Number(value))) return Number(value)\n return value\n}\n\n/**\n * Assigns a nested property into obj, creating sub-objects as needed.\n */\nfunction setObjectPropertyFromString(obj: any, property: string, value: any): void {\n const segments = property.split('.')\n let cursor = obj\n for (let i = 0; i < segments.length - 1; i++) {\n const seg = segments[i]\n if (!(seg in cursor) || typeof cursor[seg] !== 'object') {\n cursor[seg] = {}\n }\n cursor = cursor[seg]\n }\n cursor[segments[segments.length - 1]] = value\n}\n\n/**\n * Returns a new object containing only the env-var overrides\n * that match existing keys in `config`, nested and cased properly.\n */\nfunction getEnvOverrides(config: Record<string, any>): Record<string, any> {\n const result: Record<string, any> = {}\n\n for (const [envKey, rawValue] of Object.entries(process.env)) {\n if (!envKey.startsWith(ENVIRONMENT_PREFIX) || rawValue === undefined) {\n continue\n }\n\n // [\"APPLICATIONS\",\"FILES\",\"DATAPATH\"] etc.\n const segments = envKey.slice(ENVIRONMENT_PREFIX.length).split('_')\n const secretFromFile = segments[segments.length - 1] === 'FILE'\n if (secretFromFile) {\n // remove FILE attribute\n segments.pop()\n }\n\n // Walk through config to validate path & capture real key names\n let cursorConfig: any = config\n const realSegments: string[] = []\n let pathExists = true\n\n for (const seg of segments) {\n if (cursorConfig == null || typeof cursorConfig !== 'object') {\n pathExists = false\n break\n }\n // Find the actual key (preserving camelCase) whose uppercase matches seg\n const match = Object.keys(cursorConfig).find((k) => k.toUpperCase() === seg)\n if (!match) {\n pathExists = false\n break\n }\n realSegments.push(match)\n cursorConfig = cursorConfig[match]\n }\n\n if (!pathExists) {\n console.warn(`Ignoring unknown environment variable: \"${envKey}\".`)\n continue\n }\n\n // Build the nested override in `result`\n const path = realSegments.join('.')\n if (secretFromFile) {\n try {\n setObjectPropertyFromString(result, path, fs.readFileSync(rawValue, 'utf-8').trim())\n } catch (e) {\n console.error(`Unable to store secret from file ${rawValue} : ${e}`)\n }\n } else {\n setObjectPropertyFromString(result, path, parseEnvValue(rawValue))\n }\n }\n\n return result\n}\n"],"names":["configLoader","config","loadEnvFile","ENVIRONMENT_PATH","ENVIRONMENT_FILE_NAME","hasEnvConfig","envConfig","getEnvOverrides","ENVIRONMENT_DIST_PATH","ENVIRONMENT_DIST_FILE_NAME","deepmerge","Object","keys","length","Error","ENVIRONMENT_PREFIX","logger","stdout","logFilePath","filePath","DEFAULT_LOG_FILE_PATH","dirLogPath","path","dirname","fs","existsSync","mkdirSync","recursive","console","log","buildPathsUp","basePath","fileName","levels","Array","from","_","i","resolve","fill","envPath","envFileName","throwIfMissing","candidates","__dirname","envFilePath","lstatSync","isFile","yaml","load","readFileSync","process","env","some","key","startsWith","parseEnvValue","value","replace","isNaN","Number","setObjectPropertyFromString","obj","property","segments","split","cursor","seg","result","envKey","rawValue","entries","undefined","slice","secretFromFile","pop","cursorConfig","realSegments","pathExists","match","find","k","toUpperCase","push","warn","join","trim","e","error"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../backend/src/configuration/config.loader.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 deepmerge from 'deepmerge'\nimport * as yaml from 'js-yaml'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport {\n DEFAULT_LOG_FILE_PATH,\n ENVIRONMENT_DIST_FILE_NAME,\n ENVIRONMENT_DIST_PATH,\n ENVIRONMENT_FILE_NAME,\n ENVIRONMENT_PATH,\n ENVIRONMENT_PREFIX\n} from './config.constants'\nimport type { GlobalConfig } from './config.validation'\n\nexport function configLoader(): any {\n let config: Partial<GlobalConfig> = loadEnvFile(ENVIRONMENT_PATH, ENVIRONMENT_FILE_NAME)\n\n if (hasEnvConfig()) {\n // If any environment vars are found, parse the config model and apply those settings\n const envConfig = getEnvOverrides(loadEnvFile(ENVIRONMENT_DIST_PATH, ENVIRONMENT_DIST_FILE_NAME, true))\n config = deepmerge(config, envConfig)\n }\n\n if (Object.keys(config).length === 0) {\n throw new Error(`Missing configuration: \"${ENVIRONMENT_FILE_NAME}\" not found, or no variables beginning with \"${ENVIRONMENT_PREFIX}\" are set.`)\n }\n\n if (config.logger?.stdout === false) {\n // ensure log directory exists\n const logFilePath = config.logger.filePath || DEFAULT_LOG_FILE_PATH\n const dirLogPath = path.dirname(logFilePath)\n if (!fs.existsSync(dirLogPath)) {\n fs.mkdirSync(dirLogPath, { recursive: true })\n }\n console.log(`Logging to file → ${logFilePath}`)\n }\n\n return config\n}\n\nfunction buildPathsUp(basePath: string, fileName: string, levels = 4): string[] {\n // Generates candidate file paths, optionally walking up from __dirname to a given depth.\n return Array.from({ length: levels + 1 }, (_, i) => path.resolve(basePath, ...Array(i).fill('..'), fileName))\n}\n\nfunction loadEnvFile(envPath: string, envFileName: string, throwIfMissing = false): any {\n const candidates = [envPath, envFileName, ...buildPathsUp(__dirname, envPath), ...buildPathsUp(__dirname, envFileName)]\n for (const envFilePath of candidates) {\n if (fs.existsSync(envFilePath) && fs.lstatSync(envFilePath).isFile()) {\n return yaml.load(fs.readFileSync(envFilePath, 'utf8'))\n }\n }\n if (throwIfMissing) {\n throw new Error(`${envFileName} not found`)\n }\n return {}\n}\n\nfunction hasEnvConfig(): boolean {\n return Object.keys(process.env).some((key) => key.startsWith(ENVIRONMENT_PREFIX))\n}\n\n/**\n * Parse a raw env-string into boolean, number or leave as string.\n */\nfunction parseEnvValue(value: string): any {\n // remove first and last quote if exists\n value = value.replace(/^\"(.*)\"$/, '$1')\n if (value === 'true') return true\n if (value === 'false') return false\n if (!isNaN(Number(value))) return Number(value)\n return value\n}\n\n/**\n * Assigns a nested property into obj, creating sub-objects as needed.\n */\nfunction setObjectPropertyFromString(obj: any, property: string, value: any): void {\n const segments = property.split('.')\n let cursor = obj\n for (let i = 0; i < segments.length - 1; i++) {\n const seg = segments[i]\n if (!(seg in cursor) || typeof cursor[seg] !== 'object') {\n cursor[seg] = {}\n }\n cursor = cursor[seg]\n }\n cursor[segments[segments.length - 1]] = value\n}\n\n/**\n * Returns a new object containing only the env-var overrides\n * that match existing keys in `config`, nested and cased properly.\n */\nfunction getEnvOverrides(config: Record<string, any>): Record<string, any> {\n const result: Record<string, any> = {}\n\n for (const [envKey, rawValue] of Object.entries(process.env)) {\n if (!envKey.startsWith(ENVIRONMENT_PREFIX) || rawValue === undefined) {\n continue\n }\n\n // [\"APPLICATIONS\",\"FILES\",\"DATAPATH\"] etc.\n const segments = envKey.slice(ENVIRONMENT_PREFIX.length).split('_')\n const secretFromFile = segments[segments.length - 1] === 'FILE'\n if (secretFromFile) {\n // remove FILE attribute\n segments.pop()\n }\n\n // Walk through config to validate path & capture real key names\n let cursorConfig: any = config\n const realSegments: string[] = []\n let pathExists = true\n\n for (const seg of segments) {\n if (cursorConfig == null || typeof cursorConfig !== 'object') {\n pathExists = false\n break\n }\n // Find the actual key (preserving camelCase) whose uppercase matches seg\n const match = Object.keys(cursorConfig).find((k) => k.toUpperCase() === seg)\n if (!match) {\n pathExists = false\n break\n }\n realSegments.push(match)\n cursorConfig = cursorConfig[match]\n }\n\n if (!pathExists) {\n console.warn(`Ignoring unknown environment variable: \"${envKey}\".`)\n continue\n }\n\n // Build the nested override in `result`\n const path = realSegments.join('.')\n if (secretFromFile) {\n try {\n setObjectPropertyFromString(result, path, fs.readFileSync(rawValue, 'utf-8').trim())\n } catch (e) {\n console.error(`Unable to store secret from file ${rawValue} : ${e}`)\n }\n } else {\n setObjectPropertyFromString(result, path, parseEnvValue(rawValue))\n }\n }\n\n return result\n}\n"],"names":["configLoader","config","loadEnvFile","ENVIRONMENT_PATH","ENVIRONMENT_FILE_NAME","hasEnvConfig","envConfig","getEnvOverrides","ENVIRONMENT_DIST_PATH","ENVIRONMENT_DIST_FILE_NAME","deepmerge","Object","keys","length","Error","ENVIRONMENT_PREFIX","logger","stdout","logFilePath","filePath","DEFAULT_LOG_FILE_PATH","dirLogPath","path","dirname","fs","existsSync","mkdirSync","recursive","console","log","buildPathsUp","basePath","fileName","levels","Array","from","_","i","resolve","fill","envPath","envFileName","throwIfMissing","candidates","__dirname","envFilePath","lstatSync","isFile","yaml","load","readFileSync","process","env","some","key","startsWith","parseEnvValue","value","replace","isNaN","Number","setObjectPropertyFromString","obj","property","segments","split","cursor","seg","result","envKey","rawValue","entries","undefined","slice","secretFromFile","pop","cursorConfig","realSegments","pathExists","match","find","k","toUpperCase","push","warn","join","trim","e","error"],"mappings":"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"}
@@ -13,8 +13,8 @@ function _export(target, all) {
13
13
  });
14
14
  }
15
15
  _export(exports, {
16
- get Configuration () {
17
- return Configuration;
16
+ get GlobalConfig () {
17
+ return GlobalConfig;
18
18
  },
19
19
  get LoggerConfig () {
20
20
  return LoggerConfig;
@@ -47,7 +47,7 @@ let ServerConfig = class ServerConfig {
47
47
  this.host = '0.0.0.0';
48
48
  this.port = 8080;
49
49
  this.workers = 2;
50
- this.trustProxy = false;
50
+ this.trustProxy = 1;
51
51
  this.restartOnFailure = true;
52
52
  }
53
53
  };
@@ -84,8 +84,8 @@ let LoggerConfig = class LoggerConfig {
84
84
  }
85
85
  };
86
86
  _ts_decorate([
87
- (0, _classvalidator.IsDefined)(),
88
87
  (0, _classvalidator.IsString)(),
88
+ (0, _classvalidator.IsNotEmpty)(),
89
89
  _ts_metadata("design:type", typeof Level === "undefined" ? Object : Level)
90
90
  ], LoggerConfig.prototype, "level", void 0);
91
91
  _ts_decorate([
@@ -102,7 +102,7 @@ _ts_decorate([
102
102
  (0, _classtransformer.Transform)(({ value })=>value || _configconstants.DEFAULT_LOG_FILE_PATH),
103
103
  _ts_metadata("design:type", String)
104
104
  ], LoggerConfig.prototype, "filePath", void 0);
105
- let Configuration = class Configuration {
105
+ let GlobalConfig = class GlobalConfig {
106
106
  constructor(){
107
107
  this.server = new ServerConfig();
108
108
  this.logger = new LoggerConfig();
@@ -117,7 +117,7 @@ _ts_decorate([
117
117
  (0, _classvalidator.ValidateNested)(),
118
118
  (0, _classtransformer.Type)(()=>ServerConfig),
119
119
  _ts_metadata("design:type", typeof ServerConfig === "undefined" ? Object : ServerConfig)
120
- ], Configuration.prototype, "server", void 0);
120
+ ], GlobalConfig.prototype, "server", void 0);
121
121
  _ts_decorate([
122
122
  (0, _classvalidator.IsDefined)(),
123
123
  (0, _classvalidator.IsObject)(),
@@ -125,7 +125,7 @@ _ts_decorate([
125
125
  (0, _classvalidator.ValidateNested)(),
126
126
  (0, _classtransformer.Type)(()=>LoggerConfig),
127
127
  _ts_metadata("design:type", typeof LoggerConfig === "undefined" ? Object : LoggerConfig)
128
- ], Configuration.prototype, "logger", void 0);
128
+ ], GlobalConfig.prototype, "logger", void 0);
129
129
  _ts_decorate([
130
130
  (0, _classvalidator.IsDefined)(),
131
131
  (0, _classvalidator.IsObject)(),
@@ -133,7 +133,7 @@ _ts_decorate([
133
133
  (0, _classvalidator.ValidateNested)(),
134
134
  (0, _classtransformer.Type)(()=>_databaseconfig.MySQLConfig),
135
135
  _ts_metadata("design:type", typeof _databaseconfig.MySQLConfig === "undefined" ? Object : _databaseconfig.MySQLConfig)
136
- ], Configuration.prototype, "mysql", void 0);
136
+ ], GlobalConfig.prototype, "mysql", void 0);
137
137
  _ts_decorate([
138
138
  (0, _classvalidator.IsDefined)(),
139
139
  (0, _classvalidator.IsObject)(),
@@ -141,7 +141,7 @@ _ts_decorate([
141
141
  (0, _classvalidator.ValidateNested)(),
142
142
  (0, _classtransformer.Type)(()=>_cacheconfig.CacheConfig),
143
143
  _ts_metadata("design:type", typeof _cacheconfig.CacheConfig === "undefined" ? Object : _cacheconfig.CacheConfig)
144
- ], Configuration.prototype, "cache", void 0);
144
+ ], GlobalConfig.prototype, "cache", void 0);
145
145
  _ts_decorate([
146
146
  (0, _classvalidator.IsOptional)(),
147
147
  (0, _classvalidator.IsObject)(),
@@ -149,7 +149,7 @@ _ts_decorate([
149
149
  (0, _classvalidator.ValidateNested)(),
150
150
  (0, _classtransformer.Type)(()=>_websocketconfig.WebSocketConfig),
151
151
  _ts_metadata("design:type", typeof _websocketconfig.WebSocketConfig === "undefined" ? Object : _websocketconfig.WebSocketConfig)
152
- ], Configuration.prototype, "websocket", void 0);
152
+ ], GlobalConfig.prototype, "websocket", void 0);
153
153
  _ts_decorate([
154
154
  (0, _classvalidator.IsDefined)(),
155
155
  (0, _classvalidator.IsObject)(),
@@ -157,14 +157,14 @@ _ts_decorate([
157
157
  (0, _classvalidator.ValidateNested)(),
158
158
  (0, _classtransformer.Type)(()=>_authconfig.AuthConfig),
159
159
  _ts_metadata("design:type", typeof _authconfig.AuthConfig === "undefined" ? Object : _authconfig.AuthConfig)
160
- ], Configuration.prototype, "auth", void 0);
160
+ ], GlobalConfig.prototype, "auth", void 0);
161
161
  _ts_decorate([
162
162
  (0, _classvalidator.IsOptional)(),
163
163
  (0, _classvalidator.IsObject)(),
164
164
  (0, _classvalidator.ValidateNested)(),
165
165
  (0, _classtransformer.Type)(()=>_mailerconfig.MailerConfig),
166
166
  _ts_metadata("design:type", typeof _mailerconfig.MailerConfig === "undefined" ? Object : _mailerconfig.MailerConfig)
167
- ], Configuration.prototype, "mail", void 0);
167
+ ], GlobalConfig.prototype, "mail", void 0);
168
168
  _ts_decorate([
169
169
  (0, _classvalidator.IsDefined)(),
170
170
  (0, _classvalidator.IsObject)(),
@@ -172,6 +172,6 @@ _ts_decorate([
172
172
  (0, _classvalidator.ValidateNested)(),
173
173
  (0, _classtransformer.Type)(()=>_applicationsconfig.ApplicationsConfig),
174
174
  _ts_metadata("design:type", typeof _applicationsconfig.ApplicationsConfig === "undefined" ? Object : _applicationsconfig.ApplicationsConfig)
175
- ], Configuration.prototype, "applications", void 0);
175
+ ], GlobalConfig.prototype, "applications", void 0);
176
176
 
177
177
  //# sourceMappingURL=config.validation.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../backend/src/configuration/config.validation.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 { Transform, Type } from 'class-transformer'\nimport { IsBoolean, IsDefined, IsInt, IsIP, IsNotEmptyObject, IsObject, IsOptional, IsString, Max, Min, ValidateNested } from 'class-validator'\nimport { cpus } from 'node:os'\nimport type { Level } from 'pino'\nimport { ApplicationsConfig } from '../applications/applications.config'\nimport { AuthConfig } from '../authentication/auth.config'\nimport { CacheConfig } from '../infrastructure/cache/cache.config'\nimport { MySQLConfig } from '../infrastructure/database/database.config'\nimport { MailerConfig } from '../infrastructure/mailer/mailer.config'\nimport { WebSocketConfig } from '../infrastructure/websocket/web-socket.config'\nimport { DEFAULT_LOG_FILE_PATH } from './config.constants'\n\nexport class ServerConfig {\n @IsIP()\n host: string = '0.0.0.0'\n\n @IsInt()\n @Min(1024)\n @Max(65535)\n port: number = 8080\n\n @Transform(({ value }) => (value === 0 || value === 'auto' ? cpus().length : Math.max(Number(value), 2)))\n @IsInt()\n @Min(2)\n workers: number = 2\n\n @IsOptional()\n trustProxy: boolean | string = false\n\n @IsBoolean()\n restartOnFailure: boolean = true\n}\n\nexport class LoggerConfig {\n @IsDefined()\n @IsString()\n level: Level = 'info'\n\n @IsBoolean()\n stdout: boolean = true\n\n @IsBoolean()\n colorize: boolean = true\n\n @IsOptional()\n @IsString()\n @Transform(({ value }) => value || DEFAULT_LOG_FILE_PATH)\n filePath: string = DEFAULT_LOG_FILE_PATH\n}\n\nexport class Configuration {\n @IsDefined()\n @IsObject()\n @IsNotEmptyObject()\n @ValidateNested()\n @Type(() => ServerConfig)\n server: ServerConfig = new ServerConfig()\n\n @IsDefined()\n @IsObject()\n @IsNotEmptyObject()\n @ValidateNested()\n @Type(() => LoggerConfig)\n logger: LoggerConfig = new LoggerConfig()\n\n @IsDefined()\n @IsObject()\n @IsNotEmptyObject()\n @ValidateNested()\n @Type(() => MySQLConfig)\n mysql: MySQLConfig\n\n @IsDefined()\n @IsObject()\n @IsNotEmptyObject()\n @ValidateNested()\n @Type(() => CacheConfig)\n cache: CacheConfig = new CacheConfig()\n\n @IsOptional()\n @IsObject()\n @IsNotEmptyObject()\n @ValidateNested()\n @Type(() => WebSocketConfig)\n websocket: WebSocketConfig = new WebSocketConfig()\n\n @IsDefined()\n @IsObject()\n @IsNotEmptyObject()\n @ValidateNested()\n @Type(() => AuthConfig)\n auth: AuthConfig\n\n @IsOptional()\n @IsObject()\n @ValidateNested()\n @Type(() => MailerConfig)\n mail?: MailerConfig\n\n @IsDefined()\n @IsObject()\n @IsNotEmptyObject()\n @ValidateNested()\n @Type(() => ApplicationsConfig)\n applications: ApplicationsConfig\n}\n"],"names":["Configuration","LoggerConfig","ServerConfig","host","port","workers","trustProxy","restartOnFailure","value","cpus","length","Math","max","Number","level","stdout","colorize","filePath","DEFAULT_LOG_FILE_PATH","server","logger","cache","CacheConfig","websocket","WebSocketConfig","MySQLConfig","AuthConfig","MailerConfig","ApplicationsConfig"],"mappings":"AAAA;;;;CAIC;;;;;;;;;;;QAoDYA;eAAAA;;QAjBAC;eAAAA;;QArBAC;eAAAA;;;kCAZmB;gCAC8F;wBACzG;oCAEc;4BACR;6BACC;gCACA;8BACC;iCACG;iCACM;;;;;;;;;;AAE/B,IAAA,AAAMA,eAAN,MAAMA;;aAEXC,OAAe;aAKfC,OAAe;aAKfC,UAAkB;aAGlBC,aAA+B;aAG/BC,mBAA4B;;AAC9B;;;;;;;;;;;;sCAVc,EAAEC,KAAK,EAAE,GAAMA,UAAU,KAAKA,UAAU,SAASC,IAAAA,YAAI,IAAGC,MAAM,GAAGC,KAAKC,GAAG,CAACC,OAAOL,QAAQ;;;;;;;;;;;;;AAYhG,IAAA,AAAMP,eAAN,MAAMA;;aAGXa,QAAe;aAGfC,SAAkB;aAGlBC,WAAoB;aAKpBC,WAAmBC,sCAAqB;;AAC1C;;;;;;;;;;;;;;;;;sCAFc,EAAEV,KAAK,EAAE,GAAKA,SAASU,sCAAqB;;;AAInD,IAAA,AAAMlB,gBAAN,MAAMA;;aAMXmB,SAAuB,IAAIjB;aAO3BkB,SAAuB,IAAInB;aAc3BoB,QAAqB,IAAIC,wBAAW;aAOpCC,YAA6B,IAAIC,gCAAe;;AAqBlD;;;;;;oCAlDctB;;;;;;;;oCAOAD;;;;;;;;oCAOAwB,2BAAW;;;;;;;;oCAOXH,wBAAW;;;;;;;;oCAOXE,gCAAe;;;;;;;;oCAOfE,sBAAU;;;;;;;oCAMVC,0BAAY;;;;;;;;oCAOZC,sCAAkB"}
1
+ {"version":3,"sources":["../../../backend/src/configuration/config.validation.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 { Transform, Type } from 'class-transformer'\nimport {\n IsBoolean,\n IsDefined,\n IsInt,\n IsIP,\n IsNotEmpty,\n IsNotEmptyObject,\n IsObject,\n IsOptional,\n IsString,\n Max,\n Min,\n ValidateNested\n} from 'class-validator'\nimport { cpus } from 'node:os'\nimport type { Level } from 'pino'\nimport { ApplicationsConfig } from '../applications/applications.config'\nimport { AuthConfig } from '../authentication/auth.config'\nimport { CacheConfig } from '../infrastructure/cache/cache.config'\nimport { MySQLConfig } from '../infrastructure/database/database.config'\nimport { MailerConfig } from '../infrastructure/mailer/mailer.config'\nimport { WebSocketConfig } from '../infrastructure/websocket/web-socket.config'\nimport { DEFAULT_LOG_FILE_PATH } from './config.constants'\n\nexport class ServerConfig {\n @IsIP()\n host: string = '0.0.0.0'\n\n @IsInt()\n @Min(1024)\n @Max(65535)\n port: number = 8080\n\n @Transform(({ value }) => (value === 0 || value === 'auto' ? cpus().length : Math.max(Number(value), 2)))\n @IsInt()\n @Min(2)\n workers: number = 2\n\n @IsOptional()\n trustProxy: boolean | string | number = 1\n\n @IsBoolean()\n restartOnFailure: boolean = true\n}\n\nexport class LoggerConfig {\n @IsString()\n @IsNotEmpty()\n level: Level = 'info'\n\n @IsBoolean()\n stdout: boolean = true\n\n @IsBoolean()\n colorize: boolean = true\n\n @IsOptional()\n @IsString()\n @Transform(({ value }) => value || DEFAULT_LOG_FILE_PATH)\n filePath: string = DEFAULT_LOG_FILE_PATH\n}\n\nexport class GlobalConfig {\n @IsDefined()\n @IsObject()\n @IsNotEmptyObject()\n @ValidateNested()\n @Type(() => ServerConfig)\n server: ServerConfig = new ServerConfig()\n\n @IsDefined()\n @IsObject()\n @IsNotEmptyObject()\n @ValidateNested()\n @Type(() => LoggerConfig)\n logger: LoggerConfig = new LoggerConfig()\n\n @IsDefined()\n @IsObject()\n @IsNotEmptyObject()\n @ValidateNested()\n @Type(() => MySQLConfig)\n mysql: MySQLConfig\n\n @IsDefined()\n @IsObject()\n @IsNotEmptyObject()\n @ValidateNested()\n @Type(() => CacheConfig)\n cache: CacheConfig = new CacheConfig()\n\n @IsOptional()\n @IsObject()\n @IsNotEmptyObject()\n @ValidateNested()\n @Type(() => WebSocketConfig)\n websocket: WebSocketConfig = new WebSocketConfig()\n\n @IsDefined()\n @IsObject()\n @IsNotEmptyObject()\n @ValidateNested()\n @Type(() => AuthConfig)\n auth: AuthConfig\n\n @IsOptional()\n @IsObject()\n @ValidateNested()\n @Type(() => MailerConfig)\n mail?: MailerConfig\n\n @IsDefined()\n @IsObject()\n @IsNotEmptyObject()\n @ValidateNested()\n @Type(() => ApplicationsConfig)\n applications: ApplicationsConfig\n}\n"],"names":["GlobalConfig","LoggerConfig","ServerConfig","host","port","workers","trustProxy","restartOnFailure","value","cpus","length","Math","max","Number","level","stdout","colorize","filePath","DEFAULT_LOG_FILE_PATH","server","logger","cache","CacheConfig","websocket","WebSocketConfig","MySQLConfig","AuthConfig","MailerConfig","ApplicationsConfig"],"mappings":"AAAA;;;;CAIC;;;;;;;;;;;QAiEYA;eAAAA;;QAjBAC;eAAAA;;QArBAC;eAAAA;;;kCAzBmB;gCAczB;wBACc;oCAEc;4BACR;6BACC;gCACA;8BACC;iCACG;iCACM;;;;;;;;;;AAE/B,IAAA,AAAMA,eAAN,MAAMA;;aAEXC,OAAe;aAKfC,OAAe;aAKfC,UAAkB;aAGlBC,aAAwC;aAGxCC,mBAA4B;;AAC9B;;;;;;;;;;;;sCAVc,EAAEC,KAAK,EAAE,GAAMA,UAAU,KAAKA,UAAU,SAASC,IAAAA,YAAI,IAAGC,MAAM,GAAGC,KAAKC,GAAG,CAACC,OAAOL,QAAQ;;;;;;;;;;;;;AAYhG,IAAA,AAAMP,eAAN,MAAMA;;aAGXa,QAAe;aAGfC,SAAkB;aAGlBC,WAAoB;aAKpBC,WAAmBC,sCAAqB;;AAC1C;;;;;;;;;;;;;;;;;sCAFc,EAAEV,KAAK,EAAE,GAAKA,SAASU,sCAAqB;;;AAInD,IAAA,AAAMlB,eAAN,MAAMA;;aAMXmB,SAAuB,IAAIjB;aAO3BkB,SAAuB,IAAInB;aAc3BoB,QAAqB,IAAIC,wBAAW;aAOpCC,YAA6B,IAAIC,gCAAe;;AAqBlD;;;;;;oCAlDctB;;;;;;;;oCAOAD;;;;;;;;oCAOAwB,2BAAW;;;;;;;;oCAOXH,wBAAW;;;;;;;;oCAOXE,gCAAe;;;;;;;;oCAOfE,sBAAU;;;;;;;oCAMVC,0BAAY;;;;;;;;oCAOZC,sCAAkB"}
@@ -78,13 +78,13 @@ let MysqlCacheAdapter = class MysqlCacheAdapter {
78
78
  }
79
79
  async get(key) {
80
80
  const [v] = await this.db.select({
81
- value: (0, _drizzleorm.sql)`${_mysqlcacheschema.cache.value}`.mapWith(JSON.parse)
81
+ value: _mysqlcacheschema.cache.value
82
82
  }).from(_mysqlcacheschema.cache).where((0, _drizzleorm.and)((0, _drizzleorm.eq)(_mysqlcacheschema.cache.key, key), this.whereNotExpired())).limit(1);
83
83
  return v ? v.value : v;
84
84
  }
85
85
  async mget(keys) {
86
86
  const vs = await this.db.select({
87
- value: (0, _drizzleorm.sql)`${_mysqlcacheschema.cache.value}`.mapWith(JSON.parse)
87
+ value: _mysqlcacheschema.cache.value
88
88
  }).from(_mysqlcacheschema.cache).where((0, _drizzleorm.and)((0, _drizzleorm.inArray)(_mysqlcacheschema.cache.key, keys), this.whereNotExpired()));
89
89
  return vs.map((v)=>v.value);
90
90
  }
@@ -117,15 +117,15 @@ let MysqlCacheAdapter = class MysqlCacheAdapter {
117
117
  genSlugKey(...args) {
118
118
  return (0, _shared.createSlug)(args.join(' '));
119
119
  }
120
+ async quit() {
121
+ this.logger.verbose(`${this.quit.name}`);
122
+ }
120
123
  getTTL(ttl) {
121
124
  /* ttl (seconds):
122
125
  - 0 : infinite expiration
123
126
  - undefined : default ttl
124
127
  */ return ttl ? (0, _shared.currentTimeStamp)() + ttl : ttl === 0 ? this.infiniteExpiration : (0, _shared.currentTimeStamp)() + this.defaultTTL;
125
128
  }
126
- async quit() {
127
- this.logger.verbose(`${this.quit.name}`);
128
- }
129
129
  serialize(data) {
130
130
  if (data === undefined) {
131
131
  // undefined values are not handled by JSON serialization
@@ -143,7 +143,7 @@ let MysqlCacheAdapter = class MysqlCacheAdapter {
143
143
  constructor(db, scheduler){
144
144
  this.db = db;
145
145
  this.scheduler = scheduler;
146
- /* Useful sql commands to stats the scheduler
146
+ /* Useful SQL commands to stats the scheduler
147
147
  SHOW VARIABLES LIKE 'event_scheduler';
148
148
  SHOW EVENTS;
149
149
  */ this.defaultTTL = _configenvironment.configuration.cache.ttl;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../backend/src/infrastructure/cache/adapters/mysql-cache.adapter.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 { Inject, Injectable, Logger } from '@nestjs/common'\nimport { SchedulerRegistry } from '@nestjs/schedule'\nimport { CronJob } from 'cron'\nimport { and, between, eq, exists, inArray, like, notBetween, sql, SQL } from 'drizzle-orm'\nimport cluster from 'node:cluster'\nimport { createSlug, currentTimeStamp } from '../../../common/shared'\nimport { configuration } from '../../../configuration/config.environment'\nimport { DB_TOKEN_PROVIDER } from '../../database/constants'\nimport { DBSchema } from '../../database/interfaces/database.interface'\nimport { dbCheckAffectedRows } from '../../database/utils'\nimport { MysqlCache } from '../schemas/mysql-cache.interface'\nimport { cache } from '../schemas/mysql-cache.schema'\nimport { Cache } from '../services/cache.service'\n\n@Injectable()\nexport class MysqlCacheAdapter implements Cache {\n /* Useful sql commands to stats the scheduler\n SHOW VARIABLES LIKE 'event_scheduler';\n SHOW EVENTS;\n */\n defaultTTL: number = configuration.cache.ttl\n infiniteExpiration = -1\n private scheduledJob: CronJob\n private readonly scheduledJobName = 'cache_expired_keys' as const\n private readonly scheduledJobInterval = 5 // minutes\n private readonly logger = new Logger(Cache.name.toUpperCase())\n private readonly whereNotExpired: () => SQL = () => notBetween(cache.expiration, 0, currentTimeStamp())\n private readonly whereExpired: () => SQL = () => between(cache.expiration, 0, currentTimeStamp())\n\n constructor(\n @Inject(DB_TOKEN_PROVIDER) private readonly db: DBSchema,\n private readonly scheduler: SchedulerRegistry\n ) {\n this.initScheduler().catch((e: Error) => this.logger.error(e))\n }\n\n async initScheduler(): Promise<void> {\n if (!cluster.worker || cluster.worker.id === 1) {\n try {\n await this.db.execute(`SET GLOBAL event_scheduler = ON;`)\n await this.db.execute(`DROP EVENT IF EXISTS ${this.scheduledJobName};`)\n await this.db.execute(`CREATE EVENT IF NOT EXISTS ${this.scheduledJobName}\n ON SCHEDULE EVERY ${this.scheduledJobInterval} MINUTE\n DO DELETE FROM cache WHERE cache.expiration BETWEEN 0 AND UNIX_TIMESTAMP();`)\n this.logger.log(`Using MySQL scheduler`)\n } catch (e) {\n this.logger.error(`MySQL scheduler on '${e?.sql || e?.code}' : ${e.message || e}`)\n this.logger.warn(`Fallback to internal scheduler`)\n this.scheduledJob = new CronJob(`0 */${this.scheduledJobInterval} * * * *`, async () => await this.clearExpiredKeys())\n this.scheduler.addCronJob(this.scheduledJobName, this.scheduledJob)\n this.scheduledJob.start()\n }\n }\n }\n\n async keys(pattern: string): Promise<string[]> {\n const ks = await this.db\n .select({ key: cache.key })\n .from(cache)\n .where(and(like(cache.key, pattern.replaceAll('*', '%')), this.whereNotExpired()))\n return ks.map((k: { key: string }) => k.key)\n }\n\n async has(key: string): Promise<boolean> {\n const [r] = await this.db\n .select({ key: cache.key })\n .from(cache)\n .where(\n exists(\n this.db\n .select({ key: cache.key })\n .from(cache)\n .where(and(eq(cache.key, key), this.whereNotExpired()))\n )\n )\n return !!r\n }\n\n async get(key: string): Promise<any> {\n const [v]: { value: any }[] = await this.db\n .select({ value: sql`${cache.value}`.mapWith(JSON.parse) })\n .from(cache)\n .where(and(eq(cache.key, key), this.whereNotExpired()))\n .limit(1)\n return v ? v.value : v\n }\n\n async mget(keys: string[]): Promise<any[]> {\n const vs: { value: any }[] = await this.db\n .select({ value: sql`${cache.value}`.mapWith(JSON.parse) })\n .from(cache)\n .where(and(inArray(cache.key, keys), this.whereNotExpired()))\n return vs.map((v: { value: any }) => v.value)\n }\n\n async set(key: any, data: any, ttl?: number): Promise<boolean> {\n data = this.serialize(data)\n const exp = this.getTTL(ttl)\n try {\n await this.db\n .insert(cache)\n .values({ key: key, value: data, expiration: exp } as MysqlCache)\n .onDuplicateKeyUpdate({\n set: {\n value: data,\n expiration: exp\n } as Partial<MysqlCache>\n })\n return true\n } catch (e) {\n this.logger.error(`${this.set.name} - ${e}`)\n return false\n }\n }\n\n async del(key: string): Promise<boolean> {\n return dbCheckAffectedRows(await this.db.delete(cache).where(eq(cache.key, key)), 1, false)\n }\n\n async mdel(keys: string[]): Promise<boolean> {\n return dbCheckAffectedRows(await this.db.delete(cache).where(inArray(cache.key, keys)), keys.length, false)\n }\n\n genSlugKey(...args: any[]): string {\n return createSlug(args.join(' '))\n }\n\n private getTTL(ttl: number): number {\n /* ttl (seconds):\n - 0 : infinite expiration\n - undefined : default ttl\n */\n return ttl ? currentTimeStamp() + ttl : ttl === 0 ? this.infiniteExpiration : currentTimeStamp() + this.defaultTTL\n }\n\n async quit(): Promise<void> {\n this.logger.verbose(`${this.quit.name}`)\n }\n\n private serialize(data: any) {\n if (data === undefined) {\n // undefined values are not handled by JSON serialization\n return null\n }\n return data\n }\n\n private async clearExpiredKeys() {\n try {\n await this.db.delete(cache).where(this.whereExpired())\n } catch (e) {\n this.logger.error(`${this.clearExpiredKeys.name} - ${e?.code || e}`)\n }\n }\n}\n"],"names":["MysqlCacheAdapter","initScheduler","cluster","worker","id","db","execute","scheduledJobName","scheduledJobInterval","logger","log","e","error","sql","code","message","warn","scheduledJob","CronJob","clearExpiredKeys","scheduler","addCronJob","start","keys","pattern","ks","select","key","cache","from","where","and","like","replaceAll","whereNotExpired","map","k","has","r","exists","eq","get","v","value","mapWith","JSON","parse","limit","mget","vs","inArray","set","data","ttl","serialize","exp","getTTL","insert","values","expiration","onDuplicateKeyUpdate","name","del","dbCheckAffectedRows","delete","mdel","length","genSlugKey","args","createSlug","join","currentTimeStamp","infiniteExpiration","defaultTTL","quit","verbose","undefined","whereExpired","configuration","Logger","Cache","toUpperCase","notBetween","between","catch"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../backend/src/infrastructure/cache/adapters/mysql-cache.adapter.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 { Inject, Injectable, Logger } from '@nestjs/common'\nimport { SchedulerRegistry } from '@nestjs/schedule'\nimport { CronJob } from 'cron'\nimport { and, between, eq, exists, inArray, like, notBetween, SQL } from 'drizzle-orm'\nimport cluster from 'node:cluster'\nimport { createSlug, currentTimeStamp } from '../../../common/shared'\nimport { configuration } from '../../../configuration/config.environment'\nimport { DB_TOKEN_PROVIDER } from '../../database/constants'\nimport { DBSchema } from '../../database/interfaces/database.interface'\nimport { dbCheckAffectedRows } from '../../database/utils'\nimport { MysqlCache } from '../schemas/mysql-cache.interface'\nimport { cache } from '../schemas/mysql-cache.schema'\nimport { Cache } from '../services/cache.service'\n\n@Injectable()\nexport class MysqlCacheAdapter implements Cache {\n /* Useful SQL commands to stats the scheduler\n SHOW VARIABLES LIKE 'event_scheduler';\n SHOW EVENTS;\n */\n defaultTTL: number = configuration.cache.ttl\n infiniteExpiration = -1\n private scheduledJob: CronJob\n private readonly scheduledJobName = 'cache_expired_keys' as const\n private readonly scheduledJobInterval = 5 // minutes\n private readonly logger = new Logger(Cache.name.toUpperCase())\n\n constructor(\n @Inject(DB_TOKEN_PROVIDER) private readonly db: DBSchema,\n private readonly scheduler: SchedulerRegistry\n ) {\n this.initScheduler().catch((e: Error) => this.logger.error(e))\n }\n\n async initScheduler(): Promise<void> {\n if (!cluster.worker || cluster.worker.id === 1) {\n try {\n await this.db.execute(`SET GLOBAL event_scheduler = ON;`)\n await this.db.execute(`DROP EVENT IF EXISTS ${this.scheduledJobName};`)\n await this.db.execute(`CREATE EVENT IF NOT EXISTS ${this.scheduledJobName}\n ON SCHEDULE EVERY ${this.scheduledJobInterval} MINUTE\n DO DELETE FROM cache WHERE cache.expiration BETWEEN 0 AND UNIX_TIMESTAMP();`)\n this.logger.log(`Using MySQL scheduler`)\n } catch (e) {\n this.logger.error(`MySQL scheduler on '${e?.sql || e?.code}' : ${e.message || e}`)\n this.logger.warn(`Fallback to internal scheduler`)\n this.scheduledJob = new CronJob(`0 */${this.scheduledJobInterval} * * * *`, async () => await this.clearExpiredKeys())\n this.scheduler.addCronJob(this.scheduledJobName, this.scheduledJob)\n this.scheduledJob.start()\n }\n }\n }\n\n async keys(pattern: string): Promise<string[]> {\n const ks = await this.db\n .select({ key: cache.key })\n .from(cache)\n .where(and(like(cache.key, pattern.replaceAll('*', '%')), this.whereNotExpired()))\n return ks.map((k: { key: string }) => k.key)\n }\n\n async has(key: string): Promise<boolean> {\n const [r] = await this.db\n .select({ key: cache.key })\n .from(cache)\n .where(\n exists(\n this.db\n .select({ key: cache.key })\n .from(cache)\n .where(and(eq(cache.key, key), this.whereNotExpired()))\n )\n )\n return !!r\n }\n\n async get(key: string): Promise<any> {\n const [v]: { value: any }[] = await this.db\n .select({ value: cache.value })\n .from(cache)\n .where(and(eq(cache.key, key), this.whereNotExpired()))\n .limit(1)\n return v ? v.value : v\n }\n\n async mget(keys: string[]): Promise<any[]> {\n const vs: { value: any }[] = await this.db\n .select({ value: cache.value })\n .from(cache)\n .where(and(inArray(cache.key, keys), this.whereNotExpired()))\n return vs.map((v: { value: any }) => v.value)\n }\n\n async set(key: any, data: any, ttl?: number): Promise<boolean> {\n data = this.serialize(data)\n const exp = this.getTTL(ttl)\n try {\n await this.db\n .insert(cache)\n .values({ key: key, value: data, expiration: exp } as MysqlCache)\n .onDuplicateKeyUpdate({\n set: {\n value: data,\n expiration: exp\n } as Partial<MysqlCache>\n })\n return true\n } catch (e) {\n this.logger.error(`${this.set.name} - ${e}`)\n return false\n }\n }\n\n async del(key: string): Promise<boolean> {\n return dbCheckAffectedRows(await this.db.delete(cache).where(eq(cache.key, key)), 1, false)\n }\n\n async mdel(keys: string[]): Promise<boolean> {\n return dbCheckAffectedRows(await this.db.delete(cache).where(inArray(cache.key, keys)), keys.length, false)\n }\n\n genSlugKey(...args: any[]): string {\n return createSlug(args.join(' '))\n }\n\n async quit(): Promise<void> {\n this.logger.verbose(`${this.quit.name}`)\n }\n\n private readonly whereNotExpired: () => SQL = () => notBetween(cache.expiration, 0, currentTimeStamp())\n\n private readonly whereExpired: () => SQL = () => between(cache.expiration, 0, currentTimeStamp())\n\n private getTTL(ttl: number): number {\n /* ttl (seconds):\n - 0 : infinite expiration\n - undefined : default ttl\n */\n return ttl ? currentTimeStamp() + ttl : ttl === 0 ? this.infiniteExpiration : currentTimeStamp() + this.defaultTTL\n }\n\n private serialize(data: any) {\n if (data === undefined) {\n // undefined values are not handled by JSON serialization\n return null\n }\n return data\n }\n\n private async clearExpiredKeys() {\n try {\n await this.db.delete(cache).where(this.whereExpired())\n } catch (e) {\n this.logger.error(`${this.clearExpiredKeys.name} - ${e?.code || e}`)\n }\n }\n}\n"],"names":["MysqlCacheAdapter","initScheduler","cluster","worker","id","db","execute","scheduledJobName","scheduledJobInterval","logger","log","e","error","sql","code","message","warn","scheduledJob","CronJob","clearExpiredKeys","scheduler","addCronJob","start","keys","pattern","ks","select","key","cache","from","where","and","like","replaceAll","whereNotExpired","map","k","has","r","exists","eq","get","v","value","limit","mget","vs","inArray","set","data","ttl","serialize","exp","getTTL","insert","values","expiration","onDuplicateKeyUpdate","name","del","dbCheckAffectedRows","delete","mdel","length","genSlugKey","args","createSlug","join","quit","verbose","currentTimeStamp","infiniteExpiration","defaultTTL","undefined","whereExpired","configuration","Logger","Cache","toUpperCase","notBetween","between","catch"],"mappings":"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"}
@@ -13,11 +13,12 @@ Object.defineProperty(exports, "cache", {
13
13
  }
14
14
  });
15
15
  const _mysqlcore = require("drizzle-orm/mysql-core");
16
+ const _columns = require("../../database/columns");
16
17
  const cache = (0, _mysqlcore.mysqlTable)('cache', {
17
18
  key: (0, _mysqlcore.varchar)('key', {
18
19
  length: 768
19
20
  }).primaryKey(),
20
- value: (0, _mysqlcore.json)('value').$type(),
21
+ value: (0, _columns.jsonColumn)()('value'),
21
22
  expiration: (0, _mysqlcore.int)('expiration').default(-1).notNull()
22
23
  }, (table)=>[
23
24
  (0, _mysqlcore.index)('expiration_idx').on(table.expiration)
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../backend/src/infrastructure/cache/schemas/mysql-cache.schema.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 { index, int, json, mysqlTable, varchar } from 'drizzle-orm/mysql-core'\n\nexport const cache = mysqlTable(\n 'cache',\n {\n key: varchar('key', { length: 768 }).primaryKey(),\n value: json('value').$type<any>(),\n expiration: int('expiration').default(-1).notNull()\n },\n (table) => [index('expiration_idx').on(table.expiration)]\n)\n"],"names":["cache","mysqlTable","key","varchar","length","primaryKey","value","json","$type","expiration","int","default","notNull","table","index","on"],"mappings":"AAAA;;;;CAIC;;;;+BAIYA;;;eAAAA;;;2BAFyC;AAE/C,MAAMA,QAAQC,IAAAA,qBAAU,EAC7B,SACA;IACEC,KAAKC,IAAAA,kBAAO,EAAC,OAAO;QAAEC,QAAQ;IAAI,GAAGC,UAAU;IAC/CC,OAAOC,IAAAA,eAAI,EAAC,SAASC,KAAK;IAC1BC,YAAYC,IAAAA,cAAG,EAAC,cAAcC,OAAO,CAAC,CAAC,GAAGC,OAAO;AACnD,GACA,CAACC,QAAU;QAACC,IAAAA,gBAAK,EAAC,kBAAkBC,EAAE,CAACF,MAAMJ,UAAU;KAAE"}
1
+ {"version":3,"sources":["../../../../../backend/src/infrastructure/cache/schemas/mysql-cache.schema.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 { index, int, mysqlTable, varchar } from 'drizzle-orm/mysql-core'\nimport { jsonColumn } from '../../database/columns'\n\nexport const cache = mysqlTable(\n 'cache',\n {\n key: varchar('key', { length: 768 }).primaryKey(),\n value: jsonColumn<any>()('value'),\n expiration: int('expiration').default(-1).notNull()\n },\n (table) => [index('expiration_idx').on(table.expiration)]\n)\n"],"names":["cache","mysqlTable","key","varchar","length","primaryKey","value","jsonColumn","expiration","int","default","notNull","table","index","on"],"mappings":"AAAA;;;;CAIC;;;;+BAKYA;;;eAAAA;;;2BAHmC;yBACrB;AAEpB,MAAMA,QAAQC,IAAAA,qBAAU,EAC7B,SACA;IACEC,KAAKC,IAAAA,kBAAO,EAAC,OAAO;QAAEC,QAAQ;IAAI,GAAGC,UAAU;IAC/CC,OAAOC,IAAAA,mBAAU,IAAQ;IACzBC,YAAYC,IAAAA,cAAG,EAAC,cAAcC,OAAO,CAAC,CAAC,GAAGC,OAAO;AACnD,GACA,CAACC,QAAU;QAACC,IAAAA,gBAAK,EAAC,kBAAkBC,EAAE,CAACF,MAAMJ,UAAU;KAAE"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../backend/src/infrastructure/cache/services/cache.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\nexport abstract class Cache {\n abstract defaultTTL: number\n\n abstract infiniteExpiration: number\n\n abstract has(key: string): Promise<boolean>\n\n /*\n pattern must use '*' as wildcard\n */\n abstract keys(pattern: string): Promise<string[]>\n\n abstract get(key: string): Promise<any>\n\n abstract mget(keys: string[]): Promise<any[]>\n\n abstract set(key: string, data: any, ttl?: number): Promise<boolean>\n\n abstract del(key: string): Promise<boolean>\n\n abstract mdel(keys: string[]): Promise<boolean>\n\n abstract genSlugKey(...args: any[]): string\n\n abstract quit(): Promise<void>\n}\n"],"names":["Cache"],"mappings":"AAAA;;;;CAIC;;;;+BAEqBA;;;eAAAA;;;AAAf,IAAA,AAAeA,QAAf,MAAeA;AAyBtB"}
1
+ {"version":3,"sources":["../../../../../backend/src/infrastructure/cache/services/cache.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\nexport abstract class Cache {\n abstract defaultTTL: number\n\n abstract infiniteExpiration: number\n\n abstract has(key: string): Promise<boolean>\n\n /*\n pattern must use '*' as wildcard\n */\n abstract keys(pattern: string): Promise<string[]>\n\n abstract get(key: string): Promise<any>\n\n abstract mget(keys: string[]): Promise<any[]>\n\n /* ttl (seconds):\n - 0: infinite expiration\n - undefined: default ttl\n */\n abstract set(key: string, data: any, ttl?: number): Promise<boolean>\n\n abstract del(key: string): Promise<boolean>\n\n abstract mdel(keys: string[]): Promise<boolean>\n\n abstract genSlugKey(...args: any[]): string\n\n abstract quit(): Promise<void>\n}\n"],"names":["Cache"],"mappings":"AAAA;;;;CAIC;;;;+BAEqBA;;;eAAAA;;;AAAf,IAAA,AAAeA,QAAf,MAAeA;AA6BtB"}
@@ -0,0 +1,39 @@
1
+ /*
2
+ * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>
3
+ * This file is part of Sync-in | The open source file sync and share solution
4
+ * See the LICENSE file for licensing details
5
+ */ "use strict";
6
+ Object.defineProperty(exports, "__esModule", {
7
+ value: true
8
+ });
9
+ Object.defineProperty(exports, "jsonColumn", {
10
+ enumerable: true,
11
+ get: function() {
12
+ return jsonColumn;
13
+ }
14
+ });
15
+ const _mysqlcore = require("drizzle-orm/mysql-core");
16
+ const jsonColumn = ()=>(0, _mysqlcore.customType)({
17
+ dataType () {
18
+ // MariaDB will store in LONGTEXT with JSON constraint, but "json" remains correct on the DDL side
19
+ return 'json';
20
+ },
21
+ toDriver (value) {
22
+ return value == null ? null : JSON.stringify(value);
23
+ },
24
+ fromDriver (value) {
25
+ if (value == null) return null;
26
+ if (typeof value === 'string') {
27
+ try {
28
+ return JSON.parse(value);
29
+ } catch {
30
+ // Corrupt or non-JSON value: returns null (or throws if you prefer)
31
+ return null;
32
+ }
33
+ }
34
+ // In the (rare) case where the driver already returns an object
35
+ return value;
36
+ }
37
+ });
38
+
39
+ //# sourceMappingURL=columns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../backend/src/infrastructure/database/columns.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 { customType } from 'drizzle-orm/mysql-core'\n\nexport const jsonColumn = <T>() =>\n customType<{ data: T; driverData: string | null }>({\n dataType() {\n // MariaDB will store in LONGTEXT with JSON constraint, but \"json\" remains correct on the DDL side\n return 'json'\n },\n toDriver(value) {\n return value == null ? null : JSON.stringify(value)\n },\n fromDriver(value) {\n if (value == null) return null as unknown as T\n if (typeof value === 'string') {\n try {\n return JSON.parse(value) as T\n } catch {\n // Corrupt or non-JSON value: returns null (or throws if you prefer)\n return null as unknown as T\n }\n }\n // In the (rare) case where the driver already returns an object\n return value as unknown as T\n }\n })\n"],"names":["jsonColumn","customType","dataType","toDriver","value","JSON","stringify","fromDriver","parse"],"mappings":"AAAA;;;;CAIC;;;;+BAIYA;;;eAAAA;;;2BAFc;AAEpB,MAAMA,aAAa,IACxBC,IAAAA,qBAAU,EAAyC;QACjDC;YACE,kGAAkG;YAClG,OAAO;QACT;QACAC,UAASC,KAAK;YACZ,OAAOA,SAAS,OAAO,OAAOC,KAAKC,SAAS,CAACF;QAC/C;QACAG,YAAWH,KAAK;YACd,IAAIA,SAAS,MAAM,OAAO;YAC1B,IAAI,OAAOA,UAAU,UAAU;gBAC7B,IAAI;oBACF,OAAOC,KAAKG,KAAK,CAACJ;gBACpB,EAAE,OAAM;oBACN,oEAAoE;oBACpE,OAAO;gBACT;YACF;YACA,gEAAgE;YAChE,OAAOA;QACT;IACF"}
@@ -30,7 +30,6 @@ let MySQLConfig = class MySQLConfig {
30
30
  }
31
31
  };
32
32
  _ts_decorate([
33
- (0, _classvalidator.IsDefined)(),
34
33
  (0, _classvalidator.IsString)(),
35
34
  (0, _classvalidator.IsNotEmpty)(),
36
35
  (0, _classtransformer.Transform)(({ value })=>value.endsWith(`?charset=${_constants.DB_CHARSET}`) ? value : `${value}?charset=${_constants.DB_CHARSET}`),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../backend/src/infrastructure/database/database.config.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 { Transform } from 'class-transformer'\nimport { IsBoolean, IsDefined, IsNotEmpty, IsString } from 'class-validator'\nimport { DB_CHARSET } from './constants'\n\nexport class MySQLConfig {\n @IsDefined()\n @IsString()\n @IsNotEmpty()\n @Transform(({ value }) => (value.endsWith(`?charset=${DB_CHARSET}`) ? value : `${value}?charset=${DB_CHARSET}`))\n url: string\n\n @IsBoolean()\n logQueries: boolean = false\n}\n"],"names":["MySQLConfig","logQueries","value","endsWith","DB_CHARSET"],"mappings":"AAAA;;;;CAIC;;;;+BAMYA;;;eAAAA;;;kCAJa;gCACiC;2BAChC;;;;;;;;;;AAEpB,IAAA,AAAMA,cAAN,MAAMA;;aAQXC,aAAsB;;AACxB;;;;;sCALc,EAAEC,KAAK,EAAE,GAAMA,MAAMC,QAAQ,CAAC,CAAC,SAAS,EAAEC,qBAAU,EAAE,IAAIF,QAAQ,GAAGA,MAAM,SAAS,EAAEE,qBAAU,EAAE"}
1
+ {"version":3,"sources":["../../../../backend/src/infrastructure/database/database.config.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 { Transform } from 'class-transformer'\nimport { IsBoolean, IsNotEmpty, IsString } from 'class-validator'\nimport { DB_CHARSET } from './constants'\n\nexport class MySQLConfig {\n @IsString()\n @IsNotEmpty()\n @Transform(({ value }) => (value.endsWith(`?charset=${DB_CHARSET}`) ? value : `${value}?charset=${DB_CHARSET}`))\n url: string\n\n @IsBoolean()\n logQueries: boolean = false\n}\n"],"names":["MySQLConfig","logQueries","value","endsWith","DB_CHARSET"],"mappings":"AAAA;;;;CAIC;;;;+BAMYA;;;eAAAA;;;kCAJa;gCACsB;2BACrB;;;;;;;;;;AAEpB,IAAA,AAAMA,cAAN,MAAMA;;aAOXC,aAAsB;;AACxB;;;;sCALc,EAAEC,KAAK,EAAE,GAAMA,MAAMC,QAAQ,CAAC,CAAC,SAAS,EAAEC,qBAAU,EAAE,IAAIF,QAAQ,GAAGA,MAAM,SAAS,EAAEE,qBAAU,EAAE"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../backend/src/infrastructure/mailer/interfaces/mail.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 MailTransport {\n host: string\n port: number\n secure: boolean\n auth: {\n user: string\n pass: string\n }\n debug?: boolean\n logger?: any\n}\n\nexport interface MailDefaultsTransport {\n from: string\n tls: {\n rejectUnauthorized: boolean\n }\n}\n\nexport interface MailProps {\n to: string\n subject: string\n html: string\n}\n"],"names":[],"mappings":"AAAA;;;;CAIC"}
1
+ {"version":3,"sources":["../../../../../backend/src/infrastructure/mailer/interfaces/mail.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 MailTransport {\n host: string\n port: number\n secure: boolean\n ignoreTLS: boolean\n auth: {\n user: string\n pass: string\n }\n debug?: boolean\n logger?: any\n}\n\nexport interface MailDefaultsTransport {\n from: string\n tls: {\n rejectUnauthorized: boolean\n }\n}\n\nexport interface MailProps {\n to: string\n subject: string\n html: string\n}\n"],"names":[],"mappings":"AAAA;;;;CAIC"}
@@ -47,6 +47,8 @@ let MailerConfig = class MailerConfig {
47
47
  constructor(){
48
48
  this.port = 25;
49
49
  this.secure = false;
50
+ this.ignoreTLS = false;
51
+ this.rejectUnauthorized = false;
50
52
  this.sender = 'Sync-in<notification@sync-in.com>';
51
53
  this.debug = false;
52
54
  this.logger = false;
@@ -68,6 +70,16 @@ _ts_decorate([
68
70
  (0, _classvalidator.IsBoolean)(),
69
71
  _ts_metadata("design:type", Boolean)
70
72
  ], MailerConfig.prototype, "secure", void 0);
73
+ _ts_decorate([
74
+ (0, _classvalidator.IsOptional)(),
75
+ (0, _classvalidator.IsBoolean)(),
76
+ _ts_metadata("design:type", Boolean)
77
+ ], MailerConfig.prototype, "ignoreTLS", void 0);
78
+ _ts_decorate([
79
+ (0, _classvalidator.IsOptional)(),
80
+ (0, _classvalidator.IsBoolean)(),
81
+ _ts_metadata("design:type", Boolean)
82
+ ], MailerConfig.prototype, "rejectUnauthorized", void 0);
71
83
  _ts_decorate([
72
84
  (0, _classvalidator.IsOptional)(),
73
85
  (0, _classvalidator.IsObject)(),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../backend/src/infrastructure/mailer/mailer.config.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 } from 'class-transformer'\nimport { IsBoolean, IsInt, IsNotEmpty, IsObject, IsOptional, IsString, Max, Min, ValidateNested } from 'class-validator'\n\nexport class AuthMailConfig {\n @IsString()\n @IsNotEmpty()\n user: string\n\n @IsString()\n @IsNotEmpty()\n pass: string\n}\n\nexport class MailerConfig {\n @IsString()\n @IsNotEmpty()\n host: string\n\n @IsInt()\n @Min(0)\n @Max(65535)\n port: number = 25\n\n @IsOptional()\n @IsBoolean()\n secure?: boolean = false\n\n @IsOptional()\n @IsObject()\n @ValidateNested()\n @Type(() => AuthMailConfig)\n auth?: AuthMailConfig\n\n @IsOptional()\n @IsString()\n sender?: string = 'Sync-in<notification@sync-in.com>'\n\n @IsOptional()\n @IsBoolean()\n debug?: boolean = false\n\n @IsOptional()\n @IsBoolean()\n logger?: boolean = false\n}\n"],"names":["AuthMailConfig","MailerConfig","port","secure","sender","debug","logger"],"mappings":"AAAA;;;;CAIC;;;;;;;;;;;QAKYA;eAAAA;;QAUAC;eAAAA;;;kCAbQ;gCACkF;;;;;;;;;;AAEhG,IAAA,AAAMD,iBAAN,MAAMA;AAQb;;;;;;;;;;;AAEO,IAAA,AAAMC,eAAN,MAAMA;;aAQXC,OAAe;aAIfC,SAAmB;aAUnBC,SAAkB;aAIlBC,QAAkB;aAIlBC,SAAmB;;AACrB;;;;;;;;;;;;;;;;;;;;;oCAdcN"}
1
+ {"version":3,"sources":["../../../../backend/src/infrastructure/mailer/mailer.config.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 } from 'class-transformer'\nimport { IsBoolean, IsInt, IsNotEmpty, IsObject, IsOptional, IsString, Max, Min, ValidateNested } from 'class-validator'\n\nexport class AuthMailConfig {\n @IsString()\n @IsNotEmpty()\n user: string\n\n @IsString()\n @IsNotEmpty()\n pass: string\n}\n\nexport class MailerConfig {\n @IsString()\n @IsNotEmpty()\n host: string\n\n @IsInt()\n @Min(0)\n @Max(65535)\n port: number = 25\n\n @IsOptional()\n @IsBoolean()\n secure?: boolean = false\n\n @IsOptional()\n @IsBoolean()\n ignoreTLS?: boolean = false\n\n @IsOptional()\n @IsBoolean()\n rejectUnauthorized?: boolean = false\n\n @IsOptional()\n @IsObject()\n @ValidateNested()\n @Type(() => AuthMailConfig)\n auth?: AuthMailConfig\n\n @IsOptional()\n @IsString()\n sender?: string = 'Sync-in<notification@sync-in.com>'\n\n @IsOptional()\n @IsBoolean()\n debug?: boolean = false\n\n @IsOptional()\n @IsBoolean()\n logger?: boolean = false\n}\n"],"names":["AuthMailConfig","MailerConfig","port","secure","ignoreTLS","rejectUnauthorized","sender","debug","logger"],"mappings":"AAAA;;;;CAIC;;;;;;;;;;;QAKYA;eAAAA;;QAUAC;eAAAA;;;kCAbQ;gCACkF;;;;;;;;;;AAEhG,IAAA,AAAMD,iBAAN,MAAMA;AAQb;;;;;;;;;;;AAEO,IAAA,AAAMC,eAAN,MAAMA;;aAQXC,OAAe;aAIfC,SAAmB;aAInBC,YAAsB;aAItBC,qBAA+B;aAU/BC,SAAkB;aAIlBC,QAAkB;aAIlBC,SAAmB;;AACrB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oCAdcR"}
@@ -72,11 +72,12 @@ let Mailer = class Mailer {
72
72
  port: this.configuration.port,
73
73
  auth: this.configuration.auth,
74
74
  secure: this.configuration.secure,
75
+ ignoreTLS: this.configuration.ignoreTLS,
75
76
  logger: this.configuration.logger ? this.logger : false
76
77
  }, {
77
78
  from: this.configuration.sender,
78
79
  tls: {
79
- rejectUnauthorized: false
80
+ rejectUnauthorized: this.configuration.rejectUnauthorized
80
81
  }
81
82
  });
82
83
  this.verify().catch(this.logger.error);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../backend/src/infrastructure/mailer/mailer.service.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { Injectable } from '@nestjs/common'\nimport { ConfigService } from '@nestjs/config'\nimport { PinoLogger } from 'nestjs-pino'\nimport nodemailer from 'nodemailer'\nimport type { Logger as NodeMailLogger } from 'nodemailer/lib/shared'\nimport { MailDefaultsTransport, MailProps, MailTransport } from './interfaces/mail.interface'\nimport { MailerConfig } from './mailer.config'\n\n@Injectable()\nexport class Mailer {\n public available: boolean = false\n private readonly transporter: nodemailer.Transporter\n private readonly configuration: MailerConfig\n\n constructor(\n private configService: ConfigService,\n private readonly logger: PinoLogger\n ) {\n this.logger.setContext(Mailer.name.toUpperCase())\n this.configuration = this.configService.get<MailerConfig>('mail')\n if (!this.configuration) {\n return\n }\n this.logger.logger.level = this.configuration.debug ? 'debug' : 'info'\n if (this.configuration.secure && (this.configuration.port === 587 || this.configuration.port === 25)) {\n this.logger.warn(`Secure transport has been disabled due to use of port : ${this.configuration.port}`)\n this.configuration.secure = false\n }\n this.transporter = nodemailer.createTransport(\n {\n host: this.configuration.host,\n port: this.configuration.port,\n auth: this.configuration.auth,\n secure: this.configuration.secure,\n logger: this.configuration.logger ? (this.logger as NodeMailLogger & any) : false\n } satisfies MailTransport,\n { from: this.configuration.sender, tls: { rejectUnauthorized: false } } satisfies MailDefaultsTransport\n )\n this.verify().catch(this.logger.error)\n }\n\n async sendMails(mails: MailProps[]) {\n if (!this.available) {\n return\n }\n for (const m of mails) {\n this.transporter\n .sendMail(m)\n .then(() => {\n this.logger.info(`Mail sent to '${m.to}' with subject '${m.subject}'`)\n })\n .catch((e) => {\n this.logger.error(`Mail was not sent to '${m.to}' with subject '${m.subject}' : ${e}`)\n })\n }\n }\n\n private async verify(): Promise<void> {\n try {\n await this.transporter.verify()\n this.logger.info(`Using Mail Server at ${this.configuration.host}:${this.configuration.port} (secure: ${this.configuration.secure})`)\n this.available = true\n } catch (e) {\n this.logger.error(\n `Unable to use Mail Server at ${this.configuration.host}:${this.configuration.port} (secure: ${this.configuration.secure}) : ${e}`\n )\n this.available = false\n }\n }\n}\n"],"names":["Mailer","sendMails","mails","available","m","transporter","sendMail","then","logger","info","to","subject","catch","e","error","verify","configuration","host","port","secure","configService","setContext","name","toUpperCase","get","level","debug","warn","nodemailer","createTransport","auth","from","sender","tls","rejectUnauthorized"],"mappings":"AAAA;;;;CAIC;;;;+BAWYA;;;eAAAA;;;wBATc;wBACG;4BACH;mEACJ;;;;;;;;;;;;;;;AAMhB,IAAA,AAAMA,SAAN,MAAMA;IAgCX,MAAMC,UAAUC,KAAkB,EAAE;QAClC,IAAI,CAAC,IAAI,CAACC,SAAS,EAAE;YACnB;QACF;QACA,KAAK,MAAMC,KAAKF,MAAO;YACrB,IAAI,CAACG,WAAW,CACbC,QAAQ,CAACF,GACTG,IAAI,CAAC;gBACJ,IAAI,CAACC,MAAM,CAACC,IAAI,CAAC,CAAC,cAAc,EAAEL,EAAEM,EAAE,CAAC,gBAAgB,EAAEN,EAAEO,OAAO,CAAC,CAAC,CAAC;YACvE,GACCC,KAAK,CAAC,CAACC;gBACN,IAAI,CAACL,MAAM,CAACM,KAAK,CAAC,CAAC,sBAAsB,EAAEV,EAAEM,EAAE,CAAC,gBAAgB,EAAEN,EAAEO,OAAO,CAAC,IAAI,EAAEE,GAAG;YACvF;QACJ;IACF;IAEA,MAAcE,SAAwB;QACpC,IAAI;YACF,MAAM,IAAI,CAACV,WAAW,CAACU,MAAM;YAC7B,IAAI,CAACP,MAAM,CAACC,IAAI,CAAC,CAAC,qBAAqB,EAAE,IAAI,CAACO,aAAa,CAACC,IAAI,CAAC,CAAC,EAAE,IAAI,CAACD,aAAa,CAACE,IAAI,CAAC,UAAU,EAAE,IAAI,CAACF,aAAa,CAACG,MAAM,CAAC,CAAC,CAAC;YACpI,IAAI,CAAChB,SAAS,GAAG;QACnB,EAAE,OAAOU,GAAG;YACV,IAAI,CAACL,MAAM,CAACM,KAAK,CACf,CAAC,6BAA6B,EAAE,IAAI,CAACE,aAAa,CAACC,IAAI,CAAC,CAAC,EAAE,IAAI,CAACD,aAAa,CAACE,IAAI,CAAC,UAAU,EAAE,IAAI,CAACF,aAAa,CAACG,MAAM,CAAC,IAAI,EAAEN,GAAG;YAEpI,IAAI,CAACV,SAAS,GAAG;QACnB;IACF;IAtDA,YACE,AAAQiB,aAA4B,EACpC,AAAiBZ,MAAkB,CACnC;aAFQY,gBAAAA;aACSZ,SAAAA;aANZL,YAAqB;QAQ1B,IAAI,CAACK,MAAM,CAACa,UAAU,CAACrB,OAAOsB,IAAI,CAACC,WAAW;QAC9C,IAAI,CAACP,aAAa,GAAG,IAAI,CAACI,aAAa,CAACI,GAAG,CAAe;QAC1D,IAAI,CAAC,IAAI,CAACR,aAAa,EAAE;YACvB;QACF;QACA,IAAI,CAACR,MAAM,CAACA,MAAM,CAACiB,KAAK,GAAG,IAAI,CAACT,aAAa,CAACU,KAAK,GAAG,UAAU;QAChE,IAAI,IAAI,CAACV,aAAa,CAACG,MAAM,IAAK,CAAA,IAAI,CAACH,aAAa,CAACE,IAAI,KAAK,OAAO,IAAI,CAACF,aAAa,CAACE,IAAI,KAAK,EAAC,GAAI;YACpG,IAAI,CAACV,MAAM,CAACmB,IAAI,CAAC,CAAC,wDAAwD,EAAE,IAAI,CAACX,aAAa,CAACE,IAAI,EAAE;YACrG,IAAI,CAACF,aAAa,CAACG,MAAM,GAAG;QAC9B;QACA,IAAI,CAACd,WAAW,GAAGuB,mBAAU,CAACC,eAAe,CAC3C;YACEZ,MAAM,IAAI,CAACD,aAAa,CAACC,IAAI;YAC7BC,MAAM,IAAI,CAACF,aAAa,CAACE,IAAI;YAC7BY,MAAM,IAAI,CAACd,aAAa,CAACc,IAAI;YAC7BX,QAAQ,IAAI,CAACH,aAAa,CAACG,MAAM;YACjCX,QAAQ,IAAI,CAACQ,aAAa,CAACR,MAAM,GAAI,IAAI,CAACA,MAAM,GAA4B;QAC9E,GACA;YAAEuB,MAAM,IAAI,CAACf,aAAa,CAACgB,MAAM;YAAEC,KAAK;gBAAEC,oBAAoB;YAAM;QAAE;QAExE,IAAI,CAACnB,MAAM,GAAGH,KAAK,CAAC,IAAI,CAACJ,MAAM,CAACM,KAAK;IACvC;AA8BF"}
1
+ {"version":3,"sources":["../../../../backend/src/infrastructure/mailer/mailer.service.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { Injectable } from '@nestjs/common'\nimport { ConfigService } from '@nestjs/config'\nimport { PinoLogger } from 'nestjs-pino'\nimport nodemailer from 'nodemailer'\nimport type { Logger as NodeMailLogger } from 'nodemailer/lib/shared'\nimport { MailDefaultsTransport, MailProps, MailTransport } from './interfaces/mail.interface'\nimport { MailerConfig } from './mailer.config'\n\n@Injectable()\nexport class Mailer {\n public available: boolean = false\n private readonly transporter: nodemailer.Transporter\n private readonly configuration: MailerConfig\n\n constructor(\n private configService: ConfigService,\n private readonly logger: PinoLogger\n ) {\n this.logger.setContext(Mailer.name.toUpperCase())\n this.configuration = this.configService.get<MailerConfig>('mail')\n if (!this.configuration) {\n return\n }\n this.logger.logger.level = this.configuration.debug ? 'debug' : 'info'\n if (this.configuration.secure && (this.configuration.port === 587 || this.configuration.port === 25)) {\n this.logger.warn(`Secure transport has been disabled due to use of port : ${this.configuration.port}`)\n this.configuration.secure = false\n }\n this.transporter = nodemailer.createTransport(\n {\n host: this.configuration.host,\n port: this.configuration.port,\n auth: this.configuration.auth,\n secure: this.configuration.secure,\n ignoreTLS: this.configuration.ignoreTLS,\n logger: this.configuration.logger ? (this.logger as NodeMailLogger & any) : false\n } satisfies MailTransport,\n { from: this.configuration.sender, tls: { rejectUnauthorized: this.configuration.rejectUnauthorized } } satisfies MailDefaultsTransport\n )\n this.verify().catch(this.logger.error)\n }\n\n async sendMails(mails: MailProps[]) {\n if (!this.available) {\n return\n }\n for (const m of mails) {\n this.transporter\n .sendMail(m)\n .then(() => {\n this.logger.info(`Mail sent to '${m.to}' with subject '${m.subject}'`)\n })\n .catch((e) => {\n this.logger.error(`Mail was not sent to '${m.to}' with subject '${m.subject}' : ${e}`)\n })\n }\n }\n\n private async verify(): Promise<void> {\n try {\n await this.transporter.verify()\n this.logger.info(`Using Mail Server at ${this.configuration.host}:${this.configuration.port} (secure: ${this.configuration.secure})`)\n this.available = true\n } catch (e) {\n this.logger.error(\n `Unable to use Mail Server at ${this.configuration.host}:${this.configuration.port} (secure: ${this.configuration.secure}) : ${e}`\n )\n this.available = false\n }\n }\n}\n"],"names":["Mailer","sendMails","mails","available","m","transporter","sendMail","then","logger","info","to","subject","catch","e","error","verify","configuration","host","port","secure","configService","setContext","name","toUpperCase","get","level","debug","warn","nodemailer","createTransport","auth","ignoreTLS","from","sender","tls","rejectUnauthorized"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><title>file_type_c3</title><path d="M29,10.232a2.387,2.387,0,0,0-.318-1.244,2.451,2.451,0,0,0-.936-.879Q22.552,5.241,17.353,2.376A2.642,2.642,0,0,0,14.59,2.4c-1.378.779-8.275,4.565-10.331,5.706A2.287,2.287,0,0,0,3,10.231V21.77a2.4,2.4,0,0,0,.3,1.22,2.434,2.434,0,0,0,.954.9c2.056,1.141,8.954,4.927,10.332,5.706a2.642,2.642,0,0,0,2.763.026q5.19-2.871,10.386-5.733a2.444,2.444,0,0,0,.955-.9,2.4,2.4,0,0,0,.3-1.22V10.232" style="fill:#a9b9cb"/><path d="M28.549,23.171a2.126,2.126,0,0,0,.147-.182,2.4,2.4,0,0,0,.3-1.22V10.232a2.387,2.387,0,0,0-.318-1.244c-.036-.059-.089-.105-.13-.16L16,16Z" style="fill:#8b97a3"/><path d="M28.549,23.171,16,16,3.451,23.171a2.435,2.435,0,0,0,.809.72c2.056,1.141,8.954,4.927,10.332,5.706a2.642,2.642,0,0,0,2.763.026q5.19-2.871,10.386-5.733A2.43,2.43,0,0,0,28.549,23.171Z" style="fill:#7f8b99"/><path d="M19.6,18.02a4.121,4.121,0,1,1-.027-4.087l3.615-2.073A8.309,8.309,0,0,0,7.7,16a8.216,8.216,0,0,0,1.1,4.117A8.319,8.319,0,0,0,23.211,20.1L19.6,18.02" style="fill:#fff"/></svg>