@sync-in/server 1.6.1 → 1.8.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 +169 -55
- package/environment/environment.dist.yaml +14 -5
- package/migrations/0003_giant_luckman.sql +6 -0
- package/migrations/meta/0003_snapshot.json +2463 -0
- package/migrations/meta/_journal.json +7 -0
- package/package.json +16 -15
- package/server/app.bootstrap.js +1 -0
- package/server/app.bootstrap.js.map +1 -1
- package/server/app.constants.js +0 -4
- package/server/app.constants.js.map +1 -1
- package/server/app.service.js +7 -6
- package/server/app.service.js.map +1 -1
- package/server/applications/files/constants/only-office.js +12 -0
- package/server/applications/files/constants/only-office.js.map +1 -1
- package/server/applications/files/files.config.js +5 -0
- package/server/applications/files/files.config.js.map +1 -1
- package/server/applications/files/services/files-content-manager.service.js +6 -6
- package/server/applications/files/services/files-content-manager.service.js.map +1 -1
- package/server/applications/files/services/files-manager.service.js +4 -4
- package/server/applications/files/services/files-manager.service.js.map +1 -1
- package/server/applications/files/services/files-methods.service.js +5 -3
- package/server/applications/files/services/files-methods.service.js.map +1 -1
- package/server/applications/files/services/files-only-office-manager.service.js +2 -2
- package/server/applications/files/services/files-only-office-manager.service.js.map +1 -1
- package/server/applications/files/services/files-parser.service.js +6 -3
- package/server/applications/files/services/files-parser.service.js.map +1 -1
- package/server/applications/files/services/files-scheduler.service.js +51 -3
- package/server/applications/files/services/files-scheduler.service.js.map +1 -1
- package/server/applications/files/services/files-search-manager.service.js +4 -0
- package/server/applications/files/services/files-search-manager.service.js.map +1 -1
- package/server/applications/files/utils/doc-textify/adapters/pdf.js +10 -1
- package/server/applications/files/utils/doc-textify/adapters/pdf.js.map +1 -1
- package/server/applications/notifications/i18n/de.js +56 -0
- package/server/applications/notifications/i18n/de.js.map +1 -0
- package/server/applications/notifications/i18n/es.js +52 -0
- package/server/applications/notifications/i18n/es.js.map +1 -0
- package/server/applications/notifications/i18n/hi.js +52 -0
- package/server/applications/notifications/i18n/hi.js.map +1 -0
- package/server/applications/notifications/i18n/index.js +73 -8
- package/server/applications/notifications/i18n/index.js.map +1 -1
- package/server/applications/notifications/i18n/it.js +52 -0
- package/server/applications/notifications/i18n/it.js.map +1 -0
- package/server/applications/notifications/i18n/ja.js +52 -0
- package/server/applications/notifications/i18n/ja.js.map +1 -0
- package/server/applications/notifications/i18n/ko.js +52 -0
- package/server/applications/notifications/i18n/ko.js.map +1 -0
- package/server/applications/notifications/i18n/pl.js +52 -0
- package/server/applications/notifications/i18n/pl.js.map +1 -0
- package/server/applications/notifications/i18n/pt.js +52 -0
- package/server/applications/notifications/i18n/pt.js.map +1 -0
- package/server/applications/notifications/i18n/pt_br.js +52 -0
- package/server/applications/notifications/i18n/pt_br.js.map +1 -0
- package/server/applications/notifications/i18n/ru.js +52 -0
- package/server/applications/notifications/i18n/ru.js.map +1 -0
- package/server/applications/notifications/i18n/tr.js +52 -0
- package/server/applications/notifications/i18n/tr.js.map +1 -0
- package/server/applications/notifications/i18n/zh.js +52 -0
- package/server/applications/notifications/i18n/zh.js.map +1 -0
- package/server/applications/notifications/mails/models.js +6 -7
- package/server/applications/notifications/mails/models.js.map +1 -1
- package/server/applications/notifications/services/notifications-manager.service.js.map +1 -1
- package/server/applications/shares/dto/create-or-update-share.dto.js +11 -0
- package/server/applications/shares/dto/create-or-update-share.dto.js.map +1 -1
- package/server/applications/shares/interfaces/share-props.interface.js.map +1 -1
- package/server/applications/shares/schemas/share.interface.js.map +1 -1
- package/server/applications/shares/schemas/shares.schema.js +9 -0
- package/server/applications/shares/schemas/shares.schema.js.map +1 -1
- package/server/applications/shares/services/shares-manager.service.js +46 -17
- package/server/applications/shares/services/shares-manager.service.js.map +1 -1
- package/server/applications/shares/services/shares-queries.service.js +24 -5
- package/server/applications/shares/services/shares-queries.service.js.map +1 -1
- package/server/applications/spaces/constants/cache.js +4 -0
- package/server/applications/spaces/constants/cache.js.map +1 -1
- package/server/applications/spaces/dto/create-or-update-space.dto.js +5 -0
- package/server/applications/spaces/dto/create-or-update-space.dto.js.map +1 -1
- package/server/applications/spaces/guards/space.guard.js +3 -3
- package/server/applications/spaces/guards/space.guard.js.map +1 -1
- package/server/applications/spaces/models/space-props.model.js.map +1 -1
- package/server/applications/spaces/models/space.model.js.map +1 -1
- package/server/applications/spaces/schemas/space.interface.js.map +1 -1
- package/server/applications/spaces/schemas/spaces.schema.js +1 -0
- package/server/applications/spaces/schemas/spaces.schema.js.map +1 -1
- package/server/applications/spaces/services/spaces-browser.service.js.map +1 -1
- package/server/applications/spaces/services/spaces-manager.service.js +34 -31
- package/server/applications/spaces/services/spaces-manager.service.js.map +1 -1
- package/server/applications/spaces/services/spaces-queries.service.js +23 -7
- package/server/applications/spaces/services/spaces-queries.service.js.map +1 -1
- package/server/applications/spaces/services/spaces-scheduler.service.js +21 -20
- package/server/applications/spaces/services/spaces-scheduler.service.js.map +1 -1
- package/server/applications/spaces/spaces.controller.js +4 -2
- package/server/applications/spaces/spaces.controller.js.map +1 -1
- package/server/applications/spaces/utils/paths.js +14 -16
- package/server/applications/spaces/utils/paths.js.map +1 -1
- package/server/applications/sync/services/sync-manager.service.js +4 -3
- package/server/applications/sync/services/sync-manager.service.js.map +1 -1
- package/server/applications/sync/services/sync-paths-manager.service.js +1 -1
- package/server/applications/sync/services/sync-paths-manager.service.js.map +1 -1
- package/server/applications/sync/services/sync-paths-manager.service.spec.js +1 -1
- package/server/applications/sync/services/sync-paths-manager.service.spec.js.map +1 -1
- package/server/applications/sync/sync.controller.js +2 -1
- package/server/applications/sync/sync.controller.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 +0 -16
- package/server/applications/users/constants/user.js.map +1 -1
- package/server/applications/users/dto/user-properties.dto.js +10 -0
- package/server/applications/users/dto/user-properties.dto.js.map +1 -1
- 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 +3 -2
- package/server/applications/users/schemas/users.schema.js.map +1 -1
- package/server/applications/users/services/admin-users-manager.service.js +1 -0
- package/server/applications/users/services/admin-users-manager.service.js.map +1 -1
- package/server/applications/users/services/admin-users-manager.service.spec.js +2 -1
- package/server/applications/users/services/admin-users-manager.service.spec.js.map +1 -1
- package/server/applications/users/services/users-manager.service.js +8 -2
- package/server/applications/users/services/users-manager.service.js.map +1 -1
- package/server/applications/users/services/users-manager.service.spec.js +1 -0
- package/server/applications/users/services/users-manager.service.spec.js.map +1 -1
- package/server/applications/users/services/users-queries.service.js +18 -4
- package/server/applications/users/services/users-queries.service.js.map +1 -1
- package/server/applications/users/users.controller.js +15 -0
- package/server/applications/users/users.controller.js.map +1 -1
- package/server/applications/users/utils/test.js +2 -2
- package/server/applications/users/utils/test.js.map +1 -1
- package/server/applications/webdav/constants/routes.js +2 -2
- package/server/applications/webdav/constants/routes.js.map +1 -1
- package/server/applications/webdav/constants/webdav.js +2 -2
- package/server/applications/webdav/constants/webdav.js.map +1 -1
- package/server/applications/webdav/filters/webdav.filter.js +2 -2
- package/server/applications/webdav/filters/webdav.filter.js.map +1 -1
- package/server/applications/webdav/filters/webdav.filter.spec.js +2 -2
- package/server/applications/webdav/filters/webdav.filter.spec.js.map +1 -1
- package/server/applications/webdav/services/webdav-methods.service.js +3 -2
- package/server/applications/webdav/services/webdav-methods.service.js.map +1 -1
- package/server/applications/webdav/utils/webdav.js +1 -2
- package/server/applications/webdav/utils/webdav.js.map +1 -1
- package/server/authentication/auth.config.js +17 -2
- package/server/authentication/auth.config.js.map +1 -1
- package/server/authentication/constants/auth-ldap.js +2 -1
- package/server/authentication/constants/auth-ldap.js.map +1 -1
- package/server/authentication/guards/auth-basic.strategy.js +4 -2
- package/server/authentication/guards/auth-basic.strategy.js.map +1 -1
- package/server/authentication/guards/auth-local.strategy.js +2 -0
- package/server/authentication/guards/auth-local.strategy.js.map +1 -1
- package/server/authentication/services/auth-methods/auth-method-ldap.service.js +75 -32
- 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 +2 -1
- package/server/authentication/services/auth-methods/auth-method-ldap.service.spec.js.map +1 -1
- package/server/common/i18n.js +52 -0
- package/server/common/i18n.js.map +1 -0
- package/server/common/image.js +49 -33
- package/server/common/image.js.map +1 -1
- package/server/common/interfaces.js.map +1 -1
- package/server/common/shared.js +5 -2
- package/server/common/shared.js.map +1 -1
- package/server/configuration/config.validation.js +3 -3
- package/server/configuration/config.validation.js.map +1 -1
- package/server/infrastructure/cache/adapters/mysql-cache.adapter.js +8 -6
- package/server/infrastructure/cache/adapters/mysql-cache.adapter.js.map +1 -1
- package/server/infrastructure/cache/adapters/redis-cache.adapter.js +22 -17
- package/server/infrastructure/cache/adapters/redis-cache.adapter.js.map +1 -1
- package/server/infrastructure/cache/cache.e2e-spec.js +1 -0
- package/server/infrastructure/cache/cache.e2e-spec.js.map +1 -1
- package/server/infrastructure/cache/cache.module.js +1 -14
- package/server/infrastructure/cache/cache.module.js.map +1 -1
- package/server/infrastructure/cache/services/cache.service.js.map +1 -1
- package/server/infrastructure/database/database.module.js +20 -1
- package/server/infrastructure/database/database.module.js.map +1 -1
- package/server/infrastructure/database/utils.js +48 -0
- package/server/infrastructure/database/utils.js.map +1 -1
- package/server/infrastructure/scheduler/scheduler.module.js +1 -1
- package/server/infrastructure/scheduler/scheduler.module.js.map +1 -1
- package/server/infrastructure/websocket/adapters/cluster.adapter.js +1 -3
- package/server/infrastructure/websocket/adapters/cluster.adapter.js.map +1 -1
- package/static/3rdpartylicenses.txt +137 -137
- package/static/chunk-2KLC4T2Z.js +1 -0
- package/static/chunk-373XVRXW.js +1 -0
- package/static/chunk-3GMLWAFZ.js +1 -0
- package/static/chunk-3XVM35O2.js +1 -0
- package/static/chunk-3YVRP3VM.js +2 -0
- package/static/chunk-5NMSIIQB.js +1 -0
- package/static/chunk-AF24EYXU.js +1 -0
- package/static/chunk-AKQVEHO6.js +2 -0
- package/static/{chunk-LUWQFIWR.js → chunk-AY2SZ3G6.js} +1 -1
- package/static/chunk-BCVX464U.js +2 -0
- package/static/{chunk-IPAC4VAF.js → chunk-BIKLW4YS.js} +1 -1
- package/static/chunk-C36MW4ME.js +562 -0
- package/static/chunk-CHJ64RJM.js +1 -0
- package/static/chunk-DKSEQTMX.js +1 -0
- package/static/chunk-DM4NXKEP.js +1 -0
- package/static/chunk-DPUVSXRB.js +1 -0
- package/static/chunk-DSWEWLXJ.js +1 -0
- package/static/chunk-FJE6BOFL.js +1 -0
- package/static/chunk-FZ3JPGYZ.js +1 -0
- package/static/chunk-GUGNR5TF.js +3 -0
- package/static/chunk-H6NE33VX.js +1 -0
- package/static/{chunk-S75P2FFI.js → chunk-HAS5ZOTR.js} +1 -1
- package/static/chunk-HNQRZALS.js +1 -0
- package/static/chunk-JPT5WEAT.js +1 -0
- package/static/{chunk-ZQQPUYLU.js → chunk-JSWCNGXJ.js} +1 -1
- package/static/chunk-KFJIQIGR.js +1 -0
- package/static/chunk-LNTUR3GU.js +1 -0
- package/static/chunk-LVM4QB22.js +1 -0
- package/static/chunk-M3XVNQZQ.js +1 -0
- package/static/{chunk-AWQ2YTVC.js → chunk-MFLIJH6T.js} +1 -1
- package/static/{chunk-IQOALFYU.js → chunk-MSUHTBB2.js} +1 -1
- package/static/chunk-N3U6637P.js +1 -0
- package/static/chunk-NNV4OXSB.js +1 -0
- package/static/chunk-NO2LTNW3.js +1 -0
- package/static/chunk-OOGP4WSH.js +2 -0
- package/static/chunk-PB4AIT7O.js +1 -0
- package/static/chunk-PCWDQPOM.js +2 -0
- package/static/chunk-PGZZP5W3.js +1 -0
- package/static/chunk-PVDHBQRM.js +1 -0
- package/static/chunk-Q5KM7LTX.js +1 -0
- package/static/chunk-QHC6ZPQ4.js +1 -0
- package/static/chunk-QO6BTONN.js +1 -0
- package/static/chunk-QZU2S5CV.js +1 -0
- package/static/chunk-SBZ572Q4.js +2 -0
- package/static/chunk-SHIVUDP3.js +1 -0
- package/static/chunk-SLHTEGRU.js +1 -0
- package/static/{chunk-SH5EVL4E.js → chunk-SSFF27P2.js} +1 -1
- package/static/chunk-TPYBFZS5.js +1 -0
- package/static/chunk-UEQCWMXD.js +1 -0
- package/static/chunk-UG5DMXYO.js +1 -0
- package/static/chunk-UJPPR4MX.js +1 -0
- package/static/chunk-UNCPXHHT.js +1 -0
- package/static/chunk-URHTCJ7G.js +1 -0
- package/static/chunk-V3AT2BKP.js +1 -0
- package/static/chunk-VKK5BSLX.js +1 -0
- package/static/{chunk-7DN7ZAPU.js → chunk-VM4YX6Q7.js} +1 -1
- package/static/chunk-WJW7CT6G.js +27 -0
- package/static/{chunk-7ITZXYYJ.js → chunk-WLMNXRBS.js} +1 -1
- package/static/chunk-X5XGK6T7.js +4 -0
- package/static/chunk-YEKR5OPO.js +1 -0
- package/static/chunk-YW57T2PF.js +1 -0
- package/static/chunk-Z5J5F5SX.js +1 -0
- package/static/chunk-ZIJQRARU.js +1 -0
- package/static/chunk-ZPF2DSQV.js +1 -0
- package/static/chunk-ZTCRGJ6Y.js +7 -0
- package/static/index.html +2 -2
- package/static/main-VOL6OMJ5.js +9 -0
- package/static/scripts-WRDOQIU5.js +24 -0
- package/static/{styles-A5VYX3CE.css → styles-2C2UNCNB.css} +1 -1
- package/server/applications/spaces/interfaces/space-quota.interface.js +0 -10
- package/server/applications/spaces/interfaces/space-quota.interface.js.map +0 -1
- package/static/chunk-22EANI6R.js +0 -1
- package/static/chunk-27YQB3TE.js +0 -1
- package/static/chunk-2I4CUFUA.js +0 -1
- package/static/chunk-2MTM6SWN.js +0 -4
- package/static/chunk-34MKICK5.js +0 -27
- package/static/chunk-5O3DIUU3.js +0 -1
- package/static/chunk-6NMVZIIT.js +0 -1
- package/static/chunk-7FUM3JGM.js +0 -1
- package/static/chunk-7P27WBGC.js +0 -4
- package/static/chunk-ATP3BFHV.js +0 -562
- package/static/chunk-DSOE3FEP.js +0 -1
- package/static/chunk-EFKMBLRE.js +0 -1
- package/static/chunk-FUFKVHPU.js +0 -1
- package/static/chunk-HCDLWTMW.js +0 -7
- package/static/chunk-JASU3CIH.js +0 -1
- package/static/chunk-JQ5FTO2M.js +0 -1
- package/static/chunk-JUNZFADM.js +0 -1
- package/static/chunk-JXZCNFW7.js +0 -1
- package/static/chunk-LJUKI4SQ.js +0 -1
- package/static/chunk-ORMRCEGT.js +0 -1
- package/static/chunk-Q7D6RN4N.js +0 -1
- package/static/chunk-QJX6ITLW.js +0 -1
- package/static/chunk-QQ6UQQBR.js +0 -1
- package/static/chunk-RTRJ3KFH.js +0 -1
- package/static/chunk-S2HDY3OL.js +0 -1
- package/static/chunk-T3EYFSVZ.js +0 -1
- package/static/chunk-U34OZUZ7.js +0 -1
- package/static/chunk-Y7EH7G5K.js +0 -1
- package/static/main-7SQDDVMD.js +0 -9
- package/static/scripts-VZVAP2P4.js +0 -30
package/server/common/image.js
CHANGED
|
@@ -29,7 +29,8 @@ _export(exports, {
|
|
|
29
29
|
return svgMimeType;
|
|
30
30
|
}
|
|
31
31
|
});
|
|
32
|
-
const
|
|
32
|
+
const _sharp = /*#__PURE__*/ _interop_require_default(require("sharp"));
|
|
33
|
+
const _resvgjs = require("@resvg/resvg-js");
|
|
33
34
|
const _promises = /*#__PURE__*/ _interop_require_default(require("node:fs/promises"));
|
|
34
35
|
const _nodepath = /*#__PURE__*/ _interop_require_default(require("node:path"));
|
|
35
36
|
function _interop_require_default(obj) {
|
|
@@ -37,54 +38,69 @@ function _interop_require_default(obj) {
|
|
|
37
38
|
default: obj
|
|
38
39
|
};
|
|
39
40
|
}
|
|
40
|
-
(0, _canvas.registerFont)(_nodepath.default.join(__dirname, 'fonts', 'avatar.ttf'), {
|
|
41
|
-
family: 'Avatar'
|
|
42
|
-
});
|
|
43
41
|
const pngMimeType = 'image/png';
|
|
44
42
|
const svgMimeType = 'image/svg+xml';
|
|
45
43
|
async function generateThumbnail(filePath, size) {
|
|
46
|
-
const image =
|
|
47
|
-
let width = image.
|
|
48
|
-
|
|
44
|
+
const image = (0, _sharp.default)(filePath).rotate();
|
|
45
|
+
let { width, height } = await image.metadata();
|
|
46
|
+
if (!width || !height) throw new Error('Invalid image dimensions');
|
|
49
47
|
// Calculate the new dimensions, maintaining the aspect ratio
|
|
50
48
|
if (width > height) {
|
|
51
49
|
if (width > size) {
|
|
52
|
-
height
|
|
50
|
+
height = Math.round(height * size / width);
|
|
53
51
|
width = size;
|
|
54
52
|
}
|
|
55
53
|
} else {
|
|
56
54
|
if (height > size) {
|
|
57
|
-
width
|
|
55
|
+
width = Math.round(width * size / height);
|
|
58
56
|
height = size;
|
|
59
57
|
}
|
|
60
58
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const ctx = canvas.getContext('2d');
|
|
65
|
-
ctx.drawImage(image, 0, 0, width, height);
|
|
66
|
-
return canvas.createPNGStream({
|
|
59
|
+
return image.resize(width, height, {
|
|
60
|
+
fit: 'inside'
|
|
61
|
+
}).png({
|
|
67
62
|
compressionLevel: 0
|
|
68
|
-
});
|
|
63
|
+
}).toBuffer();
|
|
69
64
|
}
|
|
70
|
-
function generateAvatar(initials) {
|
|
71
|
-
const
|
|
72
|
-
const
|
|
73
|
-
const text = initials;
|
|
65
|
+
async function generateAvatar(initials) {
|
|
66
|
+
const width = 256;
|
|
67
|
+
const height = 256;
|
|
74
68
|
const { backgroundColor, foregroundColor } = randomColor();
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
69
|
+
const fontPath = _nodepath.default.join(__dirname, 'fonts', 'avatar.ttf');
|
|
70
|
+
const fontBase64 = (await _promises.default.readFile(fontPath)).toString('base64');
|
|
71
|
+
const svg = `
|
|
72
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}">
|
|
73
|
+
<style>
|
|
74
|
+
@font-face {
|
|
75
|
+
font-family: 'Avatar';
|
|
76
|
+
src: url('data:font/ttf;base64,${fontBase64}') format('truetype');
|
|
77
|
+
}
|
|
78
|
+
text {
|
|
79
|
+
font-family: 'Avatar', sans-serif;
|
|
80
|
+
font-size: 150px;
|
|
81
|
+
fill: ${foregroundColor};
|
|
82
|
+
dominant-baseline: central;
|
|
83
|
+
text-anchor: middle;
|
|
84
|
+
}
|
|
85
|
+
</style>
|
|
86
|
+
<rect width="100%" height="100%" fill="${backgroundColor}" />
|
|
87
|
+
<text x="50%" y="50%">${initials}</text>
|
|
88
|
+
</svg>
|
|
89
|
+
`;
|
|
90
|
+
// Rasterize SVG to PNG
|
|
91
|
+
const resvg = new _resvgjs.Resvg(svg, {
|
|
92
|
+
fitTo: {
|
|
93
|
+
mode: 'width',
|
|
94
|
+
value: width
|
|
95
|
+
},
|
|
96
|
+
font: {
|
|
97
|
+
fontFiles: [
|
|
98
|
+
fontPath
|
|
99
|
+
],
|
|
100
|
+
loadSystemFonts: false
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
return resvg.render().asPng();
|
|
88
104
|
}
|
|
89
105
|
async function convertImageToBase64(imgPath) {
|
|
90
106
|
const base64String = await _promises.default.readFile(imgPath, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../backend/src/common/image.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
|
|
1
|
+
{"version":3,"sources":["../../../backend/src/common/image.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 sharp from 'sharp'\nimport { Resvg } from '@resvg/resvg-js'\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\n\nexport const pngMimeType = 'image/png'\nexport const svgMimeType = 'image/svg+xml'\n\nexport async function generateThumbnail(filePath: string, size: number) {\n const image = sharp(filePath).rotate()\n let { width, height } = await image.metadata()\n\n if (!width || !height) throw new Error('Invalid image dimensions')\n\n // Calculate the new dimensions, maintaining the aspect ratio\n if (width > height) {\n if (width > size) {\n height = Math.round((height * size) / width)\n width = size\n }\n } else {\n if (height > size) {\n width = Math.round((width * size) / height)\n height = size\n }\n }\n\n return image.resize(width, height, { fit: 'inside' }).png({ compressionLevel: 0 }).toBuffer()\n}\n\nexport async function generateAvatar(initials: string): Promise<Buffer> {\n const width = 256\n const height = 256\n const { backgroundColor, foregroundColor } = randomColor()\n\n const fontPath = path.join(__dirname, 'fonts', 'avatar.ttf')\n const fontBase64 = (await fs.readFile(fontPath)).toString('base64')\n\n const svg = `\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${width}\" height=\"${height}\">\n <style>\n @font-face {\n font-family: 'Avatar';\n src: url('data:font/ttf;base64,${fontBase64}') format('truetype');\n }\n text {\n font-family: 'Avatar', sans-serif;\n font-size: 150px;\n fill: ${foregroundColor};\n dominant-baseline: central;\n text-anchor: middle;\n }\n </style>\n <rect width=\"100%\" height=\"100%\" fill=\"${backgroundColor}\" />\n <text x=\"50%\" y=\"50%\">${initials}</text>\n </svg>\n `\n\n // Rasterize SVG to PNG\n const resvg = new Resvg(svg, {\n fitTo: { mode: 'width', value: width },\n font: {\n fontFiles: [fontPath],\n loadSystemFonts: false\n }\n })\n\n return resvg.render().asPng()\n}\n\nexport async function convertImageToBase64(imgPath: string) {\n const base64String = await fs.readFile(imgPath, { encoding: 'base64' })\n return `data:image/png;base64,${base64String}`\n}\n\nfunction randomColor() {\n let color = ''\n while (color.length < 6) {\n /* sometimes the returned value does not have\n * the 6 digits needed, so we do it again until\n * it does\n */\n color = Math.floor(Math.random() * 16777215).toString(16)\n }\n const red = parseInt(color.substring(0, 2), 16)\n const green = parseInt(color.substring(2, 4), 16)\n const blue = parseInt(color.substring(4, 6), 16)\n const brightness = red * 0.299 + green * 0.587 + blue * 0.114\n\n return {\n backgroundColor: `#${color}`,\n foregroundColor: brightness > 180 ? '#000000' : '#ffffff'\n }\n}\n"],"names":["convertImageToBase64","generateAvatar","generateThumbnail","pngMimeType","svgMimeType","filePath","size","image","sharp","rotate","width","height","metadata","Error","Math","round","resize","fit","png","compressionLevel","toBuffer","initials","backgroundColor","foregroundColor","randomColor","fontPath","path","join","__dirname","fontBase64","fs","readFile","toString","svg","resvg","Resvg","fitTo","mode","value","font","fontFiles","loadSystemFonts","render","asPng","imgPath","base64String","encoding","color","length","floor","random","red","parseInt","substring","green","blue","brightness"],"mappings":"AAAA;;;;CAIC;;;;;;;;;;;QAwEqBA;eAAAA;;QAxCAC;eAAAA;;QAtBAC;eAAAA;;QAHTC;eAAAA;;QACAC;eAAAA;;;8DANK;yBACI;iEACP;iEACE;;;;;;AAEV,MAAMD,cAAc;AACpB,MAAMC,cAAc;AAEpB,eAAeF,kBAAkBG,QAAgB,EAAEC,IAAY;IACpE,MAAMC,QAAQC,IAAAA,cAAK,EAACH,UAAUI,MAAM;IACpC,IAAI,EAAEC,KAAK,EAAEC,MAAM,EAAE,GAAG,MAAMJ,MAAMK,QAAQ;IAE5C,IAAI,CAACF,SAAS,CAACC,QAAQ,MAAM,IAAIE,MAAM;IAEvC,6DAA6D;IAC7D,IAAIH,QAAQC,QAAQ;QAClB,IAAID,QAAQJ,MAAM;YAChBK,SAASG,KAAKC,KAAK,CAAC,AAACJ,SAASL,OAAQI;YACtCA,QAAQJ;QACV;IACF,OAAO;QACL,IAAIK,SAASL,MAAM;YACjBI,QAAQI,KAAKC,KAAK,CAAC,AAACL,QAAQJ,OAAQK;YACpCA,SAASL;QACX;IACF;IAEA,OAAOC,MAAMS,MAAM,CAACN,OAAOC,QAAQ;QAAEM,KAAK;IAAS,GAAGC,GAAG,CAAC;QAAEC,kBAAkB;IAAE,GAAGC,QAAQ;AAC7F;AAEO,eAAenB,eAAeoB,QAAgB;IACnD,MAAMX,QAAQ;IACd,MAAMC,SAAS;IACf,MAAM,EAAEW,eAAe,EAAEC,eAAe,EAAE,GAAGC;IAE7C,MAAMC,WAAWC,iBAAI,CAACC,IAAI,CAACC,WAAW,SAAS;IAC/C,MAAMC,aAAa,AAAC,CAAA,MAAMC,iBAAE,CAACC,QAAQ,CAACN,SAAQ,EAAGO,QAAQ,CAAC;IAE1D,MAAMC,MAAM,CAAC;iDACkC,EAAEvB,MAAM,UAAU,EAAEC,OAAO;;;;uCAIrC,EAAEkB,WAAW;;;;;cAKtC,EAAEN,gBAAgB;;;;;2CAKW,EAAED,gBAAgB;0BACnC,EAAED,SAAS;;EAEnC,CAAC;IAED,uBAAuB;IACvB,MAAMa,QAAQ,IAAIC,cAAK,CAACF,KAAK;QAC3BG,OAAO;YAAEC,MAAM;YAASC,OAAO5B;QAAM;QACrC6B,MAAM;YACJC,WAAW;gBAACf;aAAS;YACrBgB,iBAAiB;QACnB;IACF;IAEA,OAAOP,MAAMQ,MAAM,GAAGC,KAAK;AAC7B;AAEO,eAAe3C,qBAAqB4C,OAAe;IACxD,MAAMC,eAAe,MAAMf,iBAAE,CAACC,QAAQ,CAACa,SAAS;QAAEE,UAAU;IAAS;IACrE,OAAO,CAAC,sBAAsB,EAAED,cAAc;AAChD;AAEA,SAASrB;IACP,IAAIuB,QAAQ;IACZ,MAAOA,MAAMC,MAAM,GAAG,EAAG;QACvB;;;KAGC,GACDD,QAAQjC,KAAKmC,KAAK,CAACnC,KAAKoC,MAAM,KAAK,UAAUlB,QAAQ,CAAC;IACxD;IACA,MAAMmB,MAAMC,SAASL,MAAMM,SAAS,CAAC,GAAG,IAAI;IAC5C,MAAMC,QAAQF,SAASL,MAAMM,SAAS,CAAC,GAAG,IAAI;IAC9C,MAAME,OAAOH,SAASL,MAAMM,SAAS,CAAC,GAAG,IAAI;IAC7C,MAAMG,aAAaL,MAAM,QAAQG,QAAQ,QAAQC,OAAO;IAExD,OAAO;QACLjC,iBAAiB,CAAC,CAAC,EAAEyB,OAAO;QAC5BxB,iBAAiBiC,aAAa,MAAM,YAAY;IAClD;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../backend/src/common/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 type Entries<T> = { [K in keyof T]: [K, T[K]] }[keyof T][]\n"],"names":[],"mappings":"AAAA;;;;CAIC"}
|
|
1
|
+
{"version":3,"sources":["../../../backend/src/common/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 type Entries<T> = { [K in keyof T]: [K, T[K]] }[keyof T][]\n\nexport interface StorageQuota {\n storageUsage: number\n storageQuota: number\n}\n"],"names":[],"mappings":"AAAA;;;;CAIC"}
|
package/server/common/shared.js
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
* Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>
|
|
3
3
|
* This file is part of Sync-in | The open source file sync and share solution
|
|
4
4
|
* See the LICENSE file for licensing details
|
|
5
|
-
*/
|
|
6
|
-
"use strict";
|
|
5
|
+
*/ "use strict";
|
|
7
6
|
Object.defineProperty(exports, "__esModule", {
|
|
8
7
|
value: true
|
|
9
8
|
});
|
|
@@ -14,6 +13,9 @@ function _export(target, all) {
|
|
|
14
13
|
});
|
|
15
14
|
}
|
|
16
15
|
_export(exports, {
|
|
16
|
+
get SERVER_NAME () {
|
|
17
|
+
return SERVER_NAME;
|
|
18
|
+
},
|
|
17
19
|
get capitalizeString () {
|
|
18
20
|
return capitalizeString;
|
|
19
21
|
},
|
|
@@ -60,6 +62,7 @@ _export(exports, {
|
|
|
60
62
|
return regExpPreventPathTraversal;
|
|
61
63
|
}
|
|
62
64
|
});
|
|
65
|
+
const SERVER_NAME = 'Sync-in';
|
|
63
66
|
const regExpInvalidFileName = /^(?:CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])$|[<>:"/\\|?*\x00-\x1f\x80-\x9f]/;
|
|
64
67
|
const regExpPreventPathTraversal = /^(\.\.(\/|\\|$))+/;
|
|
65
68
|
const regExpNumberSuffix = /-\d+$/;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../backend/src/common/shared.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\n// eslint-disable-next-line no-control-regex\nexport const regExpInvalidFileName = /^(?:CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])$|[<>:\"/\\\\|?*\\x00-\\x1f\\x80-\\x9f]/\nexport const regExpPreventPathTraversal = /^(\\.\\.(\\/|\\\\|$))+/\nexport const regExpNumberSuffix = /-\\d+$/\nexport const forbiddenChars = '\\\\ / : * ? \" < > |'\n\nexport function isValidFileName(fileName: string) {\n if (regExpInvalidFileName.test(fileName)) {\n throw new Error('Forbidden characters')\n }\n}\n\nexport function currentTimeStamp(date?: Date, ms = false): number {\n return Math.floor((date ? date : new Date()).getTime() / (ms ? 1 : 1000))\n}\n\nexport function currentDate(value?: string): Date {\n return new Date((value ? value : new Date().toISOString()).split('T')[0])\n}\n\nexport function createSlug(input: string, replaceCount = false): string {\n const r = input\n .toLowerCase()\n .trim()\n .replace(/[\\s_-]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n if (replaceCount) return r.replace(regExpNumberSuffix, '')\n return r\n}\n\nexport function createLightSlug(input: string) {\n return input\n .toLowerCase()\n .trim()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n}\n\nexport function genPassword(length = 12) {\n const chars = '0123456789abcdefghijklmnopqrstuvwxyz!@#$%^&*()ABCDEFGHIJKLMNOPQRSTUVWXYZ'\n let password = ''\n for (let i = 0; i <= length; i++) {\n const randomNumber = Math.floor(Math.random() * chars.length)\n password += chars.substring(randomNumber, randomNumber + 1)\n }\n return password\n}\n\nexport function popFromObject(key: string, object: any): any {\n const item = object[key]\n delete object[key]\n return item\n}\n\nexport function encodeUrl(url: string): string {\n return url\n .split('/')\n .map((e) => encodeURIComponent(e))\n .join('/')\n}\n\nexport function decodeUrl(url: string): string {\n return url\n .split('/')\n .map((e) => decodeURIComponent(e))\n .join('/')\n}\n\nexport function objectPropertyFromString(obj: any, property: string): any {\n const a = property.split('.')\n let o = obj\n for (let i = 0, n = a.length; i < n; i++) {\n const k = a[i]\n if (k in o) {\n o = o[k]\n } else {\n return null\n }\n }\n return o\n}\n\nexport function capitalizeString(value: string): string {\n return value.charAt(0).toUpperCase() + value.slice(1)\n}\n"],"names":["capitalizeString","createLightSlug","createSlug","currentDate","currentTimeStamp","decodeUrl","encodeUrl","forbiddenChars","genPassword","isValidFileName","objectPropertyFromString","popFromObject","regExpInvalidFileName","regExpNumberSuffix","regExpPreventPathTraversal","fileName","test","Error","date","ms","Math","floor","Date","getTime","value","toISOString","split","input","replaceCount","r","toLowerCase","trim","replace","normalize","length","chars","password","i","randomNumber","random","substring","key","object","item","url","map","e","encodeURIComponent","join","decodeURIComponent","obj","property","a","o","n","k","charAt","toUpperCase","slice"],"mappings":"AAAA;;;;CAIC
|
|
1
|
+
{"version":3,"sources":["../../../backend/src/common/shared.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 const SERVER_NAME = 'Sync-in' as const\n\n// eslint-disable-next-line no-control-regex\nexport const regExpInvalidFileName = /^(?:CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])$|[<>:\"/\\\\|?*\\x00-\\x1f\\x80-\\x9f]/\nexport const regExpPreventPathTraversal = /^(\\.\\.(\\/|\\\\|$))+/\nexport const regExpNumberSuffix = /-\\d+$/\nexport const forbiddenChars = '\\\\ / : * ? \" < > |'\n\nexport function isValidFileName(fileName: string) {\n if (regExpInvalidFileName.test(fileName)) {\n throw new Error('Forbidden characters')\n }\n}\n\nexport function currentTimeStamp(date?: Date, ms = false): number {\n return Math.floor((date ? date : new Date()).getTime() / (ms ? 1 : 1000))\n}\n\nexport function currentDate(value?: string): Date {\n return new Date((value ? value : new Date().toISOString()).split('T')[0])\n}\n\nexport function createSlug(input: string, replaceCount = false): string {\n const r = input\n .toLowerCase()\n .trim()\n .replace(/[\\s_-]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n if (replaceCount) return r.replace(regExpNumberSuffix, '')\n return r\n}\n\nexport function createLightSlug(input: string) {\n return input\n .toLowerCase()\n .trim()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n}\n\nexport function genPassword(length = 12) {\n const chars = '0123456789abcdefghijklmnopqrstuvwxyz!@#$%^&*()ABCDEFGHIJKLMNOPQRSTUVWXYZ'\n let password = ''\n for (let i = 0; i <= length; i++) {\n const randomNumber = Math.floor(Math.random() * chars.length)\n password += chars.substring(randomNumber, randomNumber + 1)\n }\n return password\n}\n\nexport function popFromObject(key: string, object: any): any {\n const item = object[key]\n delete object[key]\n return item\n}\n\nexport function encodeUrl(url: string): string {\n return url\n .split('/')\n .map((e) => encodeURIComponent(e))\n .join('/')\n}\n\nexport function decodeUrl(url: string): string {\n return url\n .split('/')\n .map((e) => decodeURIComponent(e))\n .join('/')\n}\n\nexport function objectPropertyFromString(obj: any, property: string): any {\n const a = property.split('.')\n let o = obj\n for (let i = 0, n = a.length; i < n; i++) {\n const k = a[i]\n if (k in o) {\n o = o[k]\n } else {\n return null\n }\n }\n return o\n}\n\nexport function capitalizeString(value: string): string {\n return value.charAt(0).toUpperCase() + value.slice(1)\n}\n"],"names":["SERVER_NAME","capitalizeString","createLightSlug","createSlug","currentDate","currentTimeStamp","decodeUrl","encodeUrl","forbiddenChars","genPassword","isValidFileName","objectPropertyFromString","popFromObject","regExpInvalidFileName","regExpNumberSuffix","regExpPreventPathTraversal","fileName","test","Error","date","ms","Math","floor","Date","getTime","value","toISOString","split","input","replaceCount","r","toLowerCase","trim","replace","normalize","length","chars","password","i","randomNumber","random","substring","key","object","item","url","map","e","encodeURIComponent","join","decodeURIComponent","obj","property","a","o","n","k","charAt","toUpperCase","slice"],"mappings":"AAAA;;;;CAIC;;;;;;;;;;;QAEYA;eAAAA;;QAsFGC;eAAAA;;QApDAC;eAAAA;;QAZAC;eAAAA;;QAJAC;eAAAA;;QAJAC;eAAAA;;QAmDAC;eAAAA;;QAPAC;eAAAA;;QApDHC;eAAAA;;QAoCGC;eAAAA;;QAlCAC;eAAAA;;QAgEAC;eAAAA;;QApBAC;eAAAA;;QAjDHC;eAAAA;;QAEAC;eAAAA;;QADAC;eAAAA;;;AAJN,MAAMf,cAAc;AAGpB,MAAMa,wBAAwB;AAC9B,MAAME,6BAA6B;AACnC,MAAMD,qBAAqB;AAC3B,MAAMN,iBAAiB;AAEvB,SAASE,gBAAgBM,QAAgB;IAC9C,IAAIH,sBAAsBI,IAAI,CAACD,WAAW;QACxC,MAAM,IAAIE,MAAM;IAClB;AACF;AAEO,SAASb,iBAAiBc,IAAW,EAAEC,KAAK,KAAK;IACtD,OAAOC,KAAKC,KAAK,CAAC,AAACH,CAAAA,OAAOA,OAAO,IAAII,MAAK,EAAGC,OAAO,KAAMJ,CAAAA,KAAK,IAAI,IAAG;AACxE;AAEO,SAAShB,YAAYqB,KAAc;IACxC,OAAO,IAAIF,KAAK,AAACE,CAAAA,QAAQA,QAAQ,IAAIF,OAAOG,WAAW,EAAC,EAAGC,KAAK,CAAC,IAAI,CAAC,EAAE;AAC1E;AAEO,SAASxB,WAAWyB,KAAa,EAAEC,eAAe,KAAK;IAC5D,MAAMC,IAAIF,MACPG,WAAW,GACXC,IAAI,GACJC,OAAO,CAAC,YAAY,KACpBA,OAAO,CAAC,YAAY,IACpBC,SAAS,CAAC,OACVD,OAAO,CAAC,oBAAoB;IAC/B,IAAIJ,cAAc,OAAOC,EAAEG,OAAO,CAACnB,oBAAoB;IACvD,OAAOgB;AACT;AAEO,SAAS5B,gBAAgB0B,KAAa;IAC3C,OAAOA,MACJG,WAAW,GACXC,IAAI,GACJE,SAAS,CAAC,OACVD,OAAO,CAAC,oBAAoB;AACjC;AAEO,SAASxB,YAAY0B,SAAS,EAAE;IACrC,MAAMC,QAAQ;IACd,IAAIC,WAAW;IACf,IAAK,IAAIC,IAAI,GAAGA,KAAKH,QAAQG,IAAK;QAChC,MAAMC,eAAelB,KAAKC,KAAK,CAACD,KAAKmB,MAAM,KAAKJ,MAAMD,MAAM;QAC5DE,YAAYD,MAAMK,SAAS,CAACF,cAAcA,eAAe;IAC3D;IACA,OAAOF;AACT;AAEO,SAASzB,cAAc8B,GAAW,EAAEC,MAAW;IACpD,MAAMC,OAAOD,MAAM,CAACD,IAAI;IACxB,OAAOC,MAAM,CAACD,IAAI;IAClB,OAAOE;AACT;AAEO,SAASrC,UAAUsC,GAAW;IACnC,OAAOA,IACJlB,KAAK,CAAC,KACNmB,GAAG,CAAC,CAACC,IAAMC,mBAAmBD,IAC9BE,IAAI,CAAC;AACV;AAEO,SAAS3C,UAAUuC,GAAW;IACnC,OAAOA,IACJlB,KAAK,CAAC,KACNmB,GAAG,CAAC,CAACC,IAAMG,mBAAmBH,IAC9BE,IAAI,CAAC;AACV;AAEO,SAAStC,yBAAyBwC,GAAQ,EAAEC,QAAgB;IACjE,MAAMC,IAAID,SAASzB,KAAK,CAAC;IACzB,IAAI2B,IAAIH;IACR,IAAK,IAAIb,IAAI,GAAGiB,IAAIF,EAAElB,MAAM,EAAEG,IAAIiB,GAAGjB,IAAK;QACxC,MAAMkB,IAAIH,CAAC,CAACf,EAAE;QACd,IAAIkB,KAAKF,GAAG;YACVA,IAAIA,CAAC,CAACE,EAAE;QACV,OAAO;YACL,OAAO;QACT;IACF;IACA,OAAOF;AACT;AAEO,SAASrD,iBAAiBwB,KAAa;IAC5C,OAAOA,MAAMgC,MAAM,CAAC,GAAGC,WAAW,KAAKjC,MAAMkC,KAAK,CAAC;AACrD"}
|
|
@@ -46,7 +46,7 @@ let ServerConfig = class ServerConfig {
|
|
|
46
46
|
constructor(){
|
|
47
47
|
this.host = '0.0.0.0';
|
|
48
48
|
this.port = 8080;
|
|
49
|
-
this.workers =
|
|
49
|
+
this.workers = 1;
|
|
50
50
|
this.trustProxy = 1;
|
|
51
51
|
this.restartOnFailure = true;
|
|
52
52
|
}
|
|
@@ -62,9 +62,9 @@ _ts_decorate([
|
|
|
62
62
|
_ts_metadata("design:type", Number)
|
|
63
63
|
], ServerConfig.prototype, "port", void 0);
|
|
64
64
|
_ts_decorate([
|
|
65
|
-
(0, _classtransformer.Transform)(({ value })=>value === 0 || value === 'auto' ? (0, _nodeos.cpus)().length : Math.max(Number(value),
|
|
65
|
+
(0, _classtransformer.Transform)(({ value })=>value === 0 || value === 'auto' ? (0, _nodeos.cpus)().length : Math.max(Number(value), 1)),
|
|
66
66
|
(0, _classvalidator.IsInt)(),
|
|
67
|
-
(0, _classvalidator.Min)(
|
|
67
|
+
(0, _classvalidator.Min)(1),
|
|
68
68
|
_ts_metadata("design:type", Number)
|
|
69
69
|
], ServerConfig.prototype, "workers", void 0);
|
|
70
70
|
_ts_decorate([
|
|
@@ -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 {\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),
|
|
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), 1)))\n @IsInt()\n @Min(1)\n workers: number = 1\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"}
|
|
@@ -22,6 +22,7 @@ const _configenvironment = require("../../../configuration/config.environment");
|
|
|
22
22
|
const _constants = require("../../database/constants");
|
|
23
23
|
const _databaseinterface = require("../../database/interfaces/database.interface");
|
|
24
24
|
const _utils = require("../../database/utils");
|
|
25
|
+
const _schedulerconstants = require("../../scheduler/scheduler.constants");
|
|
25
26
|
const _mysqlcacheschema = require("../schemas/mysql-cache.schema");
|
|
26
27
|
const _cacheservice = require("../services/cache.service");
|
|
27
28
|
function _interop_require_default(obj) {
|
|
@@ -44,8 +45,8 @@ function _ts_param(paramIndex, decorator) {
|
|
|
44
45
|
};
|
|
45
46
|
}
|
|
46
47
|
let MysqlCacheAdapter = class MysqlCacheAdapter {
|
|
47
|
-
async
|
|
48
|
-
if (
|
|
48
|
+
async onModuleInit() {
|
|
49
|
+
if (_nodecluster.default.isWorker && process.env[_schedulerconstants.SCHEDULER_ENV] === _schedulerconstants.SCHEDULER_STATE.ENABLED) {
|
|
49
50
|
try {
|
|
50
51
|
await this.db.execute(`SET GLOBAL event_scheduler = ON;`);
|
|
51
52
|
await this.db.execute(`DROP EVENT IF EXISTS ${this.scheduledJobName};`);
|
|
@@ -62,6 +63,11 @@ let MysqlCacheAdapter = class MysqlCacheAdapter {
|
|
|
62
63
|
}
|
|
63
64
|
}
|
|
64
65
|
}
|
|
66
|
+
async onModuleDestroy() {
|
|
67
|
+
if (this.scheduledJob) {
|
|
68
|
+
await this.scheduledJob.stop();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
65
71
|
async keys(pattern) {
|
|
66
72
|
const ks = await this.db.select({
|
|
67
73
|
key: _mysqlcacheschema.cache.key
|
|
@@ -117,9 +123,6 @@ let MysqlCacheAdapter = class MysqlCacheAdapter {
|
|
|
117
123
|
genSlugKey(...args) {
|
|
118
124
|
return (0, _shared.createSlug)(args.join(' '));
|
|
119
125
|
}
|
|
120
|
-
async quit() {
|
|
121
|
-
this.logger.verbose(`${this.quit.name}`);
|
|
122
|
-
}
|
|
123
126
|
getTTL(ttl) {
|
|
124
127
|
/* ttl (seconds):
|
|
125
128
|
- 0 : infinite expiration
|
|
@@ -153,7 +156,6 @@ let MysqlCacheAdapter = class MysqlCacheAdapter {
|
|
|
153
156
|
this.logger = new _common.Logger(_cacheservice.Cache.name.toUpperCase());
|
|
154
157
|
this.whereNotExpired = ()=>(0, _drizzleorm.notBetween)(_mysqlcacheschema.cache.expiration, 0, (0, _shared.currentTimeStamp)());
|
|
155
158
|
this.whereExpired = ()=>(0, _drizzleorm.between)(_mysqlcacheschema.cache.expiration, 0, (0, _shared.currentTimeStamp)());
|
|
156
|
-
this.initScheduler().catch((e)=>this.logger.error(e));
|
|
157
159
|
}
|
|
158
160
|
};
|
|
159
161
|
MysqlCacheAdapter = _ts_decorate([
|
|
@@ -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 } 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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"}
|
|
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 { SCHEDULER_ENV, SCHEDULER_STATE } from '../../scheduler/scheduler.constants'\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\n async onModuleInit(): Promise<void> {\n if (cluster.isWorker && process.env[SCHEDULER_ENV] === SCHEDULER_STATE.ENABLED) {\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 onModuleDestroy(): Promise<void> {\n if (this.scheduledJob) {\n await this.scheduledJob.stop()\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: string, data: unknown, 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 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","onModuleInit","cluster","isWorker","process","env","SCHEDULER_ENV","SCHEDULER_STATE","ENABLED","db","execute","scheduledJobName","scheduledJobInterval","logger","log","e","error","sql","code","message","warn","scheduledJob","CronJob","clearExpiredKeys","scheduler","addCronJob","start","onModuleDestroy","stop","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","currentTimeStamp","infiniteExpiration","defaultTTL","undefined","whereExpired","configuration","Logger","Cache","toUpperCase","notBetween","between"],"mappings":"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"}
|
|
@@ -27,16 +27,26 @@ function _ts_metadata(k, v) {
|
|
|
27
27
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
28
28
|
}
|
|
29
29
|
let RedisCacheAdapter = class RedisCacheAdapter {
|
|
30
|
+
async onModuleInit() {
|
|
31
|
+
this.client.on('error', (e)=>this.logger.error(e.message || e));
|
|
32
|
+
this.client.on('ready', ()=>this.logger.log(`Connected to Redis Server at ${this.client.options.url}`));
|
|
33
|
+
this.client.connect().catch((e)=>this.logger.error(e));
|
|
34
|
+
}
|
|
35
|
+
async onModuleDestroy() {
|
|
36
|
+
if (this.client?.isOpen) {
|
|
37
|
+
await this.client.close();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
30
40
|
async has(key) {
|
|
31
41
|
return await this.client.exists(key) === 1;
|
|
32
42
|
}
|
|
33
43
|
async keys(pattern) {
|
|
34
44
|
const matches = [];
|
|
35
|
-
for await (const
|
|
45
|
+
for await (const keys of this.client.scanIterator({
|
|
36
46
|
MATCH: pattern,
|
|
37
47
|
COUNT: 100
|
|
38
48
|
})){
|
|
39
|
-
matches.push(
|
|
49
|
+
matches.push(...keys);
|
|
40
50
|
}
|
|
41
51
|
return matches;
|
|
42
52
|
}
|
|
@@ -49,7 +59,10 @@ let RedisCacheAdapter = class RedisCacheAdapter {
|
|
|
49
59
|
async set(key, data, ttl) {
|
|
50
60
|
const exp = this.getTTL(ttl);
|
|
51
61
|
return await this.client.set(key, this.serialize(data), {
|
|
52
|
-
|
|
62
|
+
expiration: {
|
|
63
|
+
type: 'EX',
|
|
64
|
+
value: exp === -1 ? undefined : exp
|
|
65
|
+
}
|
|
53
66
|
}) === 'OK';
|
|
54
67
|
}
|
|
55
68
|
async del(key) {
|
|
@@ -60,20 +73,16 @@ let RedisCacheAdapter = class RedisCacheAdapter {
|
|
|
60
73
|
for (const key of keys){
|
|
61
74
|
multi.unlink(key);
|
|
62
75
|
}
|
|
63
|
-
const
|
|
64
|
-
return
|
|
76
|
+
const res = await multi.exec();
|
|
77
|
+
return Array.isArray(res) && res.some((r)=>typeof r === 'number' && r > 0);
|
|
65
78
|
}
|
|
66
79
|
genSlugKey(...args) {
|
|
67
80
|
return (0, _shared.createSlug)(args.join(' '));
|
|
68
81
|
}
|
|
69
|
-
async quit() {
|
|
70
|
-
await this.client.disconnect();
|
|
71
|
-
this.logger.verbose(`${this.quit.name}`);
|
|
72
|
-
}
|
|
73
82
|
getTTL(ttl) {
|
|
74
83
|
/* ttl (seconds):
|
|
75
|
-
- 0
|
|
76
|
-
- undefined
|
|
84
|
+
- 0: infinite expiration
|
|
85
|
+
- undefined: default ttl
|
|
77
86
|
*/ return ttl ? ttl : ttl === 0 ? this.infiniteExpiration : this.defaultTTL;
|
|
78
87
|
}
|
|
79
88
|
serialize(data) {
|
|
@@ -94,8 +103,8 @@ let RedisCacheAdapter = class RedisCacheAdapter {
|
|
|
94
103
|
this.logger = new _common.Logger(_cacheservice.Cache.name.toUpperCase());
|
|
95
104
|
this.reconnectOptions = {
|
|
96
105
|
maxAttempts: 3,
|
|
97
|
-
minConnectDelay:
|
|
98
|
-
maxConnectDelay:
|
|
106
|
+
minConnectDelay: 1000,
|
|
107
|
+
maxConnectDelay: 2000
|
|
99
108
|
};
|
|
100
109
|
this.reconnectStrategy = (attempts)=>{
|
|
101
110
|
if (attempts > this.reconnectOptions.maxAttempts) {
|
|
@@ -114,10 +123,6 @@ let RedisCacheAdapter = class RedisCacheAdapter {
|
|
|
114
123
|
reconnectStrategy: this.reconnectStrategy
|
|
115
124
|
}
|
|
116
125
|
});
|
|
117
|
-
this.client.connect().then(()=>{
|
|
118
|
-
this.client.on('error', (e)=>this.logger.error(e.message || e));
|
|
119
|
-
this.client.on('ready', ()=>this.logger.log(`Connected to Redis Server at ${this.client.options.url}`));
|
|
120
|
-
});
|
|
121
126
|
}
|
|
122
127
|
};
|
|
123
128
|
RedisCacheAdapter = _ts_decorate([
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../backend/src/infrastructure/cache/adapters/redis-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 { Injectable, Logger } from '@nestjs/common'\nimport { RedisClientOptions } from '@redis/client'\nimport { createClient, RedisClientType } from 'redis'\nimport { createSlug } from '../../../common/shared'\nimport { configuration } from '../../../configuration/config.environment'\nimport { Cache } from '../services/cache.service'\n\n@Injectable()\nexport class RedisCacheAdapter implements Cache {\n defaultTTL: number = configuration.cache.ttl\n infiniteExpiration = -1\n private readonly logger = new Logger(Cache.name.toUpperCase())\n private readonly client: RedisClientType\n private readonly reconnectOptions = { maxAttempts: 3, minConnectDelay:
|
|
1
|
+
{"version":3,"sources":["../../../../../backend/src/infrastructure/cache/adapters/redis-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 { Injectable, Logger } from '@nestjs/common'\nimport { RedisClientOptions } from '@redis/client'\nimport { createClient, RedisClientType } from 'redis'\nimport { createSlug } from '../../../common/shared'\nimport { configuration } from '../../../configuration/config.environment'\nimport { Cache } from '../services/cache.service'\n\n@Injectable()\nexport class RedisCacheAdapter implements Cache {\n defaultTTL: number = configuration.cache.ttl\n infiniteExpiration = -1\n private readonly logger = new Logger(Cache.name.toUpperCase())\n private readonly client: RedisClientType\n private readonly reconnectOptions = { maxAttempts: 3, minConnectDelay: 1000, maxConnectDelay: 2000 }\n\n constructor() {\n this.client = createClient({\n url: configuration.cache.redis,\n socket: { noDelay: true, reconnectStrategy: this.reconnectStrategy }\n } satisfies RedisClientOptions)\n }\n\n async onModuleInit() {\n this.client.on('error', (e: Error) => this.logger.error(e.message || e))\n this.client.on('ready', () => this.logger.log(`Connected to Redis Server at ${this.client.options.url}`))\n this.client.connect().catch((e: Error) => this.logger.error(e))\n }\n\n async onModuleDestroy() {\n if (this.client?.isOpen) {\n await this.client.close()\n }\n }\n\n async has(key: string): Promise<boolean> {\n return (await this.client.exists(key)) === 1\n }\n\n async keys(pattern: string): Promise<string[]> {\n const matches: string[] = []\n for await (const keys of this.client.scanIterator({ MATCH: pattern, COUNT: 100 })) {\n matches.push(...keys)\n }\n return matches\n }\n\n async get(key: string): Promise<any> {\n return this.deserialize(await this.client.get(key))\n }\n\n async mget(keys: string[]): Promise<(any | undefined)[]> {\n return (await this.client.mGet(keys)).map((v) => this.deserialize(v))\n }\n\n async set(key: string, data: unknown, ttl?: number): Promise<boolean> {\n const exp = this.getTTL(ttl)\n return (await this.client.set(key, this.serialize(data), { expiration: { type: 'EX', value: exp === -1 ? undefined : exp } })) === 'OK'\n }\n\n async del(key: any): Promise<boolean> {\n return (await this.client.unlink(key)) > 0\n }\n\n async mdel(keys: string[]): Promise<boolean> {\n const multi = this.client.multi()\n for (const key of keys) {\n multi.unlink(key)\n }\n const res = await multi.exec()\n return Array.isArray(res) && res.some((r) => typeof r === 'number' && r > 0)\n }\n\n genSlugKey(...args: any[]): string {\n return createSlug(args.join(' '))\n }\n\n private readonly reconnectStrategy = (attempts: number): number => {\n if (attempts > this.reconnectOptions.maxAttempts) {\n this.logger.error('Too many retries on Redis server. Exiting')\n process.exit()\n } else {\n const wait: number = Math.min(this.reconnectOptions.minConnectDelay * Math.pow(2, attempts), this.reconnectOptions.maxConnectDelay)\n this.logger.warn(`Retrying connection to Redis server in ${wait / 1000}s`)\n return wait\n }\n }\n\n private getTTL(ttl: number): number {\n /* ttl (seconds):\n - 0: infinite expiration\n - undefined: default ttl\n */\n return ttl ? ttl : ttl === 0 ? this.infiniteExpiration : this.defaultTTL\n }\n\n private serialize(data: any) {\n if (data === undefined || data === null) {\n return 'null'\n }\n return JSON.stringify(data)\n }\n\n private deserialize(data: any) {\n if (data === null) {\n return undefined\n }\n return JSON.parse(data)\n }\n}\n"],"names":["RedisCacheAdapter","onModuleInit","client","on","e","logger","error","message","log","options","url","connect","catch","onModuleDestroy","isOpen","close","has","key","exists","keys","pattern","matches","scanIterator","MATCH","COUNT","push","get","deserialize","mget","mGet","map","v","set","data","ttl","exp","getTTL","serialize","expiration","type","value","undefined","del","unlink","mdel","multi","res","exec","Array","isArray","some","r","genSlugKey","args","createSlug","join","infiniteExpiration","defaultTTL","JSON","stringify","parse","configuration","cache","Logger","Cache","name","toUpperCase","reconnectOptions","maxAttempts","minConnectDelay","maxConnectDelay","reconnectStrategy","attempts","process","exit","wait","Math","min","pow","warn","createClient","redis","socket","noDelay"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../backend/src/infrastructure/cache/cache.e2e-spec.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { Test, TestingModule } from '@nestjs/testing'\nimport { LoggerModule } from 'nestjs-pino'\nimport { setTimeout } from 'node:timers/promises'\nimport { DatabaseModule } from '../database/database.module'\nimport { CacheModule } from './cache.module'\nimport { Cache } from './services/cache.service'\n\ndescribe(Cache.name, () => {\n let module: TestingModule\n let cache: Cache\n\n beforeAll(async () => {\n module = await Test.createTestingModule({\n imports: [CacheModule, LoggerModule.forRoot(), DatabaseModule]\n }).compile()\n\n module.useLogger(['fatal'])\n cache = module.get<Cache>(Cache)\n })\n\n afterAll(async () => {\n await module.close()\n })\n\n it('should be defined', () => {\n expect(cache).toBeDefined()\n })\n\n it('should create the key & value', async () => {\n expect(await cache.set('foo', 'bar')).toBe(true)\n expect(await cache.set('undefined', undefined)).toBe(true)\n })\n\n it('should get all keys defined', async () => {\n expect(await cache.keys('*')).toEqual(expect.arrayContaining(['foo', 'undefined']))\n })\n\n it('should has (or not) the key', async () => {\n expect(await cache.has('bar')).toBe(false)\n expect(await cache.has('fo')).toBe(false)\n expect(await cache.has('foo')).toBe(true)\n expect(await cache.has('undefined')).toBe(true)\n })\n\n it('should get value from key', async () => {\n expect(await cache.get('foo')).toBe('bar')\n expect(await cache.get('undefined')).toBeNull()\n expect(await cache.get('unknown')).toBeUndefined()\n })\n\n it('should get values from keys', async () => {\n const values = await cache.mget(['foo', 'undefined'])\n expect(values).toHaveLength(2)\n expect(values[0]).toBe('bar')\n expect(values[1]).toBeNull()\n })\n\n it('should delete the key', async () => {\n expect(await cache.del('foo')).toBe(true)\n expect(await cache.has('foo')).toBe(false)\n expect(await cache.del('undefined')).toBe(true)\n expect(await cache.get('foo')).toBeUndefined()\n expect(await cache.del('unknown')).toBe(false)\n })\n\n it('should search & delete multiple keys', async () => {\n expect(await cache.set('foo', 'bar')).toBe(true)\n expect(await cache.set('foo2', 'bar2')).toBe(true)\n expect(await cache.keys('foo*')).toEqual(expect.arrayContaining(['foo', 'foo2']))\n expect(await cache.mdel(['foo', 'foo2'])).toBe(true)\n expect(await cache.keys('foo*')).toHaveLength(0)\n })\n\n it('should create the key & value with a TTL', async () => {\n expect(await cache.set('foo', 'bar', 1)).toBe(true)\n expect(await cache.get('foo')).toBe('bar')\n await setTimeout(2000)\n expect(await cache.has('foo')).toBe(false)\n expect(await cache.get('foo')).toBeUndefined()\n })\n\n it('should create a slug key from parameters', () => {\n expect(cache.genSlugKey('foo', 'BAR', 12341)).toBe('foo-bar-12341')\n })\n\n // it('should exit if maxConnectRetry is reached', () => {\n // const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {\n // throw new Error('process.exit')\n // })\n // expect(() => RedisCache.redisReconnectStrategy(RedisCache.redisReconnectOptions.maxAttempts + 1)).toThrow()\n // expect(mockExit).toHaveBeenCalledTimes(1)\n // mockExit.mockRestore()\n // })\n //\n // it('should not exit if maxConnectRetry is not reached', () => {\n // expect(RedisCache.redisReconnectStrategy(1)).toBeGreaterThan(0)\n // })\n})\n"],"names":["describe","Cache","name","module","cache","beforeAll","Test","createTestingModule","imports","CacheModule","LoggerModule","forRoot","DatabaseModule","compile","useLogger","get","afterAll","close","it","expect","toBeDefined","set","toBe","undefined","keys","toEqual","arrayContaining","has","toBeNull","toBeUndefined","values","mget","toHaveLength","del","mdel","setTimeout","genSlugKey"],"mappings":"AAAA;;;;CAIC;;;;yBAEmC;4BACP;0BACF;gCACI;6BACH;8BACN;AAEtBA,SAASC,mBAAK,CAACC,IAAI,EAAE;IACnB,IAAIC;IACJ,IAAIC;IAEJC,UAAU;QACRF,SAAS,MAAMG,aAAI,CAACC,mBAAmB,CAAC;YACtCC,SAAS;gBAACC,wBAAW;gBAAEC,wBAAY,CAACC,OAAO;gBAAIC,8BAAc;aAAC;QAChE,GAAGC,OAAO;QAEVV,OAAOW,SAAS,CAAC;YAAC;SAAQ;QAC1BV,QAAQD,OAAOY,GAAG,CAAQd,mBAAK;
|
|
1
|
+
{"version":3,"sources":["../../../../backend/src/infrastructure/cache/cache.e2e-spec.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { Test, TestingModule } from '@nestjs/testing'\nimport { LoggerModule } from 'nestjs-pino'\nimport { setTimeout } from 'node:timers/promises'\nimport { DatabaseModule } from '../database/database.module'\nimport { CacheModule } from './cache.module'\nimport { Cache } from './services/cache.service'\n\ndescribe(Cache.name, () => {\n let module: TestingModule\n let cache: Cache\n\n beforeAll(async () => {\n module = await Test.createTestingModule({\n imports: [CacheModule, LoggerModule.forRoot(), DatabaseModule]\n }).compile()\n\n module.useLogger(['fatal'])\n cache = module.get<Cache>(Cache)\n cache.onModuleInit()\n })\n\n afterAll(async () => {\n await module.close()\n })\n\n it('should be defined', () => {\n expect(cache).toBeDefined()\n })\n\n it('should create the key & value', async () => {\n expect(await cache.set('foo', 'bar')).toBe(true)\n expect(await cache.set('undefined', undefined)).toBe(true)\n })\n\n it('should get all keys defined', async () => {\n expect(await cache.keys('*')).toEqual(expect.arrayContaining(['foo', 'undefined']))\n })\n\n it('should has (or not) the key', async () => {\n expect(await cache.has('bar')).toBe(false)\n expect(await cache.has('fo')).toBe(false)\n expect(await cache.has('foo')).toBe(true)\n expect(await cache.has('undefined')).toBe(true)\n })\n\n it('should get value from key', async () => {\n expect(await cache.get('foo')).toBe('bar')\n expect(await cache.get('undefined')).toBeNull()\n expect(await cache.get('unknown')).toBeUndefined()\n })\n\n it('should get values from keys', async () => {\n const values = await cache.mget(['foo', 'undefined'])\n expect(values).toHaveLength(2)\n expect(values[0]).toBe('bar')\n expect(values[1]).toBeNull()\n })\n\n it('should delete the key', async () => {\n expect(await cache.del('foo')).toBe(true)\n expect(await cache.has('foo')).toBe(false)\n expect(await cache.del('undefined')).toBe(true)\n expect(await cache.get('foo')).toBeUndefined()\n expect(await cache.del('unknown')).toBe(false)\n })\n\n it('should search & delete multiple keys', async () => {\n expect(await cache.set('foo', 'bar')).toBe(true)\n expect(await cache.set('foo2', 'bar2')).toBe(true)\n expect(await cache.keys('foo*')).toEqual(expect.arrayContaining(['foo', 'foo2']))\n expect(await cache.mdel(['foo', 'foo2'])).toBe(true)\n expect(await cache.keys('foo*')).toHaveLength(0)\n })\n\n it('should create the key & value with a TTL', async () => {\n expect(await cache.set('foo', 'bar', 1)).toBe(true)\n expect(await cache.get('foo')).toBe('bar')\n await setTimeout(2000)\n expect(await cache.has('foo')).toBe(false)\n expect(await cache.get('foo')).toBeUndefined()\n })\n\n it('should create a slug key from parameters', () => {\n expect(cache.genSlugKey('foo', 'BAR', 12341)).toBe('foo-bar-12341')\n })\n\n // it('should exit if maxConnectRetry is reached', () => {\n // const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {\n // throw new Error('process.exit')\n // })\n // expect(() => RedisCache.redisReconnectStrategy(RedisCache.redisReconnectOptions.maxAttempts + 1)).toThrow()\n // expect(mockExit).toHaveBeenCalledTimes(1)\n // mockExit.mockRestore()\n // })\n //\n // it('should not exit if maxConnectRetry is not reached', () => {\n // expect(RedisCache.redisReconnectStrategy(1)).toBeGreaterThan(0)\n // })\n})\n"],"names":["describe","Cache","name","module","cache","beforeAll","Test","createTestingModule","imports","CacheModule","LoggerModule","forRoot","DatabaseModule","compile","useLogger","get","onModuleInit","afterAll","close","it","expect","toBeDefined","set","toBe","undefined","keys","toEqual","arrayContaining","has","toBeNull","toBeUndefined","values","mget","toHaveLength","del","mdel","setTimeout","genSlugKey"],"mappings":"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"}
|
|
@@ -24,16 +24,7 @@ function _ts_decorate(decorators, target, key, desc) {
|
|
|
24
24
|
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
25
25
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
26
26
|
}
|
|
27
|
-
function _ts_metadata(k, v) {
|
|
28
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
29
|
-
}
|
|
30
27
|
let CacheModule = class CacheModule {
|
|
31
|
-
async beforeApplicationShutdown() {
|
|
32
|
-
await this.cache.quit();
|
|
33
|
-
}
|
|
34
|
-
constructor(cache){
|
|
35
|
-
this.cache = cache;
|
|
36
|
-
}
|
|
37
28
|
};
|
|
38
29
|
CacheModule = _ts_decorate([
|
|
39
30
|
(0, _common.Global)(),
|
|
@@ -48,11 +39,7 @@ CacheModule = _ts_decorate([
|
|
|
48
39
|
exports: [
|
|
49
40
|
_cacheservice.Cache
|
|
50
41
|
]
|
|
51
|
-
})
|
|
52
|
-
_ts_metadata("design:type", Function),
|
|
53
|
-
_ts_metadata("design:paramtypes", [
|
|
54
|
-
typeof _cacheservice.Cache === "undefined" ? Object : _cacheservice.Cache
|
|
55
|
-
])
|
|
42
|
+
})
|
|
56
43
|
], CacheModule);
|
|
57
44
|
|
|
58
45
|
//# sourceMappingURL=cache.module.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../backend/src/infrastructure/cache/cache.module.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 {
|
|
1
|
+
{"version":3,"sources":["../../../../backend/src/infrastructure/cache/cache.module.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 { Global, Module } from '@nestjs/common'\nimport { SchedulerRegistry } from '@nestjs/schedule'\nimport { configuration } from '../../configuration/config.environment'\nimport { MysqlCacheAdapter } from './adapters/mysql-cache.adapter'\nimport { RedisCacheAdapter } from './adapters/redis-cache.adapter'\nimport { Cache } from './services/cache.service'\n\n@Global()\n@Module({\n providers: [\n {\n provide: Cache,\n useClass: configuration.cache.adapter === 'mysql' ? MysqlCacheAdapter : RedisCacheAdapter\n },\n SchedulerRegistry\n ],\n exports: [Cache]\n})\nexport class CacheModule {}\n"],"names":["CacheModule","providers","provide","Cache","useClass","configuration","cache","adapter","MysqlCacheAdapter","RedisCacheAdapter","SchedulerRegistry","exports"],"mappings":"AAAA;;;;CAIC;;;;+BAoBYA;;;eAAAA;;;wBAlBkB;0BACG;mCACJ;mCACI;mCACA;8BACZ;;;;;;;AAaf,IAAA,AAAMA,cAAN,MAAMA;AAAa;;;;QATxBC,WAAW;YACT;gBACEC,SAASC,mBAAK;gBACdC,UAAUC,gCAAa,CAACC,KAAK,CAACC,OAAO,KAAK,UAAUC,oCAAiB,GAAGC,oCAAiB;YAC3F;YACAC,2BAAiB;SAClB;QACDC,SAAS;YAACR,mBAAK;SAAC"}
|
|
@@ -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
|
|
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\nimport { OnModuleDestroy, OnModuleInit } from '@nestjs/common'\n\nexport abstract class Cache implements OnModuleInit, OnModuleDestroy {\n abstract defaultTTL: number\n abstract infiniteExpiration: number\n\n abstract onModuleInit(): void\n\n abstract onModuleDestroy(): void\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: unknown, 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"],"names":["Cache"],"mappings":"AAAA;;;;CAIC;;;;+BAIqBA;;;eAAAA;;;AAAf,IAAA,AAAeA,QAAf,MAAeA;AA8BtB"}
|