data-primals-engine 1.0.7 → 1.0.9

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.
@@ -16,7 +16,7 @@ import {
16
16
  encryptValue,
17
17
  getDefaultForType,
18
18
  getFieldValueHash,
19
- getUserId,
19
+ getUserId, isDemoUser,
20
20
  isLocalUser
21
21
  } from "../data.js";
22
22
  import {
@@ -88,6 +88,7 @@ import {
88
88
  import {assistantGlobalLimiter} from "./assistant.js";
89
89
  import {getAllPacks} from "../packs.js";
90
90
  import {throttleMiddleware} from "../middlewares/throttle.js";
91
+ import {Config} from "../config.js";
91
92
 
92
93
  // Obtenir le chemin du répertoire courant de manière fiable avec ES Modules
93
94
  const __filename = fileURLToPath(import.meta.url);
@@ -100,7 +101,7 @@ const sseConnections = new Map();
100
101
 
101
102
  const delay = ms => new Promise(res => setTimeout(res, ms));
102
103
 
103
- const backupDir = process.env.BACKUP_DIR || './backups'; // Répertoire de stockage des sauvegardes
104
+ const getBackupDir = () => process.env.BACKUP_DIR || './backups'; // Répertoire de stockage des sauvegardes
104
105
  const execAsync = promisify(exec);
105
106
 
106
107
  let importJobs = {};
@@ -147,7 +148,7 @@ export const jobDumpUserData = async () => {
147
148
 
148
149
  users.forEach((user) =>
149
150
  {
150
- if( /^demo[0-9]{1,2}$/.test(user?.username))
151
+ if( isDemoUser(user) && Config.Get("useDemoAccounts"))
151
152
  return;
152
153
  try {
153
154
  dumpUserData(user).catch(e => {
@@ -383,7 +384,7 @@ export const dataTypes = {
383
384
  }
384
385
  },
385
386
  boolean: {
386
- validate: (value) => typeof value === 'boolean',
387
+ validate: (value) => value === null || typeof value === 'boolean',
387
388
  anonymize: () => {
388
389
  return !!getRandom(0, 1);
389
390
  }
@@ -1459,7 +1460,7 @@ export async function onInit(defaultEngine) {
1459
1460
 
1460
1461
  engine.put('/api/model/:id', [middlewareAuthenticator, userInitiator, setTimeoutMiddleware(15000)], async (req, res) => {
1461
1462
 
1462
- if( !/^demo[0-9]{1,2}$/.test(req.me.username) && isLocalUser(req.me) && !await hasPermission(["API_ADMIN", "API_EDIT_MODEL"], req.me)){
1463
+ if( !(isDemoUser(req.me) && Config.Get("useDemoAccounts")) && isLocalUser(req.me) && !await hasPermission(["API_ADMIN", "API_EDIT_MODEL"], req.me)){
1463
1464
  return res.status(403).json({success: false, error: i18n.t('api.permission.editModel', 'Cannot edit models from the API')})
1464
1465
  }
1465
1466
 
@@ -1698,7 +1699,7 @@ export async function onInit(defaultEngine) {
1698
1699
 
1699
1700
  engine.get('/api/models', [throttle, middlewareAuthenticator, userInitiator, middlewareLogger], async (req, res) => {
1700
1701
 
1701
- if( !/^demo[0-9]{1,2}$/.test(req.me.username) && isLocalUser(req.me) && !await hasPermission(["API_ADMIN", "API_GET_MODELS"], req.me)){
1702
+ if( !(isDemoUser(req.me) && Config.Get("useDemoAccounts")) && isLocalUser(req.me) && !await hasPermission(["API_ADMIN", "API_GET_MODELS"], req.me)){
1702
1703
  return res.status(403).json({success: false, error: i18n.t('api.permission.getModels')})
1703
1704
  }
1704
1705
 
@@ -1726,7 +1727,7 @@ export async function onInit(defaultEngine) {
1726
1727
  return res.status(400).json({error: "Le paramètre 'name' est requis."});
1727
1728
  }
1728
1729
 
1729
- if( !/^demo[0-9]{1,2}$/.test(req.me.username) && isLocalUser(req.me) && !await hasPermission(["API_ADMIN", "API_GET_MODEL"], req.me) && !await hasPermission("API_GET_MODEL_"+modelName, req.me)){
1730
+ if( !(isDemoUser(req.me) && Config.Get("useDemoAccounts")) && isLocalUser(req.me) && !await hasPermission(["API_ADMIN", "API_GET_MODEL"], req.me) && !await hasPermission("API_GET_MODEL_"+modelName, req.me)){
1730
1731
  return res.json({success: false, error: i18n.t('api.permission.getModel')})
1731
1732
  }
1732
1733
 
@@ -1738,7 +1739,7 @@ export async function onInit(defaultEngine) {
1738
1739
  });
1739
1740
  engine.post('/api/model', [throttle, middlewareAuthenticator, userInitiator, middlewareLogger, myFreePremiumAnonymousLimiter], async (req, res) => {
1740
1741
 
1741
- if( !/^demo[0-9]{1,2}$/.test(req.me.username) && isLocalUser(req.me) && !await hasPermission(["API_ADMIN", "API_ADD_MODEL"], req.me) ){
1742
+ if( !(isDemoUser(req.me) && Config.Get("useDemoAccounts")) && isLocalUser(req.me) && !await hasPermission(["API_ADMIN", "API_ADD_MODEL"], req.me) ){
1742
1743
  return res.status(403).json({success: false, error: i18n.t('api.permission.addModel')})
1743
1744
  }
1744
1745
  try {
@@ -1810,7 +1811,7 @@ export async function onInit(defaultEngine) {
1810
1811
  })));
1811
1812
  }
1812
1813
  const install = !!req.fields.install;
1813
- if( install && /^demo[0-9]{1,2}$/.test(req.me.username) ){
1814
+ if( install && (isDemoUser(req.me) && Config.Get("useDemoAccounts")) ){
1814
1815
 
1815
1816
  await datasCollection.deleteMany({ _user: req.me.username});
1816
1817
  await modelsCollection.deleteMany({ _user: req.me.username});
@@ -1846,7 +1847,7 @@ export async function onInit(defaultEngine) {
1846
1847
  return res.status(400).json({error: "Le paramètre 'name' est requis."});
1847
1848
  }
1848
1849
 
1849
- if( !/^demo[0-9]{1,2}$/.test(req.me.username) && isLocalUser(req.me) && (
1850
+ if( !(isDemoUser(req.me) && Config.Get("useDemoAccounts")) && isLocalUser(req.me) && (
1850
1851
  !await hasPermission(["API_ADMIN","API_DELETE_MODEL","API_DELETE_MODEL_"+modelName], req.me) ||
1851
1852
  await hasPermission(["API_DELETE_MODEL_NOT_"+modelName], req.me))){
1852
1853
  return res.status(403).json({success: false, error: i18n.t( "api.permission.deleteModel", { model: modelName})})
@@ -1910,7 +1911,7 @@ export async function onInit(defaultEngine) {
1910
1911
  return res.status(404).json({ error: i18n.t('api.model.notFound', { model: modelId }) });
1911
1912
  }
1912
1913
 
1913
- if( !/^demo[0-9]{1,2}$/.test(req.me.username) && isLocalUser(req.me) && (
1914
+ if( !(isDemoUser(req.me) && Config.Get("useDemoAccounts")) && isLocalUser(req.me) && (
1914
1915
  !await hasPermission(["API_ADMIN", "API_EDIT_MODEL", "API_EDIT_MODEL_"+model.name], req.me) ||
1915
1916
  await hasPermission(["API_EDIT_MODEL_NOT_"+model.name], req.me))){
1916
1917
  return res.status(403).json({success: false, error: i18n.t('api.permission.editModel')})
@@ -2323,7 +2324,7 @@ export async function onInit(defaultEngine) {
2323
2324
 
2324
2325
 
2325
2326
  engine.post('/api/data/removeFromPack', [throttle, middlewareAuthenticator, userInitiator, myFreePremiumAnonymousLimiter], async (req, res) => {
2326
- if( !/^demo[0-9]{1,2}$/.test(req.me.username) && isLocalUser(req.me) && !await hasPermission(["API_ADMIN", "API_CREATE_PACK"], req.me)){
2327
+ if( !(isDemoUser(req.me) && Config.Get("useDemoAccounts")) && isLocalUser(req.me) && !await hasPermission(["API_ADMIN", "API_CREATE_PACK"], req.me)){
2327
2328
  return res.status(403).json({success: false, error: i18n.t('api.permission.createPack')})
2328
2329
  }
2329
2330
  const { itemIds } = req.fields;
@@ -2459,7 +2460,7 @@ export async function onInit(defaultEngine) {
2459
2460
  const { packName, itemIds } = req.fields;
2460
2461
  const user = req.me;
2461
2462
 
2462
- if( !/^demo[0-9]{1,2}$/.test(req.me.username) && isLocalUser(req.me) && !await hasPermission(["API_ADMIN", "API_CREATE_PACK"], req.me)){
2463
+ if( !(isDemoUser(req.me) && Config.Get("useDemoAccounts")) && isLocalUser(req.me) && !await hasPermission(["API_ADMIN", "API_CREATE_PACK"], req.me)){
2463
2464
  return res.status(403).json({success: false, error: i18n.t('api.permission.createPack')})
2464
2465
  }
2465
2466
  // --- Validation ---
@@ -2591,14 +2592,18 @@ export async function onInit(defaultEngine) {
2591
2592
  }
2592
2593
 
2593
2594
  export const createModel = async (data) => {
2594
- return await modelsCollection.insertOne(data);
2595
+ return await getCollection('models').insertOne(data);
2596
+ }
2597
+
2598
+ export const deleteModels = async (filter) => {
2599
+ return await getCollection('models').deleteMany(filter ? filter : {_user: { $exists: false }});
2595
2600
  }
2596
2601
 
2597
2602
  export const getModel = async (modelName, user) => {
2598
2603
  const modelInCache = modelsCache.get(user.username+"@@"+modelName);
2599
2604
  if(modelInCache)
2600
2605
  return modelInCache;
2601
- const model = await modelsCollection.findOne({name: modelName, $and: [{_user: {$exists: true}}, {$or: [{_user: user._user}, {_user: user.username}]}]});
2606
+ const model = await getCollection('models').findOne({name: modelName, $and: [{_user: {$exists: true}}, {$or: [{_user: user._user}, {_user: user.username}]}]});
2602
2607
  if (!model) {
2603
2608
  throw new Error(i18n.t('api.model.notFound', {model: modelName}));
2604
2609
  }
@@ -2606,7 +2611,7 @@ export const getModel = async (modelName, user) => {
2606
2611
  return model;
2607
2612
  }
2608
2613
  export const getModels = async () => {
2609
- return await modelsCollection.find({'$or': [{_user: { $exists: false}}]}).toArray();
2614
+ return await getCollection('models').find({'$or': [{_user: { $exists: false}}]}).toArray();
2610
2615
  }
2611
2616
 
2612
2617
 
@@ -2722,7 +2727,7 @@ export async function checkServerCapacity(incomingDataSize = 0) {
2722
2727
  export const insertData = async (modelName, data, files, user, triggerWorkflow = true, waitForWorkflow = true) => {
2723
2728
 
2724
2729
  // --- Vérification des permissions (inchangée) ---
2725
- if (!/^demo[0-9]{1,2}$/.test(user.username) && isLocalUser(user) && (
2730
+ if (!(isDemoUser(user) && Config.Get("useDemoAccounts")) && isLocalUser(user) && (
2726
2731
  !await hasPermission(["API_ADMIN", "API_ADD_DATA", "API_ADD_DATA_" + modelName], user) ||
2727
2732
  await hasPermission(["API_ADD_DATA_NOT_" + modelName], user))) {
2728
2733
  // Renvoyer une structure d'erreur cohérente
@@ -2998,10 +3003,14 @@ async function processRelations(docToProcess, model, collection, me, idMap) {
2998
3003
  // Phase 3: Traitement des résultats
2999
3004
  findResults.forEach((result, index) => {
3000
3005
  const { field, multiple } = batchFinds[index];
3001
- if (result.data?.length) {
3006
+ if (result.data?.length > 0) {
3007
+ // Cas où des documents sont trouvés
3002
3008
  docToProcess[field] = multiple
3003
3009
  ? result.data.map(r => r._id.toString())
3004
3010
  : result.data[0]._id.toString();
3011
+ } else {
3012
+ // Cas où AUCUN document n'est trouvé : il faut nettoyer le champ !
3013
+ docToProcess[field] = multiple ? [] : null;
3005
3014
  }
3006
3015
  });
3007
3016
 
@@ -3161,28 +3170,29 @@ async function applyFieldFilters(docToProcess, model) {
3161
3170
  /**
3162
3171
  * Valide la structure et le contenu du document selon le modèle
3163
3172
  */
3164
- function validateModelData(doc, model) {
3165
- // 1. Vérification des champs obligatoires
3166
- model.fields.forEach(field =>{
3167
- const value = doc[field.name];
3168
- if (field.required) {
3169
- if (value === undefined && !('default' in field)) {
3170
- throw new Error(i18n.t('api.field.missingRequired', { field: field.name+" ("+model.name+")" }));
3171
- }
3172
- if (value === '' || value === null) {
3173
- throw new Error(i18n.t('api.field.requiredCannotBeEmpty', { field: field.name }));
3173
+ function validateModelData(doc, model, isPatch = false) {
3174
+ if (!isPatch) {
3175
+ model.fields.forEach(field => {
3176
+ const value = doc[field.name];
3177
+ if (field.required) {
3178
+ if (value === undefined && !('default' in field)) {
3179
+ throw new Error(i18n.t('api.field.missingRequired', { field: field.name + " (" + model.name + ")" }));
3180
+ }
3181
+ if (value === '' || value === null) {
3182
+ throw new Error(i18n.t('api.field.requiredCannotBeEmpty', { field: field.name }));
3183
+ }
3174
3184
  }
3175
- }
3176
- });
3185
+ });
3186
+ }
3177
3187
 
3178
- // 2. Validation des types de champs
3188
+ // 2. Validation des types de champs (toujours exécutée pour les champs fournis)
3179
3189
  for (const [fieldName, value] of Object.entries(doc)) {
3180
3190
  const fieldDef = model.fields.find(f => f.name === fieldName);
3181
3191
  if (!fieldDef) continue; // On ignore les champs supplémentaires
3182
3192
 
3183
3193
  const validator = dataTypes[fieldDef.type]?.validate;
3184
3194
  if (validator && !validator(value, fieldDef)) {
3185
- throw new Error(i18n.t('api.field.validationFailed', { field: fieldName, value}));
3195
+ throw new Error(i18n.t('api.field.validationFailed', { field: fieldName, value }));
3186
3196
  }
3187
3197
  }
3188
3198
  }
@@ -3358,9 +3368,11 @@ export const editData = async (modelName, filter, data, files, user, triggerWork
3358
3368
  return await internalEditOrPatchData(modelName, filter, data, files, user, false, triggerWorkflow, waitForWorkflow);
3359
3369
  };
3360
3370
 
3371
+ // Dans src/modules/data.js
3372
+
3361
3373
  const internalEditOrPatchData = async (modelName, filter, data, files, user, isPatch, triggerWorkflow = true, waitForWorkflow = false) => {
3362
3374
  try {
3363
- // Vérification des permissions
3375
+ // 1. Vérification des permissions
3364
3376
  if (user.username !== 'demo' && isLocalUser(user) && (
3365
3377
  !await hasPermission(["API_ADMIN", "API_EDIT_DATA", "API_EDIT_DATA_" + modelName], user) ||
3366
3378
  await hasPermission(["API_EDIT_DATA_NOT_" + modelName], user))) {
@@ -3373,22 +3385,20 @@ const internalEditOrPatchData = async (modelName, filter, data, files, user, isP
3373
3385
  throw new Error(i18n.t("api.model.notFound", {model: modelName}));
3374
3386
  }
3375
3387
 
3376
- // Récupération des documents existants
3388
+ // 2. Récupération des documents existants et de leur hash original
3377
3389
  const existingDocs = (await searchData({user, query: {model: modelName, filter}}))?.data;
3378
3390
  if (!existingDocs || existingDocs.length === 0) {
3379
3391
  return {success: false, error: i18n.t("api.data.notFound")};
3380
3392
  }
3381
-
3382
3393
  const ids = existingDocs.map(d => new ObjectId(d._id));
3394
+ const originalHash = existingDocs[0]._hash; // Sauvegarde du hash avant modification
3383
3395
 
3384
- // Préparation des données de mise à jour
3396
+ // 3. Préparation des données de mise à jour (inchangé)
3385
3397
  const updateData = {...data};
3386
3398
  delete updateData._model;
3387
3399
  delete updateData._user;
3388
3400
 
3389
- const dataToValidateAndHash = { ...existingDocs[0], ...updateData };
3390
-
3391
- // Traitement des fichiers
3401
+ // Traitement des fichiers (inchangé)
3392
3402
  const fileFields = model.fields.filter(f => f.type === 'file' || (f.type === 'array' && f.itemsType === 'file'));
3393
3403
  for (const field of fileFields) {
3394
3404
  if (files?.[field.name+'[0]']) {
@@ -3402,12 +3412,15 @@ const internalEditOrPatchData = async (modelName, filter, data, files, user, isP
3402
3412
  }
3403
3413
  }
3404
3414
 
3405
- // Validation complète si ce n'est pas un patch
3415
+ // 4. Validation adaptée pour patch ou edit (inchangé)
3406
3416
  if (!isPatch) {
3407
- validateModelData(updateData, model);
3417
+ const dataToValidate = { ...existingDocs[0], ...updateData };
3418
+ validateModelData(dataToValidate, model, false);
3419
+ } else {
3420
+ validateModelData(updateData, model, true);
3408
3421
  }
3409
3422
 
3410
- // Vérification des champs uniques
3423
+ // 5. Vérification des champs uniques (inchangé)
3411
3424
  const uniqueFields = model.fields.filter(f => f.unique);
3412
3425
  for (const field of uniqueFields) {
3413
3426
  if (updateData[field.name] !== undefined) {
@@ -3415,31 +3428,27 @@ const internalEditOrPatchData = async (modelName, filter, data, files, user, isP
3415
3428
  _user: user._user || user.username,
3416
3429
  _model: modelName,
3417
3430
  [field.name]: updateData[field.name],
3418
- _id: {$nin: ids} // Exclure les documents actuels
3431
+ _id: {$nin: ids}
3419
3432
  });
3420
-
3421
3433
  if (existing) {
3422
- throw new Error(i18n.t("api.data.duplicateValue", {
3423
- field: field.name,
3424
- value: updateData[field.name]
3425
- }));
3434
+ throw new Error(i18n.t("api.data.duplicateValue", { field: field.name, value: updateData[field.name] }));
3426
3435
  }
3427
3436
  }
3428
3437
  }
3438
+
3439
+ // 6. Traitement des relations (inchangé)
3429
3440
  const relationFields = model.fields.filter(f => f.type === 'relation');
3430
3441
  for (const field of relationFields) {
3431
3442
  if (updateData[field.name] !== undefined) {
3432
3443
  const relationValue = updateData[field.name];
3433
3444
  if (relationValue !== null && typeof relationValue === 'object') {
3434
- // Ajouter l'option preserveIds: true pour conserver les IDs
3435
3445
  const insertedIds = await pushDataUnsecure(relationValue, field.relation, user, { preserveIds: true });
3436
3446
  updateData[field.name] = field.multiple ? insertedIds || [] : insertedIds?.[0] || null;
3437
3447
  }
3438
3448
  }
3439
3449
  }
3440
3450
 
3441
-
3442
- // Gestion des mots de passe et autres filtres
3451
+ // 7. Application des filtres de champ (ex: hashage de mot de passe) (inchangé)
3443
3452
  for (const field of model.fields) {
3444
3453
  if (updateData[field.name] !== undefined && dataTypes[field.type]?.filter) {
3445
3454
  updateData[field.name] = await dataTypes[field.type].filter(
@@ -3449,23 +3458,31 @@ const internalEditOrPatchData = async (modelName, filter, data, files, user, isP
3449
3458
  }
3450
3459
  }
3451
3460
 
3452
- const finalHash = getFieldValueHash(model, dataToValidateAndHash);
3461
+ // 8. Calcul du nouveau hash et préparation des données finales
3462
+ const finalStateForHash = { ...existingDocs[0], ...updateData };
3463
+ const newHash = getFieldValueHash(model, finalStateForHash);
3453
3464
 
3454
- // On ne met à jour que les champs fournis + le nouveau hash
3455
3465
  const finalDataForSet = {
3456
3466
  ...updateData,
3457
- _hash: finalHash
3467
+ _hash: newHash
3458
3468
  };
3459
3469
 
3460
- const hashCheck = await checkHash(user, model, finalHash, existingDocs[0]._id.toString());
3461
- if (hashCheck) {
3462
- throw new Error(i18n.t("api.data.notUniqueData"));
3470
+ // 9. *** CORRECTION LOGIQUE ***
3471
+ // On ne vérifie l'unicité que si le hash a réellement changé.
3472
+ if (newHash !== originalHash) {
3473
+ const hashCheck = await checkHash(user, model, newHash, existingDocs[0]._id.toString());
3474
+ if (hashCheck) {
3475
+ // Le nouvel état du document créerait un doublon.
3476
+ throw new Error(i18n.t("api.data.notUniqueData"));
3477
+ }
3463
3478
  }
3464
3479
 
3480
+ // 10. Exécution de la mise à jour (inchangé)
3465
3481
  const bulkOps = [{ updateMany: { filter: {_id: {$in: ids}}, update: {$set: finalDataForSet} } }];
3466
3482
  const bulkResult = await collection.bulkWrite(bulkOps);
3467
3483
  const modifiedCount = bulkResult.modifiedCount || 0;
3468
3484
 
3485
+ // 11. Tâches post-mise à jour (schedules, workflows) (inchangé)
3469
3486
  if (["workflowTrigger", "alert"].includes(modelName)) {
3470
3487
  await handleScheduledJobs(modelName, existingDocs, collection, finalDataForSet);
3471
3488
  }
@@ -4231,8 +4248,10 @@ export const searchData = async ({user, query}) => {
4231
4248
  pipelines.push({$project: {_user: 0}});
4232
4249
  pipelines.push({$project: {_model: 0}});
4233
4250
  }
4234
- console.log(util.inspect(pipelines, false, 29, true))
4235
4251
 
4252
+ console.log(util.inspect(pipelines, false, 29, true));
4253
+
4254
+ // 4. Exécuter la pipeline
4236
4255
  const ts = parseInt(timeout, 10)/2.0 || searchRequestTimeout;
4237
4256
  const count = await collection.aggregate([...pipelines, { $count: "count" }]).maxTimeMS(ts).toArray();
4238
4257
  let prom = collection.aggregate(pipelines).maxTimeMS(ts);
@@ -4249,7 +4268,7 @@ export const searchData = async ({user, query}) => {
4249
4268
 
4250
4269
  export const importData = async(options, files, user) => {
4251
4270
 
4252
- if( !/^demo[0-9]{1,2}$/.test(user.username) && isLocalUser(user) && !await hasPermission(["API_ADMIN", "API_IMPORT_DATA"], user)){
4271
+ if( !(isDemoUser(user) && Config.Get("useDemoAccounts")) && isLocalUser(user) && !await hasPermission(["API_ADMIN", "API_IMPORT_DATA"], user)){
4253
4272
  return ({ success: false, error: "API_IMPORT_DATA permission needed." });
4254
4273
  }
4255
4274
 
@@ -4602,7 +4621,7 @@ export const exportData= async (options, user) =>{
4602
4621
  // Example using hasPermission:
4603
4622
  // if (user.username !== 'demo' && isLocalUser(user) && !await hasPermission(["API_ADMIN", "API_SEARCH_DATA", "API_SEARCH_DATA_"+modelName], user)) {
4604
4623
  // Example using checkPermission (if it exists and works similarly):
4605
- if (isLocalUser(user) && /^demo[0-9]{1,2}$/.test(user?.username) && !(await hasPermission('API_EXPORT_DATA', user))) { // Adapt this line based on your actual permission function
4624
+ if (isLocalUser(user) && !(isDemoUser(user) && Config.Get("useDemoAccounts")) && !(await hasPermission('API_EXPORT_DATA', user))) { // Adapt this line based on your actual permission function
4606
4625
  console.warn(`User ${userId} lacks permission to search/export model ${modelName}`);
4607
4626
  errors.push(i18n.t('api.permission.searchData', 'Cannot search data from the API') + ` (${modelName})`);
4608
4627
  continue; // Skip this model
@@ -4925,6 +4944,7 @@ export const loadFromDump = async (user, options = {}) => {
4925
4944
  // ...
4926
4945
  let backupFilePath; // Assurez-vous que cette variable est bien définie avec le chemin du fichier .tar.gz
4927
4946
  // Exemple simplifié :
4947
+ const backupDir = getBackupDir();
4928
4948
  const backupFilenameRegex = new RegExp(`^backup_${user.username}_(\\d+)\\.tar\\.gz$`);
4929
4949
  const backupFiles = fs.readdirSync(backupDir).filter(filename => backupFilenameRegex.test(filename));
4930
4950
  if (backupFiles.length === 0) throw new Error(`Aucun fichier de sauvegarde local trouvé pour l'utilisateur ${user.username}.`);
@@ -5001,6 +5021,7 @@ export const loadFromDump = async (user, options = {}) => {
5001
5021
 
5002
5022
  // Fonction pour générer une clé aléatoire et la stocker dans un fichier
5003
5023
  const generateAndStoreKey = (user) => {
5024
+ const backupDir = getBackupDir();
5004
5025
  const keyFile = path.join(backupDir, getObjectHash({id:getUserId(user)})+'_encryption.key');
5005
5026
  const key = crypto.randomBytes(16).toString('hex');
5006
5027
  fs.writeFileSync(keyFile, key, { mode: 0o600 }); // Permissions strictes
@@ -5009,6 +5030,7 @@ const generateAndStoreKey = (user) => {
5009
5030
 
5010
5031
  // Fonction pour lire la clé depuis le fichier
5011
5032
  const readKeyFromFile = (user) => {
5033
+ const backupDir = getBackupDir();
5012
5034
  const keyFile = path.join(backupDir, getObjectHash({id:getUserId(user)})+'_encryption.key');
5013
5035
  if (fs.existsSync(keyFile)) {
5014
5036
  return fs.readFileSync(keyFile, 'utf8');
@@ -5020,6 +5042,7 @@ export const dumpUserData = async (user) => {
5020
5042
  // Déterminer la clé de chiffrement
5021
5043
  // Pour cet exemple, on simule la config S3. Remplace par la vraie récupération.
5022
5044
  const s3Config = user.configS3; // Supposons que l'objet 'user' passé contient déjà 'configS3'
5045
+ const backupDir = getBackupDir();
5023
5046
 
5024
5047
  let encryptedKey = readKeyFromFile(user);
5025
5048
  if (!encryptedKey) {
@@ -5065,7 +5088,7 @@ export const dumpUserData = async (user) => {
5065
5088
  let col;
5066
5089
  for (const collection of collections) {
5067
5090
 
5068
- const colls = ['datas', 'models'];
5091
+ const colls = [getUserCollectionName(user), 'models'];
5069
5092
  if( colls.includes(collection.name) ){
5070
5093
 
5071
5094
  // Exécuter mongodump avec les filtres appropriés
@@ -5151,6 +5174,7 @@ async function manageBackupRotation(user, backupFrequency, s3Config = null) { //
5151
5174
 
5152
5175
  } else {
5153
5176
  logger.info(`Gestion de la rotation des sauvegardes locales pour ${userId}.`);
5177
+ const backupDir = getBackupDir();
5154
5178
  const localFiles = fs.readdirSync(backupDir);
5155
5179
  filesToManage = localFiles
5156
5180
  .filter(f => !fs.lstatSync(path.join(backupDir, f)).isDirectory() && f.startsWith(`backup_${userId}_`) && f.endsWith('.tar.gz'))
@@ -5384,6 +5408,8 @@ export async function installPack(logger, packId, user, lang) {
5384
5408
  if (documents.length === 0) continue;
5385
5409
 
5386
5410
  const docsToInsert = [];
5411
+ console.log(modelName, user);
5412
+
5387
5413
  const modelDefForHash = await getModel(modelName, user);
5388
5414
 
5389
5415
  for (const docSource of documents) {
@@ -2,7 +2,7 @@
2
2
  import process from "process";
3
3
  import {MongoClient as InternalMongoClient} from "mongodb";
4
4
  import {Logger} from "../gameObject.js";
5
- import {MongoDatabase} from "../engine.js";
5
+ import {MongoClient, MongoDatabase} from "../engine.js";
6
6
  import * as tls from "node:tls";
7
7
  import fs from "node:fs";
8
8
 
@@ -23,13 +23,6 @@ export async function onInit(defaultEngine) {
23
23
  } catch (e) {
24
24
 
25
25
  }
26
- // Create a SecureContext object
27
- // Connection URL
28
- const dbUrl = process.env.MONGO_DB_URL || 'mongodb://localhost:27017';
29
- const MongoClient = new InternalMongoClient(dbUrl, {
30
- tls: false, maxPoolSize: 20
31
- });
32
- await MongoClient.connect();
33
26
 
34
27
  modelsCollection = MongoDatabase.collection("models");
35
28
  datasCollection = MongoDatabase.collection("datas");