zyket 1.2.10 → 1.2.12
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/.github/workflows/publish.yml +37 -37
- package/README.md +279 -279
- package/bin/cli.js +201 -201
- package/index.js +32 -32
- package/package.json +54 -50
- package/src/Middleware.js +3 -3
- package/src/extensions/Extension.js +10 -10
- package/src/extensions/bullboard/index.js +38 -38
- package/src/extensions/interactive-storage/index.js +162 -162
- package/src/extensions/interactive-storage/middlewares/MulterMiddleware.js +31 -31
- package/src/extensions/interactive-storage/routes/browse.js +31 -31
- package/src/extensions/interactive-storage/routes/create-folder.js +37 -37
- package/src/extensions/interactive-storage/routes/delete-folder.js +57 -57
- package/src/extensions/interactive-storage/routes/delete.js +41 -41
- package/src/extensions/interactive-storage/routes/download.js +47 -47
- package/src/extensions/interactive-storage/routes/info.js +37 -37
- package/src/extensions/interactive-storage/routes/upload.js +46 -46
- package/src/kernel/HTTPServer.js +31 -31
- package/src/kernel/index.js +78 -78
- package/src/services/Service.js +10 -10
- package/src/services/auth/auth.js +7 -7
- package/src/services/auth/index.js +199 -199
- package/src/services/bullmq/Worker.js +7 -7
- package/src/services/bullmq/index.js +92 -92
- package/src/services/cache/index.js +96 -96
- package/src/services/database/index.js +127 -127
- package/src/services/events/Event.js +6 -6
- package/src/services/events/index.js +59 -59
- package/src/services/express/Express.js +248 -248
- package/src/services/express/Middleware.js +7 -7
- package/src/services/express/RedirectResponse.js +8 -8
- package/src/services/express/Route.js +6 -6
- package/src/services/express/index.js +4 -4
- package/src/services/index.js +29 -29
- package/src/services/logger/index.js +80 -80
- package/src/services/s3/index.js +82 -82
- package/src/services/scheduler/Schedule.js +6 -6
- package/src/services/scheduler/index.js +47 -47
- package/src/services/socketio/Guard.js +10 -10
- package/src/services/socketio/Handler.js +10 -10
- package/src/services/socketio/SocketIO.js +159 -132
- package/src/services/socketio/index.js +4 -4
- package/src/services/template-manager/index.js +73 -73
- package/src/templates/default/config/cors.js +5 -1
- package/src/templates/default/config/swagger.js +15 -15
- package/src/templates/default/frontend/main.jsx +15 -15
- package/src/templates/default/frontend/src/hooks/useAuth.jsx +51 -51
- package/src/templates/default/frontend/src/hooks/useLayout.jsx +18 -18
- package/src/templates/default/frontend/src/layouts/auth/index.jsx +45 -45
- package/src/templates/default/frontend/src/layouts/auth/routes.js +17 -17
- package/src/templates/default/frontend/src/layouts/landing/index.jsx +61 -61
- package/src/templates/default/frontend/src/layouts/landing/routes.js +10 -10
- package/src/templates/default/frontend/src/layouts/panel/index.jsx +115 -115
- package/src/templates/default/frontend/src/layouts/panel/routes.js +10 -10
- package/src/templates/default/frontend/src/middlewares/LoggedMiddleware.jsx +21 -21
- package/src/templates/default/frontend/src/middlewares/NotLoggedMiddleware.jsx +14 -14
- package/src/templates/default/frontend/src/store/index.jsx +5 -5
- package/src/templates/default/frontend/src/store/storeAuth.jsx +14 -14
- package/src/templates/default/frontend/src/views/auth/index.jsx +4 -4
- package/src/templates/default/frontend/src/views/auth/register/index.jsx +4 -4
- package/src/templates/default/frontend/src/views/landing/index.jsx +4 -4
- package/src/templates/default/frontend/src/views/panel/dashboard/index.jsx +4 -4
- package/src/templates/default/frontend/styles.css +1 -1
- package/src/templates/default/src/guards/default.js +6 -6
- package/src/templates/default/src/handlers/connection.js +6 -6
- package/src/templates/default/src/handlers/message.js +8 -8
- package/src/templates/default/src/middlewares/default.js +7 -7
- package/src/templates/default/src/routes/[test]/message.js +26 -26
- package/src/templates/default/src/routes/index.js +22 -22
- package/src/templates/default/src/services/auth/auth.js +7 -7
- package/src/templates/default/src/services/auth/index.js +32 -32
- package/src/utils/EnvManager.js +65 -65
|
@@ -1,93 +1,93 @@
|
|
|
1
|
-
const Service = require("../Service");
|
|
2
|
-
const { Queue, QueueEvents, Worker: BullWorker } = require("bullmq");
|
|
3
|
-
const Worker = require("./Worker");
|
|
4
|
-
const fs = require("fs");
|
|
5
|
-
const path = require("path");
|
|
6
|
-
const fg = require('fast-glob');
|
|
7
|
-
|
|
8
|
-
module.exports = class BullMQ extends Service {
|
|
9
|
-
#container
|
|
10
|
-
queues = {};
|
|
11
|
-
queuesEvents = {};
|
|
12
|
-
|
|
13
|
-
constructor(container) {
|
|
14
|
-
super("queues");
|
|
15
|
-
this.#container = container;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
async boot() {
|
|
19
|
-
const queusConfig = this.#getActivatedQueues();
|
|
20
|
-
|
|
21
|
-
for (const queueName of queusConfig) {
|
|
22
|
-
this.queues[queueName] = new Queue(queueName, this.#connection());
|
|
23
|
-
this.queuesEvents[queueName] = new QueueEvents(queueName, this.#connection());
|
|
24
|
-
await this.#container.get('logger').info(`Queue ${queueName} initialized`);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const workers = await this.#loadWorkersFromFolder(path.join(process.cwd(), "src", "workers"));
|
|
28
|
-
|
|
29
|
-
for (const wkr of workers) {
|
|
30
|
-
if(!wkr.queueName) {
|
|
31
|
-
this.#container.get('logger').warn(`Worker ${wkr.name} has no queueName defined, skipping...`);
|
|
32
|
-
continue;
|
|
33
|
-
}
|
|
34
|
-
if (!this.queues[wkr.queueName]) {
|
|
35
|
-
this.#container.get('logger').warn(`Queue ${wkr.queueName} not found for worker ${wkr.name}, skipping...`);
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
new BullWorker(
|
|
39
|
-
wkr.queueName,
|
|
40
|
-
async (job) => wkr.handle({ container: this.#container, job }),
|
|
41
|
-
this.#connection()
|
|
42
|
-
);
|
|
43
|
-
this.#container.get('logger').info(`Worker ${wkr.name} for queue ${wkr.queueName} initialized`);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async addJob(queueName, jobName, data, opts = {}, waitForCompletion = false) {
|
|
48
|
-
if (!this.queues[queueName]) throw new Error(`Queue ${queueName} not found`);
|
|
49
|
-
const job = await this.queues[queueName].add(jobName, data, opts);
|
|
50
|
-
if (!waitForCompletion) return job;
|
|
51
|
-
|
|
52
|
-
return new Promise((resolve, reject) => {
|
|
53
|
-
const queueEvent = this.queuesEvents[queueName];
|
|
54
|
-
job.waitUntilFinished(queueEvent, 1000 * 60).then((result) => {
|
|
55
|
-
resolve(result);
|
|
56
|
-
}).catch((err) => {
|
|
57
|
-
reject(err);
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
async #loadWorkersFromFolder(workersFolder) {
|
|
63
|
-
this.#createWorkersFolder(workersFolder);
|
|
64
|
-
const workers = (await fg('**/*.js', { cwd: workersFolder })).map((wkr) => {
|
|
65
|
-
const worker = require(path.join(workersFolder, wkr));
|
|
66
|
-
if(!(worker.prototype instanceof Worker)) {
|
|
67
|
-
this.#container.get('logger').warn(`File ${wkr} does not export a valid Worker class, skipping...`);
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
return new worker(wkr.replace('.js', ''));
|
|
71
|
-
}).filter(wkr => wkr !== null);
|
|
72
|
-
return workers;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
#createWorkersFolder(workersFolder, overwrite = false) {
|
|
76
|
-
if (fs.existsSync(workersFolder) && !overwrite) return;
|
|
77
|
-
this.#container.get('logger').info(`Creating workers folder at ${workersFolder}`);
|
|
78
|
-
fs.mkdirSync(workersFolder);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
#getActivatedQueues() {
|
|
82
|
-
const list = process.env.QUEUES || "";
|
|
83
|
-
return list.split(",").map(q => q.trim()).filter(q => q.length);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
#connection() {
|
|
87
|
-
return {
|
|
88
|
-
connection: {
|
|
89
|
-
url: process.env.CACHE_URL || "redis://localhost:6379"
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
}
|
|
1
|
+
const Service = require("../Service");
|
|
2
|
+
const { Queue, QueueEvents, Worker: BullWorker } = require("bullmq");
|
|
3
|
+
const Worker = require("./Worker");
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const fg = require('fast-glob');
|
|
7
|
+
|
|
8
|
+
module.exports = class BullMQ extends Service {
|
|
9
|
+
#container
|
|
10
|
+
queues = {};
|
|
11
|
+
queuesEvents = {};
|
|
12
|
+
|
|
13
|
+
constructor(container) {
|
|
14
|
+
super("queues");
|
|
15
|
+
this.#container = container;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async boot() {
|
|
19
|
+
const queusConfig = this.#getActivatedQueues();
|
|
20
|
+
|
|
21
|
+
for (const queueName of queusConfig) {
|
|
22
|
+
this.queues[queueName] = new Queue(queueName, this.#connection());
|
|
23
|
+
this.queuesEvents[queueName] = new QueueEvents(queueName, this.#connection());
|
|
24
|
+
await this.#container.get('logger').info(`Queue ${queueName} initialized`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const workers = await this.#loadWorkersFromFolder(path.join(process.cwd(), "src", "workers"));
|
|
28
|
+
|
|
29
|
+
for (const wkr of workers) {
|
|
30
|
+
if(!wkr.queueName) {
|
|
31
|
+
this.#container.get('logger').warn(`Worker ${wkr.name} has no queueName defined, skipping...`);
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (!this.queues[wkr.queueName]) {
|
|
35
|
+
this.#container.get('logger').warn(`Queue ${wkr.queueName} not found for worker ${wkr.name}, skipping...`);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
new BullWorker(
|
|
39
|
+
wkr.queueName,
|
|
40
|
+
async (job) => wkr.handle({ container: this.#container, job }),
|
|
41
|
+
this.#connection()
|
|
42
|
+
);
|
|
43
|
+
this.#container.get('logger').info(`Worker ${wkr.name} for queue ${wkr.queueName} initialized`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async addJob(queueName, jobName, data, opts = {}, waitForCompletion = false) {
|
|
48
|
+
if (!this.queues[queueName]) throw new Error(`Queue ${queueName} not found`);
|
|
49
|
+
const job = await this.queues[queueName].add(jobName, data, opts);
|
|
50
|
+
if (!waitForCompletion) return job;
|
|
51
|
+
|
|
52
|
+
return new Promise((resolve, reject) => {
|
|
53
|
+
const queueEvent = this.queuesEvents[queueName];
|
|
54
|
+
job.waitUntilFinished(queueEvent, 1000 * 60).then((result) => {
|
|
55
|
+
resolve(result);
|
|
56
|
+
}).catch((err) => {
|
|
57
|
+
reject(err);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async #loadWorkersFromFolder(workersFolder) {
|
|
63
|
+
this.#createWorkersFolder(workersFolder);
|
|
64
|
+
const workers = (await fg('**/*.js', { cwd: workersFolder })).map((wkr) => {
|
|
65
|
+
const worker = require(path.join(workersFolder, wkr));
|
|
66
|
+
if(!(worker.prototype instanceof Worker)) {
|
|
67
|
+
this.#container.get('logger').warn(`File ${wkr} does not export a valid Worker class, skipping...`);
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
return new worker(wkr.replace('.js', ''));
|
|
71
|
+
}).filter(wkr => wkr !== null);
|
|
72
|
+
return workers;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
#createWorkersFolder(workersFolder, overwrite = false) {
|
|
76
|
+
if (fs.existsSync(workersFolder) && !overwrite) return;
|
|
77
|
+
this.#container.get('logger').info(`Creating workers folder at ${workersFolder}`);
|
|
78
|
+
fs.mkdirSync(workersFolder);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
#getActivatedQueues() {
|
|
82
|
+
const list = process.env.QUEUES || "";
|
|
83
|
+
return list.split(",").map(q => q.trim()).filter(q => q.length);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
#connection() {
|
|
87
|
+
return {
|
|
88
|
+
connection: {
|
|
89
|
+
url: process.env.CACHE_URL || "redis://localhost:6379"
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
93
|
}
|
|
@@ -1,97 +1,97 @@
|
|
|
1
|
-
const Service = require("../Service");
|
|
2
|
-
const { createClient } = require("redis");
|
|
3
|
-
|
|
4
|
-
module.exports = class Cache extends Service {
|
|
5
|
-
#container;
|
|
6
|
-
#client;
|
|
7
|
-
#useRedis;
|
|
8
|
-
#memoryCache;
|
|
9
|
-
#expirations;
|
|
10
|
-
|
|
11
|
-
constructor(container, url) {
|
|
12
|
-
super("cache");
|
|
13
|
-
this.#container = container;
|
|
14
|
-
// Check if URL is a valid Redis URL (starts with redis://)
|
|
15
|
-
// Empty string, 'memory', or 'in-memory' will use in-memory cache
|
|
16
|
-
this.#useRedis = !!url && url.startsWith('redis://');
|
|
17
|
-
|
|
18
|
-
if (this.#useRedis) {
|
|
19
|
-
this.#client = createClient({ url });
|
|
20
|
-
} else {
|
|
21
|
-
// Use in-memory cache
|
|
22
|
-
this.#memoryCache = new Map();
|
|
23
|
-
this.#expirations = new Map();
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async boot() {
|
|
28
|
-
if (this.#useRedis) {
|
|
29
|
-
await this.#client.connect();
|
|
30
|
-
this.#container.get('logger').info('Cache service using Redis');
|
|
31
|
-
} else {
|
|
32
|
-
this.#container.get('logger').info('Cache service using in-memory cache');
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async set(key, value) {
|
|
37
|
-
if (this.#useRedis) {
|
|
38
|
-
return await this.#client.set(key, value);
|
|
39
|
-
} else {
|
|
40
|
-
this.#memoryCache.set(key, value);
|
|
41
|
-
return 'OK';
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async get(key) {
|
|
46
|
-
if (this.#useRedis) {
|
|
47
|
-
return await this.#client.get(key);
|
|
48
|
-
} else {
|
|
49
|
-
// Check if the key has expired
|
|
50
|
-
if (this.#expirations.has(key)) {
|
|
51
|
-
const expireTime = this.#expirations.get(key);
|
|
52
|
-
if (Date.now() > expireTime) {
|
|
53
|
-
this.#memoryCache.delete(key);
|
|
54
|
-
this.#expirations.delete(key);
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
return this.#memoryCache.get(key) || null;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
async del(key) {
|
|
63
|
-
if (this.#useRedis) {
|
|
64
|
-
return await this.#client.del(key);
|
|
65
|
-
} else {
|
|
66
|
-
this.#expirations.delete(key);
|
|
67
|
-
return this.#memoryCache.delete(key) ? 1 : 0;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
async keys(pattern) {
|
|
72
|
-
if (this.#useRedis) {
|
|
73
|
-
return await this.#client.keys(pattern);
|
|
74
|
-
} else {
|
|
75
|
-
// Simple pattern matching for memory cache
|
|
76
|
-
const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
|
|
77
|
-
return Array.from(this.#memoryCache.keys()).filter(key => regex.test(key));
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async expire(key, seconds) {
|
|
82
|
-
if (this.#useRedis) {
|
|
83
|
-
return await this.#client.expire(key, seconds);
|
|
84
|
-
} else {
|
|
85
|
-
if (this.#memoryCache.has(key)) {
|
|
86
|
-
const expireTime = Date.now() + (seconds * 1000);
|
|
87
|
-
this.#expirations.set(key, expireTime);
|
|
88
|
-
return 1;
|
|
89
|
-
}
|
|
90
|
-
return 0;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
get client() {
|
|
95
|
-
return this.#useRedis ? this.#client : null;
|
|
96
|
-
}
|
|
1
|
+
const Service = require("../Service");
|
|
2
|
+
const { createClient } = require("redis");
|
|
3
|
+
|
|
4
|
+
module.exports = class Cache extends Service {
|
|
5
|
+
#container;
|
|
6
|
+
#client;
|
|
7
|
+
#useRedis;
|
|
8
|
+
#memoryCache;
|
|
9
|
+
#expirations;
|
|
10
|
+
|
|
11
|
+
constructor(container, url) {
|
|
12
|
+
super("cache");
|
|
13
|
+
this.#container = container;
|
|
14
|
+
// Check if URL is a valid Redis URL (starts with redis://)
|
|
15
|
+
// Empty string, 'memory', or 'in-memory' will use in-memory cache
|
|
16
|
+
this.#useRedis = !!url && url.startsWith('redis://');
|
|
17
|
+
|
|
18
|
+
if (this.#useRedis) {
|
|
19
|
+
this.#client = createClient({ url });
|
|
20
|
+
} else {
|
|
21
|
+
// Use in-memory cache
|
|
22
|
+
this.#memoryCache = new Map();
|
|
23
|
+
this.#expirations = new Map();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async boot() {
|
|
28
|
+
if (this.#useRedis) {
|
|
29
|
+
await this.#client.connect();
|
|
30
|
+
this.#container.get('logger').info('Cache service using Redis');
|
|
31
|
+
} else {
|
|
32
|
+
this.#container.get('logger').info('Cache service using in-memory cache');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async set(key, value) {
|
|
37
|
+
if (this.#useRedis) {
|
|
38
|
+
return await this.#client.set(key, value);
|
|
39
|
+
} else {
|
|
40
|
+
this.#memoryCache.set(key, value);
|
|
41
|
+
return 'OK';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async get(key) {
|
|
46
|
+
if (this.#useRedis) {
|
|
47
|
+
return await this.#client.get(key);
|
|
48
|
+
} else {
|
|
49
|
+
// Check if the key has expired
|
|
50
|
+
if (this.#expirations.has(key)) {
|
|
51
|
+
const expireTime = this.#expirations.get(key);
|
|
52
|
+
if (Date.now() > expireTime) {
|
|
53
|
+
this.#memoryCache.delete(key);
|
|
54
|
+
this.#expirations.delete(key);
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return this.#memoryCache.get(key) || null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async del(key) {
|
|
63
|
+
if (this.#useRedis) {
|
|
64
|
+
return await this.#client.del(key);
|
|
65
|
+
} else {
|
|
66
|
+
this.#expirations.delete(key);
|
|
67
|
+
return this.#memoryCache.delete(key) ? 1 : 0;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async keys(pattern) {
|
|
72
|
+
if (this.#useRedis) {
|
|
73
|
+
return await this.#client.keys(pattern);
|
|
74
|
+
} else {
|
|
75
|
+
// Simple pattern matching for memory cache
|
|
76
|
+
const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
|
|
77
|
+
return Array.from(this.#memoryCache.keys()).filter(key => regex.test(key));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async expire(key, seconds) {
|
|
82
|
+
if (this.#useRedis) {
|
|
83
|
+
return await this.#client.expire(key, seconds);
|
|
84
|
+
} else {
|
|
85
|
+
if (this.#memoryCache.has(key)) {
|
|
86
|
+
const expireTime = Date.now() + (seconds * 1000);
|
|
87
|
+
this.#expirations.set(key, expireTime);
|
|
88
|
+
return 1;
|
|
89
|
+
}
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
get client() {
|
|
95
|
+
return this.#useRedis ? this.#client : null;
|
|
96
|
+
}
|
|
97
97
|
}
|
|
@@ -1,128 +1,128 @@
|
|
|
1
|
-
const { Sequelize, Op } = require("sequelize");
|
|
2
|
-
const Service = require("../Service");
|
|
3
|
-
const fg = require('fast-glob');
|
|
4
|
-
const fs = require('fs');
|
|
5
|
-
const path = require('path');
|
|
6
|
-
const { Umzug, SequelizeStorage } = require('umzug')
|
|
7
|
-
|
|
8
|
-
module.exports = class Database extends Service {
|
|
9
|
-
#container;
|
|
10
|
-
#databaseUrl;
|
|
11
|
-
sequelize
|
|
12
|
-
Op = Op;
|
|
13
|
-
Sequelize = Sequelize;
|
|
14
|
-
|
|
15
|
-
models = {}
|
|
16
|
-
|
|
17
|
-
constructor(container, databaseUrl) {
|
|
18
|
-
super('database');
|
|
19
|
-
this.#container = container;
|
|
20
|
-
this.#databaseUrl = databaseUrl;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async boot() {
|
|
24
|
-
this.#createModelsFolder();
|
|
25
|
-
const dialect = process.env.DATABASE_DIALECT || 'mariadb';
|
|
26
|
-
const options = {
|
|
27
|
-
dialect,
|
|
28
|
-
logging: (msg) => this.#container.get('logger').debug(msg),
|
|
29
|
-
operatorsAliases: 0,
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
// SQLite doesn't support connection pooling
|
|
33
|
-
if (dialect !== 'sqlite') {
|
|
34
|
-
options.pool = {
|
|
35
|
-
max: 40,
|
|
36
|
-
min: 0,
|
|
37
|
-
acquire: 30000,
|
|
38
|
-
idle: 10000
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// For SQLite, handle storage option
|
|
43
|
-
if (dialect === 'sqlite') {
|
|
44
|
-
// If using sqlite dialect, the database URL should be a file path
|
|
45
|
-
// or ':memory:' for in-memory database
|
|
46
|
-
this.sequelize = new Sequelize({
|
|
47
|
-
dialect: 'sqlite',
|
|
48
|
-
storage: this.#databaseUrl.replace('sqlite://', '') || ':memory:',
|
|
49
|
-
logging: (msg) => this.#container.get('logger').debug(msg),
|
|
50
|
-
operatorsAliases: 0,
|
|
51
|
-
});
|
|
52
|
-
} else {
|
|
53
|
-
this.sequelize = new Sequelize(this.#databaseUrl, options);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
await this.sequelize.authenticate();
|
|
57
|
-
|
|
58
|
-
await this.#loadModels();
|
|
59
|
-
|
|
60
|
-
this.#container.get('logger').debug(`Database modesl loaded: ${Object.keys(this.models).join(", ")}`);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async #loadModels() {
|
|
64
|
-
const models = await fg('*.js', { cwd: path.join(process.cwd(), "src", "models") });
|
|
65
|
-
for (const model of models) {
|
|
66
|
-
const modelPath = path.join(process.cwd(), "src", "models", model);
|
|
67
|
-
const modelFunc = require(modelPath);
|
|
68
|
-
if (typeof modelFunc !== 'function') {
|
|
69
|
-
this.#container.get('logger').error(`Model ${model} is not a function`);
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
const modelInstance = modelFunc({sequelize: this.sequelize, container: this.#container, Sequelize});
|
|
73
|
-
this.models[model.replace('.js', '')] = modelInstance;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
for (const model of Object.values(this.models)) {
|
|
77
|
-
if (model.associate) {
|
|
78
|
-
model.associate(this.models);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
async loadModel(model) {
|
|
84
|
-
if (typeof model !== 'function') {
|
|
85
|
-
this.#container.get('logger').error(`Model ${model} is not a function`);
|
|
86
|
-
throw new Error(`Model ${model} is not a function`);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const modelInstance = model({sequelize: this.sequelize, container: this.#container, Sequelize});
|
|
90
|
-
if(this.models[modelInstance.name]) {
|
|
91
|
-
this.#container.get('logger').warn(`Model ${modelInstance.name} is already loaded, cannot load it again`);
|
|
92
|
-
throw new Error(`Model ${modelInstance.name} is already loaded, cannot load it again`);
|
|
93
|
-
}
|
|
94
|
-
this.models[modelInstance.name] = modelInstance;
|
|
95
|
-
|
|
96
|
-
if (modelInstance.associate) {
|
|
97
|
-
modelInstance.associate(this.models);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
async runMigrations(migrationsPath = 'src/models/migrations') {
|
|
102
|
-
const umzug = new Umzug({
|
|
103
|
-
migrations: { glob: `${migrationsPath}/*.js` },
|
|
104
|
-
context: {
|
|
105
|
-
queryInterface: this.#container.get('database').sequelize.getQueryInterface(),
|
|
106
|
-
container: this.#container
|
|
107
|
-
},
|
|
108
|
-
storage: new SequelizeStorage({ sequelize: this.#container.get('database').sequelize }),
|
|
109
|
-
logger: console
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
return await umzug.up()
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
#createModelsFolder() {
|
|
116
|
-
const path = 'src/models';
|
|
117
|
-
if (!fs.existsSync(path)) fs.mkdirSync(path);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
sync() {
|
|
121
|
-
return this.sequelize.sync();
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
forceSync() {
|
|
125
|
-
return this.sequelize.sync({ force: true });
|
|
126
|
-
}
|
|
127
|
-
|
|
1
|
+
const { Sequelize, Op } = require("sequelize");
|
|
2
|
+
const Service = require("../Service");
|
|
3
|
+
const fg = require('fast-glob');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const { Umzug, SequelizeStorage } = require('umzug')
|
|
7
|
+
|
|
8
|
+
module.exports = class Database extends Service {
|
|
9
|
+
#container;
|
|
10
|
+
#databaseUrl;
|
|
11
|
+
sequelize
|
|
12
|
+
Op = Op;
|
|
13
|
+
Sequelize = Sequelize;
|
|
14
|
+
|
|
15
|
+
models = {}
|
|
16
|
+
|
|
17
|
+
constructor(container, databaseUrl) {
|
|
18
|
+
super('database');
|
|
19
|
+
this.#container = container;
|
|
20
|
+
this.#databaseUrl = databaseUrl;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async boot() {
|
|
24
|
+
this.#createModelsFolder();
|
|
25
|
+
const dialect = process.env.DATABASE_DIALECT || 'mariadb';
|
|
26
|
+
const options = {
|
|
27
|
+
dialect,
|
|
28
|
+
logging: (msg) => this.#container.get('logger').debug(msg),
|
|
29
|
+
operatorsAliases: 0,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// SQLite doesn't support connection pooling
|
|
33
|
+
if (dialect !== 'sqlite') {
|
|
34
|
+
options.pool = {
|
|
35
|
+
max: 40,
|
|
36
|
+
min: 0,
|
|
37
|
+
acquire: 30000,
|
|
38
|
+
idle: 10000
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// For SQLite, handle storage option
|
|
43
|
+
if (dialect === 'sqlite') {
|
|
44
|
+
// If using sqlite dialect, the database URL should be a file path
|
|
45
|
+
// or ':memory:' for in-memory database
|
|
46
|
+
this.sequelize = new Sequelize({
|
|
47
|
+
dialect: 'sqlite',
|
|
48
|
+
storage: this.#databaseUrl.replace('sqlite://', '') || ':memory:',
|
|
49
|
+
logging: (msg) => this.#container.get('logger').debug(msg),
|
|
50
|
+
operatorsAliases: 0,
|
|
51
|
+
});
|
|
52
|
+
} else {
|
|
53
|
+
this.sequelize = new Sequelize(this.#databaseUrl, options);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
await this.sequelize.authenticate();
|
|
57
|
+
|
|
58
|
+
await this.#loadModels();
|
|
59
|
+
|
|
60
|
+
this.#container.get('logger').debug(`Database modesl loaded: ${Object.keys(this.models).join(", ")}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async #loadModels() {
|
|
64
|
+
const models = await fg('*.js', { cwd: path.join(process.cwd(), "src", "models") });
|
|
65
|
+
for (const model of models) {
|
|
66
|
+
const modelPath = path.join(process.cwd(), "src", "models", model);
|
|
67
|
+
const modelFunc = require(modelPath);
|
|
68
|
+
if (typeof modelFunc !== 'function') {
|
|
69
|
+
this.#container.get('logger').error(`Model ${model} is not a function`);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
const modelInstance = modelFunc({sequelize: this.sequelize, container: this.#container, Sequelize});
|
|
73
|
+
this.models[model.replace('.js', '')] = modelInstance;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
for (const model of Object.values(this.models)) {
|
|
77
|
+
if (model.associate) {
|
|
78
|
+
model.associate(this.models);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async loadModel(model) {
|
|
84
|
+
if (typeof model !== 'function') {
|
|
85
|
+
this.#container.get('logger').error(`Model ${model} is not a function`);
|
|
86
|
+
throw new Error(`Model ${model} is not a function`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const modelInstance = model({sequelize: this.sequelize, container: this.#container, Sequelize});
|
|
90
|
+
if(this.models[modelInstance.name]) {
|
|
91
|
+
this.#container.get('logger').warn(`Model ${modelInstance.name} is already loaded, cannot load it again`);
|
|
92
|
+
throw new Error(`Model ${modelInstance.name} is already loaded, cannot load it again`);
|
|
93
|
+
}
|
|
94
|
+
this.models[modelInstance.name] = modelInstance;
|
|
95
|
+
|
|
96
|
+
if (modelInstance.associate) {
|
|
97
|
+
modelInstance.associate(this.models);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async runMigrations(migrationsPath = 'src/models/migrations') {
|
|
102
|
+
const umzug = new Umzug({
|
|
103
|
+
migrations: { glob: `${migrationsPath}/*.js` },
|
|
104
|
+
context: {
|
|
105
|
+
queryInterface: this.#container.get('database').sequelize.getQueryInterface(),
|
|
106
|
+
container: this.#container
|
|
107
|
+
},
|
|
108
|
+
storage: new SequelizeStorage({ sequelize: this.#container.get('database').sequelize }),
|
|
109
|
+
logger: console
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
return await umzug.up()
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
#createModelsFolder() {
|
|
116
|
+
const path = 'src/models';
|
|
117
|
+
if (!fs.existsSync(path)) fs.mkdirSync(path);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
sync() {
|
|
121
|
+
return this.sequelize.sync();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
forceSync() {
|
|
125
|
+
return this.sequelize.sync({ force: true });
|
|
126
|
+
}
|
|
127
|
+
|
|
128
128
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
module.exports = class Event {
|
|
2
|
-
name;
|
|
3
|
-
|
|
4
|
-
constructor(name) {
|
|
5
|
-
this.name = name;
|
|
6
|
-
}
|
|
1
|
+
module.exports = class Event {
|
|
2
|
+
name;
|
|
3
|
+
|
|
4
|
+
constructor(name) {
|
|
5
|
+
this.name = name;
|
|
6
|
+
}
|
|
7
7
|
}
|