@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.
- package/CHANGELOG.md +20 -0
- package/README.md +1 -0
- package/environment/environment.dist.min.yaml +1 -0
- package/environment/environment.dist.yaml +88 -30
- package/migrations/0002_sleepy_korath.sql +1 -0
- package/migrations/meta/0002_snapshot.json +2424 -0
- package/migrations/meta/_journal.json +7 -0
- package/package.json +6 -4
- package/server/app.bootstrap.js +1 -1
- package/server/app.bootstrap.js.map +1 -1
- package/server/applications/files/services/files-manager.service.js +1 -2
- package/server/applications/files/services/files-manager.service.js.map +1 -1
- package/server/applications/files/services/files-only-office-manager.service.js +5 -6
- package/server/applications/files/services/files-only-office-manager.service.js.map +1 -1
- package/server/applications/files/utils/files.js +6 -4
- package/server/applications/files/utils/files.js.map +1 -1
- package/server/applications/links/links.controller.js +2 -2
- package/server/applications/links/links.controller.js.map +1 -1
- package/server/applications/links/services/links-manager.service.js +2 -1
- package/server/applications/links/services/links-manager.service.js.map +1 -1
- package/server/applications/links/services/links-manager.service.spec.js +6 -3
- package/server/applications/links/services/links-manager.service.spec.js.map +1 -1
- package/server/applications/notifications/constants/notifications.js +9 -0
- package/server/applications/notifications/constants/notifications.js.map +1 -1
- package/server/applications/notifications/i18n/fr.js +10 -1
- package/server/applications/notifications/i18n/fr.js.map +1 -1
- package/server/applications/notifications/interfaces/notification-properties.interface.js.map +1 -1
- package/server/applications/notifications/mails/models.js +41 -3
- package/server/applications/notifications/mails/models.js.map +1 -1
- package/server/applications/notifications/mails/templates.js +1 -1
- package/server/applications/notifications/mails/templates.js.map +1 -1
- package/server/applications/notifications/schemas/notifications.schema.js +2 -1
- package/server/applications/notifications/schemas/notifications.schema.js.map +1 -1
- package/server/applications/notifications/services/notifications-manager.service.js +16 -13
- package/server/applications/notifications/services/notifications-manager.service.js.map +1 -1
- package/server/applications/notifications/services/notifications-manager.service.spec.js +9 -8
- package/server/applications/notifications/services/notifications-manager.service.spec.js.map +1 -1
- package/server/applications/notifications/services/notifications-queries.service.js +1 -1
- package/server/applications/notifications/services/notifications-queries.service.js.map +1 -1
- package/server/applications/shares/services/shares-manager.service.js +3 -2
- package/server/applications/shares/services/shares-manager.service.js.map +1 -1
- package/server/applications/sync/constants/auth.js +2 -2
- package/server/applications/sync/constants/auth.js.map +1 -1
- package/server/applications/sync/dtos/sync-client-registration.dto.js +5 -0
- package/server/applications/sync/dtos/sync-client-registration.dto.js.map +1 -1
- package/server/applications/sync/dtos/sync-operations.dto.js +1 -2
- package/server/applications/sync/dtos/sync-operations.dto.js.map +1 -1
- package/server/applications/sync/schemas/sync-clients.schema.js +2 -1
- package/server/applications/sync/schemas/sync-clients.schema.js.map +1 -1
- package/server/applications/sync/schemas/sync-paths.schema.js +2 -1
- package/server/applications/sync/schemas/sync-paths.schema.js.map +1 -1
- package/server/applications/sync/services/sync-clients-manager.service.js +28 -20
- package/server/applications/sync/services/sync-clients-manager.service.js.map +1 -1
- package/server/applications/sync/services/sync-clients-manager.service.spec.js +24 -18
- package/server/applications/sync/services/sync-clients-manager.service.spec.js.map +1 -1
- package/server/applications/sync/services/sync-queries.service.js +5 -5
- package/server/applications/sync/services/sync-queries.service.js.map +1 -1
- package/server/applications/users/admin-users.controller.js +48 -37
- package/server/applications/users/admin-users.controller.js.map +1 -1
- package/server/applications/users/admin-users.controller.spec.js +15 -0
- package/server/applications/users/admin-users.controller.spec.js.map +1 -1
- package/server/applications/users/constants/routes.js +5 -0
- package/server/applications/users/constants/routes.js.map +1 -1
- package/server/applications/users/constants/user.js +8 -0
- package/server/applications/users/constants/user.js.map +1 -1
- package/server/applications/users/dto/delete-user.dto.js +5 -23
- package/server/applications/users/dto/delete-user.dto.js.map +1 -1
- package/server/applications/users/dto/user-properties.dto.js +38 -3
- package/server/applications/users/dto/user-properties.dto.js.map +1 -1
- package/server/applications/users/interfaces/admin-user.interface.js.map +1 -1
- package/server/applications/users/interfaces/user-secrets.interface.js +10 -0
- package/server/applications/users/interfaces/user-secrets.interface.js.map +1 -0
- package/server/applications/users/models/user.model.js +84 -50
- package/server/applications/users/models/user.model.js.map +1 -1
- package/server/applications/users/schemas/user.interface.js.map +1 -1
- package/server/applications/users/schemas/users.schema.js +2 -0
- package/server/applications/users/schemas/users.schema.js.map +1 -1
- package/server/applications/users/services/admin-users-manager.service.js +7 -19
- package/server/applications/users/services/admin-users-manager.service.js.map +1 -1
- package/server/applications/users/services/admin-users-manager.service.spec.js +7 -26
- package/server/applications/users/services/admin-users-manager.service.spec.js.map +1 -1
- package/server/applications/users/services/admin-users-queries.service.js +1 -0
- package/server/applications/users/services/admin-users-queries.service.js.map +1 -1
- package/server/applications/users/services/users-manager.service.js +138 -28
- package/server/applications/users/services/users-manager.service.js.map +1 -1
- package/server/applications/users/services/users-manager.service.spec.js +11 -9
- package/server/applications/users/services/users-manager.service.spec.js.map +1 -1
- package/server/applications/users/services/users-queries.service.js +63 -57
- package/server/applications/users/services/users-queries.service.js.map +1 -1
- package/server/applications/users/users.controller.js +48 -1
- package/server/applications/users/users.controller.js.map +1 -1
- package/server/applications/users/users.controller.spec.js +8 -1
- package/server/applications/users/users.controller.spec.js.map +1 -1
- package/server/applications/users/users.e2e-spec.js +2 -1
- package/server/applications/users/users.e2e-spec.js.map +1 -1
- package/server/applications/users/utils/avatar.js +48 -0
- package/server/applications/users/utils/avatar.js.map +1 -0
- package/server/authentication/auth.config.js +85 -26
- package/server/authentication/auth.config.js.map +1 -1
- package/server/authentication/auth.controller.js +117 -9
- package/server/authentication/auth.controller.js.map +1 -1
- package/server/authentication/auth.controller.spec.js +16 -1
- package/server/authentication/auth.controller.spec.js.map +1 -1
- package/server/authentication/auth.e2e-spec.js +4 -3
- package/server/authentication/auth.e2e-spec.js.map +1 -1
- package/server/authentication/auth.module.js +4 -1
- package/server/authentication/auth.module.js.map +1 -1
- package/server/authentication/constants/auth.js +37 -4
- package/server/authentication/constants/auth.js.map +1 -1
- package/server/authentication/constants/routes.js +21 -0
- package/server/authentication/constants/routes.js.map +1 -1
- package/server/authentication/constants/scope.js +20 -0
- package/server/authentication/constants/scope.js.map +1 -0
- package/server/authentication/dto/login-response.dto.js +27 -4
- package/server/authentication/dto/login-response.dto.js.map +1 -1
- package/server/authentication/dto/token-response.dto.js +5 -0
- package/server/authentication/dto/token-response.dto.js.map +1 -1
- package/server/{applications/users/dto/user-password.dto.js → authentication/dto/two-fa-verify.dto.js} +27 -9
- package/server/authentication/dto/two-fa-verify.dto.js.map +1 -0
- package/server/authentication/guards/auth-basic.strategy.js +6 -5
- package/server/authentication/guards/auth-basic.strategy.js.map +1 -1
- package/server/authentication/guards/auth-token-access.strategy.js +3 -2
- package/server/authentication/guards/auth-token-access.strategy.js.map +1 -1
- package/server/authentication/guards/auth-token-refresh.strategy.js +3 -2
- package/server/authentication/guards/auth-token-refresh.strategy.js.map +1 -1
- package/server/authentication/guards/auth-two-fa-guard.js +81 -0
- package/server/authentication/guards/auth-two-fa-guard.js.map +1 -0
- package/server/authentication/interfaces/jwt-payload.interface.js +5 -0
- package/server/authentication/interfaces/jwt-payload.interface.js.map +1 -1
- package/server/authentication/interfaces/token.interface.js +2 -0
- package/server/authentication/interfaces/token.interface.js.map +1 -1
- package/server/authentication/interfaces/two-fa-setup.interface.js +10 -0
- package/server/authentication/interfaces/two-fa-setup.interface.js.map +1 -0
- package/server/authentication/models/auth-method.js.map +1 -1
- package/server/authentication/services/auth-manager.service.js +72 -49
- package/server/authentication/services/auth-manager.service.js.map +1 -1
- package/server/authentication/services/auth-methods/auth-method-database.service.js +3 -3
- package/server/authentication/services/auth-methods/auth-method-database.service.js.map +1 -1
- package/server/authentication/services/auth-methods/auth-method-database.service.spec.js +5 -0
- package/server/authentication/services/auth-methods/auth-method-database.service.spec.js.map +1 -1
- package/server/authentication/services/auth-methods/auth-method-ldap.service.js +100 -27
- package/server/authentication/services/auth-methods/auth-method-ldap.service.js.map +1 -1
- package/server/authentication/services/auth-methods/auth-method-ldap.service.spec.js +11 -12
- package/server/authentication/services/auth-methods/auth-method-ldap.service.spec.js.map +1 -1
- package/server/authentication/services/auth-methods/auth-method-two-fa.service.js +251 -0
- package/server/authentication/services/auth-methods/auth-method-two-fa.service.js.map +1 -0
- package/server/authentication/services/auth-methods/auth-method-two-fa.service.spec.js +41 -0
- package/server/authentication/services/auth-methods/auth-method-two-fa.service.spec.js.map +1 -0
- package/server/authentication/utils/crypt-secret.js +68 -0
- package/server/authentication/utils/crypt-secret.js.map +1 -0
- package/server/common/functions.js +18 -2
- package/server/common/functions.js.map +1 -1
- package/server/common/qrcode.js +34 -0
- package/server/common/qrcode.js.map +1 -0
- package/server/common/shared.js +18 -0
- package/server/common/shared.js.map +1 -1
- package/server/configuration/config.environment.js +23 -6
- package/server/configuration/config.environment.js.map +1 -1
- package/server/configuration/config.interfaces.js +10 -0
- package/server/configuration/config.interfaces.js.map +1 -0
- package/server/configuration/config.loader.js.map +1 -1
- package/server/configuration/config.validation.js +13 -13
- package/server/configuration/config.validation.js.map +1 -1
- package/server/infrastructure/cache/adapters/mysql-cache.adapter.js +6 -6
- package/server/infrastructure/cache/adapters/mysql-cache.adapter.js.map +1 -1
- package/server/infrastructure/cache/schemas/mysql-cache.schema.js +2 -1
- package/server/infrastructure/cache/schemas/mysql-cache.schema.js.map +1 -1
- package/server/infrastructure/cache/services/cache.service.js.map +1 -1
- package/server/infrastructure/database/columns.js +39 -0
- package/server/infrastructure/database/columns.js.map +1 -0
- package/server/infrastructure/database/database.config.js +0 -1
- package/server/infrastructure/database/database.config.js.map +1 -1
- package/server/infrastructure/mailer/interfaces/mail.interface.js.map +1 -1
- package/server/infrastructure/mailer/mailer.config.js +12 -0
- package/server/infrastructure/mailer/mailer.config.js.map +1 -1
- package/server/infrastructure/mailer/mailer.service.js +2 -1
- package/server/infrastructure/mailer/mailer.service.js.map +1 -1
- package/static/assets/mimes/text-x-c.svg +1 -0
- package/static/chunk-2TZUZMCM.js +4 -0
- package/static/chunk-2XJ5Z2GZ.js +1 -0
- package/static/{chunk-7VRUZRJG.js → chunk-5M4YJZUB.js} +2 -2
- package/static/{chunk-MRSWNAVB.js → chunk-5ZGQYTS2.js} +1 -1
- package/static/chunk-6BFNMDUD.js +1 -0
- package/static/chunk-6IRL673W.js +559 -0
- package/static/{chunk-2R6HHGUR.js → chunk-ABGR5AYC.js} +1 -1
- package/static/chunk-CN27VAGB.js +1 -0
- package/static/{chunk-MVO4WZLK.js → chunk-DNMO47SY.js} +1 -1
- package/static/{chunk-MGGT6MIJ.js → chunk-EI4PVI2W.js} +1 -1
- package/static/chunk-ET6QDNNM.js +1 -0
- package/static/{chunk-L6MU6S2V.js → chunk-G2TKYYWK.js} +1 -1
- package/static/chunk-G3FOG2QB.js +1 -0
- package/static/{chunk-MCLQFZ3S.js → chunk-GCUWGVYT.js} +1 -1
- package/static/{chunk-RSS6GYNE.js → chunk-HME7LAEY.js} +1 -1
- package/static/chunk-IEUANP3Q.js +1 -0
- package/static/{chunk-VJRTMDEJ.js → chunk-IIFHIIC6.js} +1 -1
- package/static/{chunk-YJMN3B4N.js → chunk-KPZ7FEMO.js} +1 -1
- package/static/{chunk-JYXLQRHG.js → chunk-M57NVD4V.js} +1 -1
- package/static/chunk-NN3VQOS7.js +1 -0
- package/static/chunk-NW3CTYUW.js +1 -0
- package/static/{chunk-6OJZWYRZ.js → chunk-O3ANXCPE.js} +1 -1
- package/static/{chunk-ZC5NIT55.js → chunk-QFOMEU3T.js} +1 -1
- package/static/{chunk-BIUNUYZ5.js → chunk-RKNTQYMU.js} +1 -1
- package/static/{chunk-VUI3KV7V.js → chunk-UQ4TRQCE.js} +1 -1
- package/static/{chunk-WI7FOANP.js → chunk-WINILGQN.js} +1 -1
- package/static/{chunk-NE4NDO45.js → chunk-X7MFVDBY.js} +1 -1
- package/static/chunk-XCBLEI2E.js +1 -0
- package/static/{chunk-CRQNEHTX.js → chunk-XLWCV4HI.js} +1 -1
- package/static/chunk-XPIYOZBX.js +4 -0
- package/static/{chunk-LLWSLOSX.js → chunk-YD74UCFG.js} +1 -1
- package/static/{chunk-IZL7JPTS.js → chunk-YDFVKH2D.js} +1 -1
- package/static/{chunk-SPTF6FSM.js → chunk-YVJDYSDE.js} +1 -1
- package/static/index.html +2 -2
- package/static/main-QNBKYA6L.js +9 -0
- package/static/{styles-FYUSO6OJ.css → styles-A5VYX3CE.css} +1 -1
- package/server/applications/users/dto/user-password.dto.js.map +0 -1
- package/static/chunk-4U5A2DEP.js +0 -4
- package/static/chunk-54EAZ2UD.js +0 -1
- package/static/chunk-7ZRXJONB.js +0 -1
- package/static/chunk-F2J2IIJE.js +0 -1
- package/static/chunk-FNFGUIQH.js +0 -4
- package/static/chunk-GGLK52CG.js +0 -1
- package/static/chunk-HW2H3ISM.js +0 -559
- package/static/chunk-HX6BBYVD.js +0 -1
- package/static/chunk-JF7S3UYQ.js +0 -1
- package/static/chunk-KSHPKI4G.js +0 -1
- package/static/chunk-VPJ2V27B.js +0 -1
- package/static/chunk-ZXS4V7J2.js +0 -1
- 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.
|
|
35
|
-
...config.auth.token.
|
|
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.
|
|
39
|
-
...config.auth.token.
|
|
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.
|
|
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 {
|
|
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 {
|
|
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":"AAAA;;;;CAIC;;;;+BAgBeA;;;eAAAA;;;kEAdM;gEACA;+DACP;iEACE;iCAQV;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGA,SAASA;IACd,IAAIC,SAAgCC,YAAYC,iCAAgB,EAAEC,sCAAqB;IAEvF,IAAIC,gBAAgB;QAClB,qFAAqF;QACrF,MAAMC,YAAYC,gBAAgBL,YAAYM,sCAAqB,EAAEC,2CAA0B,EAAE;QACjGR,SAASS,IAAAA,kBAAS,EAACT,QAAQK;IAC7B;IAEA,IAAIK,OAAOC,IAAI,CAACX,QAAQY,MAAM,KAAK,GAAG;QACpC,MAAM,IAAIC,MAAM,CAAC,wBAAwB,EAAEV,sCAAqB,CAAC,6CAA6C,EAAEW,mCAAkB,CAAC,UAAU,CAAC;IAChJ;IAEA,IAAId,OAAOe,MAAM,EAAEC,WAAW,OAAO;QACnC,8BAA8B;QAC9B,MAAMC,cAAcjB,OAAOe,MAAM,CAACG,QAAQ,IAAIC,sCAAqB;QACnE,MAAMC,aAAaC,iBAAI,CAACC,OAAO,CAACL;QAChC,IAAI,CAACM,eAAE,CAACC,UAAU,CAACJ,aAAa;YAC9BG,eAAE,CAACE,SAAS,CAACL,YAAY;gBAAEM,WAAW;YAAK;QAC7C;QACAC,QAAQC,GAAG,CAAC,CAAC,kBAAkB,EAAEX,aAAa;IAChD;IAEA,OAAOjB;AACT;AAEA,SAAS6B,aAAaC,QAAgB,EAAEC,QAAgB,EAAEC,SAAS,CAAC;IAClE,yFAAyF;IACzF,OAAOC,MAAMC,IAAI,CAAC;QAAEtB,QAAQoB,SAAS;IAAE,GAAG,CAACG,GAAGC,IAAMf,iBAAI,CAACgB,OAAO,CAACP,aAAaG,MAAMG,GAAGE,IAAI,CAAC,OAAOP;AACrG;AAEA,SAAS9B,YAAYsC,OAAe,EAAEC,WAAmB,EAAEC,iBAAiB,KAAK;IAC/E,MAAMC,aAAa;QAACH;QAASC;WAAgBX,aAAac,WAAWJ;WAAaV,aAAac,WAAWH;KAAa;IACvH,KAAK,MAAMI,eAAeF,WAAY;QACpC,IAAInB,eAAE,CAACC,UAAU,CAACoB,gBAAgBrB,eAAE,CAACsB,SAAS,CAACD,aAAaE,MAAM,IAAI;YACpE,OAAOC,QAAKC,IAAI,CAACzB,eAAE,CAAC0B,YAAY,CAACL,aAAa;QAChD;IACF;IACA,IAAIH,gBAAgB;QAClB,MAAM,IAAI5B,MAAM,GAAG2B,YAAY,UAAU,CAAC;IAC5C;IACA,OAAO,CAAC;AACV;AAEA,SAASpC;IACP,OAAOM,OAAOC,IAAI,CAACuC,QAAQC,GAAG,EAAEC,IAAI,CAAC,CAACC,MAAQA,IAAIC,UAAU,CAACxC,mCAAkB;AACjF;AAEA;;CAEC,GACD,SAASyC,cAAcC,KAAa;IAClC,wCAAwC;IACxCA,QAAQA,MAAMC,OAAO,CAAC,YAAY;IAClC,IAAID,UAAU,QAAQ,OAAO;IAC7B,IAAIA,UAAU,SAAS,OAAO;IAC9B,IAAI,CAACE,MAAMC,OAAOH,SAAS,OAAOG,OAAOH;IACzC,OAAOA;AACT;AAEA;;CAEC,GACD,SAASI,4BAA4BC,GAAQ,EAAEC,QAAgB,EAAEN,KAAU;IACzE,MAAMO,WAAWD,SAASE,KAAK,CAAC;IAChC,IAAIC,SAASJ;IACb,IAAK,IAAIzB,IAAI,GAAGA,IAAI2B,SAASnD,MAAM,GAAG,GAAGwB,IAAK;QAC5C,MAAM8B,MAAMH,QAAQ,CAAC3B,EAAE;QACvB,IAAI,CAAE8B,CAAAA,OAAOD,MAAK,KAAM,OAAOA,MAAM,CAACC,IAAI,KAAK,UAAU;YACvDD,MAAM,CAACC,IAAI,GAAG,CAAC;QACjB;QACAD,SAASA,MAAM,CAACC,IAAI;IACtB;IACAD,MAAM,CAACF,QAAQ,CAACA,SAASnD,MAAM,GAAG,EAAE,CAAC,GAAG4C;AAC1C;AAEA;;;CAGC,GACD,SAASlD,gBAAgBN,MAA2B;IAClD,MAAMmE,SAA8B,CAAC;IAErC,KAAK,MAAM,CAACC,QAAQC,SAAS,IAAI3D,OAAO4D,OAAO,CAACpB,QAAQC,GAAG,EAAG;QAC5D,IAAI,CAACiB,OAAOd,UAAU,CAACxC,mCAAkB,KAAKuD,aAAaE,WAAW;YACpE;QACF;QAEA,2CAA2C;QAC3C,MAAMR,WAAWK,OAAOI,KAAK,CAAC1D,mCAAkB,CAACF,MAAM,EAAEoD,KAAK,CAAC;QAC/D,MAAMS,iBAAiBV,QAAQ,CAACA,SAASnD,MAAM,GAAG,EAAE,KAAK;QACzD,IAAI6D,gBAAgB;YAClB,wBAAwB;YACxBV,SAASW,GAAG;QACd;QAEA,gEAAgE;QAChE,IAAIC,eAAoB3E;QACxB,MAAM4E,eAAyB,EAAE;QACjC,IAAIC,aAAa;QAEjB,KAAK,MAAMX,OAAOH,SAAU;YAC1B,IAAIY,gBAAgB,QAAQ,OAAOA,iBAAiB,UAAU;gBAC5DE,aAAa;gBACb;YACF;YACA,yEAAyE;YACzE,MAAMC,QAAQpE,OAAOC,IAAI,CAACgE,cAAcI,IAAI,CAAC,CAACC,IAAMA,EAAEC,WAAW,OAAOf;YACxE,IAAI,CAACY,OAAO;gBACVD,aAAa;gBACb;YACF;YACAD,aAAaM,IAAI,CAACJ;YAClBH,eAAeA,YAAY,CAACG,MAAM;QACpC;QAEA,IAAI,CAACD,YAAY;YACflD,QAAQwD,IAAI,CAAC,CAAC,wCAAwC,EAAEf,OAAO,EAAE,CAAC;YAClE;QACF;QAEA,wCAAwC;QACxC,MAAM/C,OAAOuD,aAAaQ,IAAI,CAAC;QAC/B,IAAIX,gBAAgB;YAClB,IAAI;gBACFb,4BAA4BO,QAAQ9C,MAAME,eAAE,CAAC0B,YAAY,CAACoB,UAAU,SAASgB,IAAI;YACnF,EAAE,OAAOC,GAAG;gBACV3D,QAAQ4D,KAAK,CAAC,CAAC,iCAAiC,EAAElB,SAAS,GAAG,EAAEiB,GAAG;YACrE;QACF,OAAO;YACL1B,4BAA4BO,QAAQ9C,MAAMkC,cAAcc;QAC1D;IACF;IAEA,OAAOF;AACT"}
|
|
@@ -13,8 +13,8 @@ function _export(target, all) {
|
|
|
13
13
|
});
|
|
14
14
|
}
|
|
15
15
|
_export(exports, {
|
|
16
|
-
get
|
|
17
|
-
return
|
|
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 =
|
|
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
|
|
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
|
-
],
|
|
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
|
-
],
|
|
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
|
-
],
|
|
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
|
-
],
|
|
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
|
-
],
|
|
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
|
-
],
|
|
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
|
-
],
|
|
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
|
-
],
|
|
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 {
|
|
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:
|
|
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:
|
|
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
|
|
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,
|
|
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,
|
|
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;
|
|
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,
|
|
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;
|
|
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:
|
|
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:
|
|
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>
|