data-primals-engine 1.0.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/LICENSE.md +7 -0
- package/README.md +169 -0
- package/package.json +72 -0
- package/server.js +85 -0
- package/src/config.js +16 -0
- package/src/constants.js +210 -0
- package/src/core.js +340 -0
- package/src/data.js +495 -0
- package/src/defaultModels.js +1287 -0
- package/src/email.js +349 -0
- package/src/engine.js +168 -0
- package/src/events.js +81 -0
- package/src/gameObject.js +189 -0
- package/src/i18n.js +16855 -0
- package/src/index.js +15 -0
- package/src/middlewares/middleware-mongodb.js +131 -0
- package/src/middlewares/throttle.js +33 -0
- package/src/middlewares/timeout.js +47 -0
- package/src/migrate.js +216 -0
- package/src/migrations/20240522143000-content.js +20 -0
- package/src/modules/assistant.js +296 -0
- package/src/modules/bucket.js +233 -0
- package/src/modules/data.js +5692 -0
- package/src/modules/file.js +192 -0
- package/src/modules/mongodb.js +67 -0
- package/src/modules/swagger.js +18 -0
- package/src/modules/user.js +226 -0
- package/src/modules/workflow.js +1296 -0
- package/src/openai.jobs.js +145 -0
- package/src/packs.js +3799 -0
- package/src/providers.js +61 -0
- package/src/tutorials.js +112 -0
- package/src/user.js +17 -0
- package/src/workers/crypto-worker.js +77 -0
- package/src/workers/import-export-worker.js +36 -0
package/src/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// data-primals-engine/src/index.js
|
|
2
|
+
|
|
3
|
+
// --- Core Engine & Utilities ---
|
|
4
|
+
export { Engine } from './engine.js';
|
|
5
|
+
export { GameObject, Logger, BenchmarkTool } from './gameObject.js';
|
|
6
|
+
export { Config } from './config.js';
|
|
7
|
+
export { event_on, event_trigger, event_off } from './core.js';
|
|
8
|
+
|
|
9
|
+
export { UserProvider } from './providers.js';
|
|
10
|
+
|
|
11
|
+
// --- Database & Data Modules ---
|
|
12
|
+
export { datasCollection, filesCollection, modelsCollection, packsCollection } from './modules/mongodb.js';
|
|
13
|
+
export { searchData, insertData, editData, loadFromDump, dumpUserData, validateRestoreRequest, patchData, deleteData, createModel, getModel, getModels } from './modules/data.js';
|
|
14
|
+
|
|
15
|
+
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import {isIsoDate} from "../core.js";
|
|
2
|
+
|
|
3
|
+
const TEST_REGEX = /^\$|\./;
|
|
4
|
+
const TEST_REGEX_WITHOUT_DOT = /^\$/;
|
|
5
|
+
const REPLACE_REGEX = /^\$|\./g;
|
|
6
|
+
|
|
7
|
+
function isPlainObject(obj) {
|
|
8
|
+
return typeof obj === 'object' && obj !== null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function getTestRegex(allowDots) {
|
|
12
|
+
return allowDots ? TEST_REGEX_WITHOUT_DOT : TEST_REGEX;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function withEach(target, cb) {
|
|
16
|
+
(function act(obj) {
|
|
17
|
+
if (Array.isArray(obj)) {
|
|
18
|
+
obj.forEach(act);
|
|
19
|
+
} else if (isPlainObject(obj)) {
|
|
20
|
+
Object.keys(obj).forEach(function (key) {
|
|
21
|
+
const val = obj[key];
|
|
22
|
+
const resp = cb(obj, val, key);
|
|
23
|
+
if (resp.shouldRecurse) {
|
|
24
|
+
act(obj[resp.key || key]);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
})(target);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function has(target, allowDots) {
|
|
32
|
+
const regex = getTestRegex(allowDots);
|
|
33
|
+
|
|
34
|
+
let hasProhibited = false;
|
|
35
|
+
withEach(target, function (obj, val, key) {
|
|
36
|
+
if (regex.test(key)) {
|
|
37
|
+
hasProhibited = true;
|
|
38
|
+
return { shouldRecurse: false };
|
|
39
|
+
} else {
|
|
40
|
+
return { shouldRecurse: true };
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return hasProhibited;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function _sanitize(target, options) {
|
|
48
|
+
const regex = getTestRegex(options.allowDots);
|
|
49
|
+
|
|
50
|
+
let isSanitized = false;
|
|
51
|
+
let replaceWith = null;
|
|
52
|
+
const dryRun = Boolean(options.dryRun);
|
|
53
|
+
if (!regex.test(options.replaceWith) && options.replaceWith !== '.') {
|
|
54
|
+
replaceWith = options.replaceWith;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const wl = options.whitelist || [];
|
|
58
|
+
RegExp.prototype.toJSON = RegExp.prototype.toString;
|
|
59
|
+
|
|
60
|
+
withEach(target, function (obj, val, key) {
|
|
61
|
+
let shouldRecurse = true;
|
|
62
|
+
|
|
63
|
+
if( (key === 'regex' && obj.input) || key === '$regex' ){
|
|
64
|
+
obj[key] = new RegExp(val, "ui");
|
|
65
|
+
console.log('regex san', obj[key]);
|
|
66
|
+
}
|
|
67
|
+
else if (!wl.includes(key) && regex.test(key)) {
|
|
68
|
+
isSanitized = true;
|
|
69
|
+
// if dryRun is enabled, do not modify the target
|
|
70
|
+
if (dryRun) {
|
|
71
|
+
return {
|
|
72
|
+
shouldRecurse: shouldRecurse,
|
|
73
|
+
key: key,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
delete obj[key];
|
|
77
|
+
if (replaceWith) {
|
|
78
|
+
key = key.replace(REPLACE_REGEX, replaceWith);
|
|
79
|
+
// Avoid to set __proto__ and constructor.prototype
|
|
80
|
+
// https://portswigger.net/daily-swig/prototype-pollution-the-dangerous-and-underrated-vulnerability-impacting-javascript-applications
|
|
81
|
+
// https://snyk.io/vuln/SNYK-JS-LODASH-73638
|
|
82
|
+
if (
|
|
83
|
+
key !== '__proto__' &&
|
|
84
|
+
key !== 'constructor' &&
|
|
85
|
+
key !== 'prototype'
|
|
86
|
+
) {
|
|
87
|
+
obj[key] = val;
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
shouldRecurse = false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
shouldRecurse: shouldRecurse,
|
|
96
|
+
key: key,
|
|
97
|
+
};
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
isSanitized,
|
|
102
|
+
target,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function sanitize(target, options = {}) {
|
|
107
|
+
return _sanitize(target, options).target;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* @param {{replaceWith?: string, onSanitize?: function, dryRun?: boolean}} options
|
|
112
|
+
* @returns {function}
|
|
113
|
+
*/
|
|
114
|
+
export function middleware(options = {}) {
|
|
115
|
+
const hasOnSanitize = typeof options.onSanitize === 'function';
|
|
116
|
+
return function (req, res, next) {
|
|
117
|
+
['body', 'params', 'headers', 'query'].forEach(function (key) {
|
|
118
|
+
if (req[key]) {
|
|
119
|
+
const { target, isSanitized } = _sanitize(req[key], options);
|
|
120
|
+
req[key] = target;
|
|
121
|
+
if (isSanitized && hasOnSanitize) {
|
|
122
|
+
options.onSanitize({
|
|
123
|
+
req,
|
|
124
|
+
key,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
next();
|
|
130
|
+
};
|
|
131
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export const throttleMiddleware = (bps) => function(req, res, next) {
|
|
2
|
+
if (bps > 0) {
|
|
3
|
+
var total = 0;
|
|
4
|
+
var resume = req.socket.resume;
|
|
5
|
+
|
|
6
|
+
// make sure nothing else can resume
|
|
7
|
+
req.socket.resume = function() {};
|
|
8
|
+
|
|
9
|
+
var pulse = setInterval(function() {
|
|
10
|
+
total = total - bps / 100;
|
|
11
|
+
if (total < bps) {
|
|
12
|
+
resume.call(req.socket);
|
|
13
|
+
}
|
|
14
|
+
}, 10);
|
|
15
|
+
|
|
16
|
+
req.on('data', function(chunk) {
|
|
17
|
+
total += chunk.length;
|
|
18
|
+
if (total >= bps) {
|
|
19
|
+
req.socket.pause();
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
req.on('end', function() {
|
|
24
|
+
clearInterval(pulse);
|
|
25
|
+
// restore resume because socket could be reused
|
|
26
|
+
req.socket.resume = resume;
|
|
27
|
+
// future requests need the socket to be flowing
|
|
28
|
+
req.socket.resume();
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
next();
|
|
33
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* Middleware to set a specific request timeout.
|
|
4
|
+
* If the timeout is reached, it sends a 408 Request Timeout response.
|
|
5
|
+
* @param {number} timeoutMs - The timeout in milliseconds.
|
|
6
|
+
* @returns {function(req, res, next): void}
|
|
7
|
+
*/
|
|
8
|
+
// Fichier: server/src/middlewares/timeout.js (suggestion)
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Middleware to set a specific request timeout.
|
|
12
|
+
* If the timeout is reached, it sends a 408 Request Timeout response.
|
|
13
|
+
* @param {number} timeoutMs - The timeout in milliseconds.
|
|
14
|
+
* @returns {function(req, res, next): void}
|
|
15
|
+
*/
|
|
16
|
+
export const setTimeoutMiddleware = (timeoutMs) => {
|
|
17
|
+
return (req, res, next) => {
|
|
18
|
+
// Set the timeout for this specific request's socket
|
|
19
|
+
req.setTimeout(timeoutMs);
|
|
20
|
+
|
|
21
|
+
const timeoutHandler = () => {
|
|
22
|
+
// Check if headers have already been sent
|
|
23
|
+
if (!res.headersSent) {
|
|
24
|
+
res.status(408).json({
|
|
25
|
+
error: true,
|
|
26
|
+
message: `Request Timeout: The server did not receive a complete request message within the time that it was prepared to wait (${timeoutMs}ms).`
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
// req.abort() n'est généralement pas nécessaire ici.
|
|
30
|
+
// La réponse envoyée ci-dessus signale la fin de la requête.
|
|
31
|
+
// Le serveur gérera la fermeture du socket.
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const cleanupListeners = () => {
|
|
35
|
+
req.removeListener('timeout', timeoutHandler);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Assign the timeout handler
|
|
39
|
+
req.on('timeout', timeoutHandler);
|
|
40
|
+
|
|
41
|
+
// Clean up the listener when the response finishes or the connection closes
|
|
42
|
+
res.on('finish', cleanupListeners);
|
|
43
|
+
res.on('close', cleanupListeners);
|
|
44
|
+
|
|
45
|
+
next();
|
|
46
|
+
};
|
|
47
|
+
};
|
package/src/migrate.js
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
// C:/Dev/hackersonline-engine/server/src/migrate.js
|
|
2
|
+
|
|
3
|
+
import process from "node:process";
|
|
4
|
+
import fs from "node:fs/promises";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
// FIX: Import 'pathToFileURL' instead of 'fileURLToPath'
|
|
7
|
+
import { pathToFileURL } from 'node:url';
|
|
8
|
+
import { Engine, MongoDatabase } from "./engine.js";
|
|
9
|
+
import { Logger } from "./gameObject.js";
|
|
10
|
+
import { Config } from "./config.js";
|
|
11
|
+
import chalk from "chalk"; // Pour une sortie plus lisible. Assurez-vous que 'chalk' est dans vos dépendances.
|
|
12
|
+
|
|
13
|
+
// Configuration de base
|
|
14
|
+
Config.Set("modules", ["mongodb"]);
|
|
15
|
+
const MIGRATIONS_DIR = path.resolve(process.cwd(), 'server', 'src', 'migrations');
|
|
16
|
+
const MIGRATIONS_COLLECTION = 'migrations_log';
|
|
17
|
+
|
|
18
|
+
const engine = Engine.Create();
|
|
19
|
+
const port = process.env.MIGRATE_PORT || 7640;
|
|
20
|
+
|
|
21
|
+
engine.start(port, async () => {
|
|
22
|
+
const logger = engine.getComponent(Logger);
|
|
23
|
+
logger.info("Migration Runner started on port " + port);
|
|
24
|
+
|
|
25
|
+
const command = process.argv[2]; // 'up', 'down', 'create', 'status', 'to', 'revert'
|
|
26
|
+
const target = process.argv[3]; // Le nom du fichier de migration cible
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const db = MongoDatabase;
|
|
30
|
+
const { executedNames, allMigrationFiles } = await getMigrationStatus(db);
|
|
31
|
+
|
|
32
|
+
switch (command) {
|
|
33
|
+
case 'create':
|
|
34
|
+
if (!target) throw new Error("Please provide a name for the migration.");
|
|
35
|
+
await createMigrationFile(target, logger);
|
|
36
|
+
break;
|
|
37
|
+
case 'status':
|
|
38
|
+
displayStatus(executedNames, allMigrationFiles, logger);
|
|
39
|
+
break;
|
|
40
|
+
case 'up':
|
|
41
|
+
await runMigrations('up', null, logger, db, executedNames, allMigrationFiles);
|
|
42
|
+
break;
|
|
43
|
+
case 'down':
|
|
44
|
+
// Annule la dernière migration
|
|
45
|
+
await runMigrations('down', 'last', logger, db, executedNames, allMigrationFiles);
|
|
46
|
+
break;
|
|
47
|
+
case 'to':
|
|
48
|
+
if (!target) throw new Error("Please specify a target migration file for the 'to' command.");
|
|
49
|
+
await runMigrations('up', target, logger, db, executedNames, allMigrationFiles);
|
|
50
|
+
break;
|
|
51
|
+
case 'revert':
|
|
52
|
+
if (!target) throw new Error("Please specify a target migration file for the 'revert' command. Use '0' to revert all.");
|
|
53
|
+
await runMigrations('down', target, logger, db, executedNames, allMigrationFiles);
|
|
54
|
+
break;
|
|
55
|
+
default:
|
|
56
|
+
throw new Error(`Unknown command: ${command}. Use 'up', 'down', 'create', 'status', 'to', 'revert'.`);
|
|
57
|
+
}
|
|
58
|
+
} catch (error) {
|
|
59
|
+
logger.error(chalk.red("Migration process failed:"), error.message);
|
|
60
|
+
if (error.stack) {
|
|
61
|
+
logger.error(error.stack);
|
|
62
|
+
}
|
|
63
|
+
process.exit(1); // Sortir avec un code d'erreur
|
|
64
|
+
} finally {
|
|
65
|
+
logger.info("Migration process finished. Shutting down.");
|
|
66
|
+
process.exit(0);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
async function getMigrationStatus(db) {
|
|
71
|
+
const migrationLogCollection = db.collection(MIGRATIONS_COLLECTION);
|
|
72
|
+
const executedMigrations = await migrationLogCollection.find().sort({ name: 1 }).toArray();
|
|
73
|
+
const executedNames = new Set(executedMigrations.map(m => m.name));
|
|
74
|
+
|
|
75
|
+
let allMigrationFiles = [];
|
|
76
|
+
try {
|
|
77
|
+
allMigrationFiles = (await fs.readdir(MIGRATIONS_DIR))
|
|
78
|
+
.filter(file => file.endsWith('.js'))
|
|
79
|
+
.sort();
|
|
80
|
+
} catch (e) {
|
|
81
|
+
if (e.code !== 'ENOENT') throw e;
|
|
82
|
+
// Le dossier n'existe pas, c'est ok s'il n'y a aucune migration.
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return { executedNames, allMigrationFiles };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function displayStatus(executedNames, allMigrationFiles, logger) {
|
|
89
|
+
logger.info(chalk.bold('Migration Status:'));
|
|
90
|
+
if (allMigrationFiles.length === 0) {
|
|
91
|
+
logger.info('No migration files found.');
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
allMigrationFiles.forEach(file => {
|
|
95
|
+
if (executedNames.has(file)) {
|
|
96
|
+
logger.info(`${chalk.green('[Applied]')} ${file}`);
|
|
97
|
+
} else {
|
|
98
|
+
logger.info(`${chalk.yellow('[Pending]')} ${file}`);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function runMigrations(direction, target, logger, db, executedNames, allMigrationFiles) {
|
|
104
|
+
const migrationLogCollection = db.collection(MIGRATIONS_COLLECTION);
|
|
105
|
+
|
|
106
|
+
if (direction === 'up') {
|
|
107
|
+
let pendingMigrations = allMigrationFiles.filter(file => !executedNames.has(file));
|
|
108
|
+
|
|
109
|
+
if (target) {
|
|
110
|
+
const targetIndex = pendingMigrations.findIndex(file => file.startsWith(target) || file === target);
|
|
111
|
+
if (targetIndex === -1) {
|
|
112
|
+
logger.info(`Target migration '${target}' not found or already applied.`);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
pendingMigrations = pendingMigrations.slice(0, targetIndex + 1);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (pendingMigrations.length === 0) {
|
|
119
|
+
logger.info(chalk.green("Database is already up to date."));
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
logger.info(`Applying ${pendingMigrations.length} new migration(s)...`);
|
|
124
|
+
for (const fileName of pendingMigrations) {
|
|
125
|
+
logger.info(`-> ${chalk.cyan('Running UP')}: ${fileName}`);
|
|
126
|
+
const migrationPath = path.resolve(MIGRATIONS_DIR, fileName);
|
|
127
|
+
// FIX: Convert the absolute path to a file URL for dynamic import
|
|
128
|
+
const migration = await import(pathToFileURL(migrationPath).href);
|
|
129
|
+
await migration.up(db);
|
|
130
|
+
await migrationLogCollection.insertOne({ name: fileName, executedAt: new Date() });
|
|
131
|
+
logger.info(` ${chalk.green('OK')} - ${fileName} applied and logged.`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (direction === 'down') {
|
|
136
|
+
const executedFiles = allMigrationFiles
|
|
137
|
+
.filter(file => executedNames.has(file))
|
|
138
|
+
.sort((a, b) => b.localeCompare(a)); // Trier en ordre inverse pour la descente
|
|
139
|
+
|
|
140
|
+
if (executedFiles.length === 0) {
|
|
141
|
+
logger.info(chalk.yellow("No migrations to roll back."));
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
let migrationsToRevert;
|
|
146
|
+
|
|
147
|
+
if (target === 'last') {
|
|
148
|
+
migrationsToRevert = [executedFiles[0]]; // Juste la dernière
|
|
149
|
+
} else if (target === '0') {
|
|
150
|
+
migrationsToRevert = executedFiles; // Toutes les migrations
|
|
151
|
+
} else {
|
|
152
|
+
const targetIndex = executedFiles.findIndex(file => file.startsWith(target) || file === target);
|
|
153
|
+
if (targetIndex === -1) {
|
|
154
|
+
logger.info(`Target migration '${target}' not found among applied migrations.`);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
migrationsToRevert = executedFiles.slice(0, targetIndex);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (!migrationsToRevert || migrationsToRevert.length === 0) {
|
|
161
|
+
logger.info(chalk.green("Already at the target state. No migrations to revert."));
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
logger.info(`Reverting ${migrationsToRevert.length} migration(s)...`);
|
|
166
|
+
for (const fileName of migrationsToRevert) {
|
|
167
|
+
logger.info(`-> ${chalk.cyan('Running DOWN')}: ${fileName}`);
|
|
168
|
+
const migrationPath = path.resolve(MIGRATIONS_DIR, fileName);
|
|
169
|
+
// FIX: Convert the absolute path to a file URL for dynamic import
|
|
170
|
+
const migration = await import(pathToFileURL(migrationPath).href);
|
|
171
|
+
await migration.down(db);
|
|
172
|
+
await migrationLogCollection.deleteOne({ name: fileName });
|
|
173
|
+
logger.info(` ${chalk.green('OK')} - ${fileName} rolled back and removed from log.`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// La fonction createMigrationFile reste la même
|
|
179
|
+
async function createMigrationFile(name, logger) {
|
|
180
|
+
const timestamp = new Date().toISOString().replace(/[-:.]/g, '').slice(0, 14);
|
|
181
|
+
const sanitizedName = name.replace(/[^a-z0-9_]/gi, '-').toLowerCase();
|
|
182
|
+
const fileName = `${timestamp}-${sanitizedName}.js`;
|
|
183
|
+
const filePath = path.join(MIGRATIONS_DIR, fileName);
|
|
184
|
+
|
|
185
|
+
const template = `
|
|
186
|
+
/**
|
|
187
|
+
* Migration: ${name}
|
|
188
|
+
* Created at: ${new Date().toISOString()}
|
|
189
|
+
*/
|
|
190
|
+
import { Db } from 'mongodb';
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* La fonction 'up' est exécutée pour appliquer la migration.
|
|
194
|
+
* @param {Db} db - L'instance de la base de données MongoDB.
|
|
195
|
+
*/
|
|
196
|
+
export const up = async (db) => {
|
|
197
|
+
// Votre logique de migration ici. Exemple :
|
|
198
|
+
// await db.collection('users').updateMany({}, { $set: { newField: 'defaultValue' } });
|
|
199
|
+
console.log("Migration UP: ${fileName}");
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* La fonction 'down' est exécutée pour annuler la migration.
|
|
204
|
+
* @param {Db} db - L'instance de la base de données MongoDB.
|
|
205
|
+
*/
|
|
206
|
+
export const down = async (db) => {
|
|
207
|
+
// Votre logique pour annuler la migration ici. Exemple :
|
|
208
|
+
// await db.collection('users').updateMany({}, { $unset: { newField: '' } });
|
|
209
|
+
console.log("Migration DOWN: ${fileName}");
|
|
210
|
+
};
|
|
211
|
+
`;
|
|
212
|
+
|
|
213
|
+
await fs.mkdir(MIGRATIONS_DIR, { recursive: true });
|
|
214
|
+
await fs.writeFile(filePath, template.trim());
|
|
215
|
+
logger.info(`Created migration file: ${chalk.magenta(filePath)}`);
|
|
216
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration: setting content field to 'fr' in 'content' model
|
|
3
|
+
* Created at: 2024-05-21T10:00:00.000Z
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const up = async (db) => {
|
|
7
|
+
const datasCollection = db.collection("datas");
|
|
8
|
+
const content = await datasCollection.find({ _model:"content"}).toArray();
|
|
9
|
+
await Promise.all(content.map(async (doc) => {
|
|
10
|
+
if (typeof(doc.html)==='string') {
|
|
11
|
+
const c = { "fr" : doc.html };
|
|
12
|
+
await datasCollection.updateOne({_id: doc._id}, {$set: {html: c}});
|
|
13
|
+
}
|
|
14
|
+
}));
|
|
15
|
+
console.log("Migration UP: setting content field to 'fr' in 'content' model");
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const down = async (db) => {
|
|
19
|
+
console.log("Migration DOWN: noting to do");
|
|
20
|
+
};
|