zyket 1.0.30 → 1.0.32
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/index.js +6 -1
- package/package.json +3 -1
- package/src/extensions/Extension.js +11 -0
- package/src/extensions/bullboard/index.js +28 -0
- package/src/extensions/interactive-storage/index.js +161 -0
- package/src/extensions/interactive-storage/routes/browse.js +18 -0
- package/src/extensions/interactive-storage/routes/create-folder.js +26 -0
- package/src/extensions/interactive-storage/routes/delete-folder.js +44 -0
- package/src/extensions/interactive-storage/routes/delete.js +32 -0
- package/src/extensions/interactive-storage/routes/download.js +30 -0
- package/src/extensions/interactive-storage/routes/info.js +20 -0
- package/src/extensions/interactive-storage/routes/upload.js +34 -0
- package/src/kernel/index.js +19 -1
- package/src/services/database/index.js +18 -0
package/index.js
CHANGED
|
@@ -7,6 +7,9 @@ const { Handler, Guard } = require("./src/services/socketio");
|
|
|
7
7
|
const Schedule = require("./src/services/scheduler/Schedule");
|
|
8
8
|
const Event = require("./src/services/events/Event");
|
|
9
9
|
const Worker = require("./src/services/bullmq/Worker");
|
|
10
|
+
const BullBoardExtension = require("./src/extensions/bullboard");
|
|
11
|
+
const Extension = require("./src/extensions/Extension");
|
|
12
|
+
const InteractiveStorageExtension = require("./src/extensions/interactive-storage");
|
|
10
13
|
|
|
11
14
|
|
|
12
15
|
module.exports = {
|
|
@@ -16,5 +19,7 @@ module.exports = {
|
|
|
16
19
|
Handler, Guard,
|
|
17
20
|
Schedule, Event,
|
|
18
21
|
Worker,
|
|
19
|
-
EnvManager
|
|
22
|
+
EnvManager,
|
|
23
|
+
BullBoardExtension, InteractiveStorageExtension,
|
|
24
|
+
Extension
|
|
20
25
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zyket",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.32",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"license": "ISC",
|
|
14
14
|
"description": "",
|
|
15
15
|
"dependencies": {
|
|
16
|
+
"@bull-board/express": "^6.14.2",
|
|
16
17
|
"bullmq": "^5.63.0",
|
|
17
18
|
"colors": "^1.4.0",
|
|
18
19
|
"cors": "^2.8.5",
|
|
@@ -21,6 +22,7 @@
|
|
|
21
22
|
"fast-glob": "^3.3.3",
|
|
22
23
|
"mariadb": "^3.4.5",
|
|
23
24
|
"minio": "^8.0.6",
|
|
25
|
+
"multer": "^2.0.2",
|
|
24
26
|
"node-cron": "^4.2.1",
|
|
25
27
|
"node-dependency-injection": "^3.2.4",
|
|
26
28
|
"prompts": "^2.4.2",
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const Extension = require('../Extension');
|
|
2
|
+
const { createBullBoard } = require('@bull-board/api')
|
|
3
|
+
const { BullMQAdapter } = require('@bull-board/api/bullMQAdapter')
|
|
4
|
+
const { ExpressAdapter } = require('@bull-board/express')
|
|
5
|
+
|
|
6
|
+
module.exports = class BullBoardExtension extends Extension {
|
|
7
|
+
path;
|
|
8
|
+
|
|
9
|
+
constructor({ path = '/bullboard' } = {}) {
|
|
10
|
+
super("BullBoardExtension");
|
|
11
|
+
this.path = path || '/bullboard';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
load(container) {
|
|
15
|
+
if (!container.get('bullmq')) return container.get('logger').warn('BullBoardExtension: bullmq service not found, skipping BullBoard setup');
|
|
16
|
+
const bull = container.get('bullmq')
|
|
17
|
+
const serverAdapter = new ExpressAdapter()
|
|
18
|
+
serverAdapter.setBasePath(this.path)
|
|
19
|
+
|
|
20
|
+
createBullBoard({
|
|
21
|
+
queues: Object.values(bull.queues).map(queue => new BullMQAdapter(queue)),
|
|
22
|
+
serverAdapter
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
const app = container.get('express').app()
|
|
26
|
+
app.use(this.path, serverAdapter.getRouter())
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
const Extension = require('../Extension');
|
|
2
|
+
const multer = require('multer');
|
|
3
|
+
|
|
4
|
+
// Import route handlers
|
|
5
|
+
const uploadHandler = require('./routes/upload');
|
|
6
|
+
const browseHandler = require('./routes/browse');
|
|
7
|
+
const downloadHandler = require('./routes/download');
|
|
8
|
+
const infoHandler = require('./routes/info');
|
|
9
|
+
const deleteHandler = require('./routes/delete');
|
|
10
|
+
const createFolderHandler = require('./routes/create-folder');
|
|
11
|
+
const deleteFolderHandler = require('./routes/delete-folder');
|
|
12
|
+
|
|
13
|
+
module.exports = class InteractiveStorageExtension extends Extension {
|
|
14
|
+
path;
|
|
15
|
+
bucketName;
|
|
16
|
+
maxFileSize;
|
|
17
|
+
middlewares;
|
|
18
|
+
|
|
19
|
+
constructor({ path = '/storage', bucketName = 'dropbox', maxFileSize = 100 * 1024 * 1024, middlewares = [] } = {}) {
|
|
20
|
+
super("InteractiveStorageExtension");
|
|
21
|
+
this.path = path || '/storage';
|
|
22
|
+
this.bucketName = bucketName;
|
|
23
|
+
this.maxFileSize = maxFileSize; // Default 100MB
|
|
24
|
+
this.middlewares = middlewares || [];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
load(container) {
|
|
28
|
+
if (!container.get('s3')) return container.get('logger').warn('InteractiveStorageExtension: s3 service not found, skipping InteractiveStorage setup');
|
|
29
|
+
if (!container.get('express')) return container.get('logger').warn('InteractiveStorageExtension: express service not found, skipping InteractiveStorage setup');
|
|
30
|
+
|
|
31
|
+
const app = container.get('express').app();
|
|
32
|
+
const s3 = container.get('s3');
|
|
33
|
+
const logger = container.get('logger');
|
|
34
|
+
|
|
35
|
+
// Ensure bucket exists
|
|
36
|
+
this.#ensureBucket(s3, logger);
|
|
37
|
+
|
|
38
|
+
// Configure multer for file uploads (memory storage)
|
|
39
|
+
const upload = multer({
|
|
40
|
+
storage: multer.memoryStorage(),
|
|
41
|
+
limits: {
|
|
42
|
+
fileSize: this.maxFileSize
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Bind helper methods
|
|
47
|
+
const normalizePath = this.#normalizePath.bind(this);
|
|
48
|
+
const listFiles = this.#listFiles.bind(this);
|
|
49
|
+
const listFilesAndFolders = this.#listFilesAndFolders.bind(this);
|
|
50
|
+
const getFileStat = this.#getFileStat.bind(this);
|
|
51
|
+
|
|
52
|
+
// Register routes
|
|
53
|
+
app.post(`${this.path}/upload`, upload.array('files'), uploadHandler(s3, this.bucketName, logger, normalizePath));
|
|
54
|
+
app.get(`${this.path}/browse`, browseHandler(s3, this.bucketName, logger, normalizePath, listFilesAndFolders));
|
|
55
|
+
app.get(`${this.path}/download/:fileName`, downloadHandler(s3, this.bucketName, logger, getFileStat));
|
|
56
|
+
app.get(`${this.path}/info/:fileName`, infoHandler(s3, this.bucketName, logger, getFileStat));
|
|
57
|
+
app.delete(`${this.path}/delete`, deleteHandler(s3, this.bucketName, logger));
|
|
58
|
+
app.post(`${this.path}/create-folder`, createFolderHandler(s3, this.bucketName, logger, normalizePath));
|
|
59
|
+
app.delete(`${this.path}/delete-folder`, deleteFolderHandler(s3, this.bucketName, logger, normalizePath, listFiles));
|
|
60
|
+
|
|
61
|
+
logger.info(`InteractiveStorage extension loaded at ${this.path}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async #ensureBucket(s3, logger) {
|
|
65
|
+
try {
|
|
66
|
+
const buckets = await s3.listBuckets();
|
|
67
|
+
const bucketExists = buckets.some(bucket => bucket.name === this.bucketName);
|
|
68
|
+
|
|
69
|
+
if (!bucketExists) {
|
|
70
|
+
await s3.createBucket(this.bucketName);
|
|
71
|
+
logger.info(`Created S3 bucket: ${this.bucketName}`);
|
|
72
|
+
}
|
|
73
|
+
} catch (error) {
|
|
74
|
+
logger.error(`Error ensuring bucket exists: ${error.message}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async #listFiles(s3, prefix = '') {
|
|
79
|
+
return new Promise((resolve, reject) => {
|
|
80
|
+
const files = [];
|
|
81
|
+
const stream = s3.client.listObjectsV2(this.bucketName, prefix, true);
|
|
82
|
+
|
|
83
|
+
stream.on('data', (obj) => {
|
|
84
|
+
files.push({
|
|
85
|
+
name: obj.name,
|
|
86
|
+
size: obj.size,
|
|
87
|
+
lastModified: obj.lastModified,
|
|
88
|
+
etag: obj.etag
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
stream.on('end', () => resolve(files));
|
|
93
|
+
stream.on('error', reject);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async #listFilesAndFolders(s3, prefix = '') {
|
|
98
|
+
return new Promise((resolve, reject) => {
|
|
99
|
+
const items = [];
|
|
100
|
+
const folders = new Set();
|
|
101
|
+
|
|
102
|
+
const stream = s3.client.listObjectsV2(this.bucketName, prefix, false);
|
|
103
|
+
|
|
104
|
+
stream.on('data', (obj) => {
|
|
105
|
+
if (obj.prefix) {
|
|
106
|
+
// This is a folder
|
|
107
|
+
const folderName = obj.prefix.slice(prefix.length).replace(/\/$/, '');
|
|
108
|
+
if (folderName && !folderName.includes('/')) {
|
|
109
|
+
folders.add(folderName);
|
|
110
|
+
}
|
|
111
|
+
} else if (obj.name) {
|
|
112
|
+
// This is a file
|
|
113
|
+
const fileName = obj.name.slice(prefix.length);
|
|
114
|
+
|
|
115
|
+
// Skip folder markers and files in subfolders
|
|
116
|
+
if (fileName === '.folder' || fileName.includes('/')) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
items.push({
|
|
121
|
+
type: 'file',
|
|
122
|
+
name: fileName,
|
|
123
|
+
fullPath: obj.name,
|
|
124
|
+
size: obj.size,
|
|
125
|
+
lastModified: obj.lastModified,
|
|
126
|
+
etag: obj.etag
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
stream.on('end', () => {
|
|
132
|
+
// Add folders to items
|
|
133
|
+
folders.forEach(folder => {
|
|
134
|
+
items.unshift({
|
|
135
|
+
type: 'folder',
|
|
136
|
+
name: folder,
|
|
137
|
+
fullPath: prefix + folder
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
resolve(items);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
stream.on('error', reject);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
#normalizePath(path) {
|
|
149
|
+
// Remove leading/trailing slashes and normalize
|
|
150
|
+
return path.replace(/^\/+|\/+$/g, '').replace(/\/+/g, '/');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async #getFileStat(s3, fileName) {
|
|
154
|
+
return new Promise((resolve, reject) => {
|
|
155
|
+
s3.client.statObject(this.bucketName, fileName, (err, stat) => {
|
|
156
|
+
if (err) return reject(err);
|
|
157
|
+
resolve(stat);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module.exports = (s3, bucketName, logger, normalizePath, listFilesAndFolders) => async (req, res) => {
|
|
2
|
+
try {
|
|
3
|
+
const folder = req.query.folder || '';
|
|
4
|
+
const prefix = folder ? normalizePath(folder) + '/' : '';
|
|
5
|
+
|
|
6
|
+
const items = await listFilesAndFolders(s3, prefix);
|
|
7
|
+
|
|
8
|
+
res.json({
|
|
9
|
+
success: true,
|
|
10
|
+
bucket: bucketName,
|
|
11
|
+
currentPath: folder,
|
|
12
|
+
items: items
|
|
13
|
+
});
|
|
14
|
+
} catch (error) {
|
|
15
|
+
logger.error(`Error browsing files: ${error.message}`);
|
|
16
|
+
res.status(500).json({ success: false, message: error.message });
|
|
17
|
+
}
|
|
18
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module.exports = (s3, bucketName, logger, normalizePath) => async (req, res) => {
|
|
2
|
+
try {
|
|
3
|
+
const { folderPath } = req.body;
|
|
4
|
+
|
|
5
|
+
if (!folderPath) {
|
|
6
|
+
return res.status(400).json({ success: false, message: 'Folder path is required' });
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const normalizedPath = normalizePath(folderPath);
|
|
10
|
+
const folderMarker = `${normalizedPath}/.folder`;
|
|
11
|
+
|
|
12
|
+
// Create an empty marker file to represent the folder
|
|
13
|
+
await s3.saveFile(bucketName, folderMarker, Buffer.from(''), 'text/plain');
|
|
14
|
+
|
|
15
|
+
logger.info(`Created folder: ${normalizedPath}`);
|
|
16
|
+
|
|
17
|
+
res.json({
|
|
18
|
+
success: true,
|
|
19
|
+
message: 'Folder created successfully',
|
|
20
|
+
folderPath: normalizedPath
|
|
21
|
+
});
|
|
22
|
+
} catch (error) {
|
|
23
|
+
logger.error(`Error creating folder: ${error.message}`);
|
|
24
|
+
res.status(500).json({ success: false, message: error.message });
|
|
25
|
+
}
|
|
26
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module.exports = (s3, bucketName, logger, normalizePath, listFiles) => async (req, res) => {
|
|
2
|
+
try {
|
|
3
|
+
const { folderPath } = req.body;
|
|
4
|
+
|
|
5
|
+
if (!folderPath) {
|
|
6
|
+
return res.status(400).json({ success: false, message: 'Folder path is required' });
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const normalizedPath = normalizePath(folderPath);
|
|
10
|
+
const prefix = `${normalizedPath}/`;
|
|
11
|
+
|
|
12
|
+
// List all files in the folder
|
|
13
|
+
const files = await listFiles(s3, prefix);
|
|
14
|
+
|
|
15
|
+
if (files.length === 0) {
|
|
16
|
+
return res.json({
|
|
17
|
+
success: true,
|
|
18
|
+
message: 'Folder is empty or does not exist',
|
|
19
|
+
deletedCount: 0
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Delete all files
|
|
24
|
+
const deletePromises = files.map(file =>
|
|
25
|
+
s3.removeFile(bucketName, file.name).catch(err => ({ error: err.message, fileName: file.name }))
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const results = await Promise.all(deletePromises);
|
|
29
|
+
const errors = results.filter(r => r && r.error);
|
|
30
|
+
const successCount = results.length - errors.length;
|
|
31
|
+
|
|
32
|
+
logger.info(`Deleted folder ${normalizedPath} with ${successCount} files`);
|
|
33
|
+
|
|
34
|
+
res.json({
|
|
35
|
+
success: errors.length === 0,
|
|
36
|
+
message: `Deleted folder and ${successCount} files`,
|
|
37
|
+
deletedCount: successCount,
|
|
38
|
+
errors: errors.length > 0 ? errors : undefined
|
|
39
|
+
});
|
|
40
|
+
} catch (error) {
|
|
41
|
+
logger.error(`Error deleting folder: ${error.message}`);
|
|
42
|
+
res.status(500).json({ success: false, message: error.message });
|
|
43
|
+
}
|
|
44
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module.exports = (s3, bucketName, logger) => async (req, res) => {
|
|
2
|
+
try {
|
|
3
|
+
const { fileName, fileNames } = req.body;
|
|
4
|
+
|
|
5
|
+
// Support both single file and multiple files
|
|
6
|
+
const filesToDelete = fileNames || (fileName ? [fileName] : []);
|
|
7
|
+
|
|
8
|
+
if (!Array.isArray(filesToDelete) || filesToDelete.length === 0) {
|
|
9
|
+
return res.status(400).json({ success: false, message: 'fileName or fileNames array is required' });
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const deletePromises = filesToDelete.map(file =>
|
|
13
|
+
s3.removeFile(bucketName, file).catch(err => ({ error: err.message, fileName: file }))
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const results = await Promise.all(deletePromises);
|
|
17
|
+
const errors = results.filter(r => r && r.error);
|
|
18
|
+
const successCount = results.length - errors.length;
|
|
19
|
+
|
|
20
|
+
logger.info(`Deleted ${successCount} file(s) from S3 dropbox`);
|
|
21
|
+
|
|
22
|
+
res.json({
|
|
23
|
+
success: errors.length === 0,
|
|
24
|
+
message: `Deleted ${successCount} of ${filesToDelete.length} file(s)`,
|
|
25
|
+
deletedCount: successCount,
|
|
26
|
+
errors: errors.length > 0 ? errors : undefined
|
|
27
|
+
});
|
|
28
|
+
} catch (error) {
|
|
29
|
+
logger.error(`Error deleting files: ${error.message}`);
|
|
30
|
+
res.status(500).json({ success: false, message: error.message });
|
|
31
|
+
}
|
|
32
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module.exports = (s3, bucketName, logger, getFileStat) => async (req, res) => {
|
|
2
|
+
try {
|
|
3
|
+
const { fileName } = req.params;
|
|
4
|
+
|
|
5
|
+
// Get file info first
|
|
6
|
+
const stat = await getFileStat(s3, fileName);
|
|
7
|
+
|
|
8
|
+
// Set response headers
|
|
9
|
+
res.setHeader('Content-Type', stat.metaData?.['content-type'] || 'application/octet-stream');
|
|
10
|
+
res.setHeader('Content-Disposition', `attachment; filename="${fileName}"`);
|
|
11
|
+
res.setHeader('Content-Length', stat.size);
|
|
12
|
+
|
|
13
|
+
// Stream the file
|
|
14
|
+
await new Promise((resolve, reject) => {
|
|
15
|
+
s3.client.getObject(bucketName, fileName, (err, stream) => {
|
|
16
|
+
if (err) return reject(err);
|
|
17
|
+
stream.pipe(res);
|
|
18
|
+
stream.on('end', resolve);
|
|
19
|
+
stream.on('error', reject);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
logger.info(`Downloaded file: ${fileName}`);
|
|
24
|
+
} catch (error) {
|
|
25
|
+
logger.error(`Error downloading file: ${error.message}`);
|
|
26
|
+
if (!res.headersSent) {
|
|
27
|
+
res.status(404).json({ success: false, message: 'File not found' });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module.exports = (s3, bucketName, logger, getFileStat) => async (req, res) => {
|
|
2
|
+
try {
|
|
3
|
+
const { fileName } = req.params;
|
|
4
|
+
const stat = await getFileStat(s3, fileName);
|
|
5
|
+
|
|
6
|
+
res.json({
|
|
7
|
+
success: true,
|
|
8
|
+
file: {
|
|
9
|
+
name: fileName,
|
|
10
|
+
size: stat.size,
|
|
11
|
+
lastModified: stat.lastModified,
|
|
12
|
+
etag: stat.etag,
|
|
13
|
+
contentType: stat.metaData?.['content-type']
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
} catch (error) {
|
|
17
|
+
logger.error(`Error getting file info: ${error.message}`);
|
|
18
|
+
res.status(404).json({ success: false, message: 'File not found' });
|
|
19
|
+
}
|
|
20
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module.exports = (s3, bucketName, logger, normalizePath) => async (req, res) => {
|
|
2
|
+
try {
|
|
3
|
+
if (!req.files || req.files.length === 0) {
|
|
4
|
+
return res.status(400).json({ success: false, message: 'No files uploaded' });
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const folder = req.body.folder || '';
|
|
8
|
+
const folderPath = folder ? normalizePath(folder) + '/' : '';
|
|
9
|
+
|
|
10
|
+
const uploadPromises = req.files.map(async (file) => {
|
|
11
|
+
const fileName = `${folderPath}${Date.now()}-${file.originalname}`;
|
|
12
|
+
await s3.saveFile(bucketName, fileName, file.buffer, file.mimetype);
|
|
13
|
+
return {
|
|
14
|
+
originalName: file.originalname,
|
|
15
|
+
fileName: fileName,
|
|
16
|
+
folder: folder,
|
|
17
|
+
size: file.size,
|
|
18
|
+
mimeType: file.mimetype
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const uploadedFiles = await Promise.all(uploadPromises);
|
|
23
|
+
logger.info(`Uploaded ${uploadedFiles.length} file(s) to S3 dropbox`);
|
|
24
|
+
|
|
25
|
+
res.json({
|
|
26
|
+
success: true,
|
|
27
|
+
message: 'Files uploaded successfully',
|
|
28
|
+
files: uploadedFiles
|
|
29
|
+
});
|
|
30
|
+
} catch (error) {
|
|
31
|
+
logger.error(`Error uploading files: ${error.message}`);
|
|
32
|
+
res.status(500).json({ success: false, message: error.message });
|
|
33
|
+
}
|
|
34
|
+
};
|
package/src/kernel/index.js
CHANGED
|
@@ -3,18 +3,22 @@ const EnvManager = require("../utils/EnvManager");
|
|
|
3
3
|
const fs = require("fs");
|
|
4
4
|
const path = require("path");
|
|
5
5
|
const HTTPServer = require("./HTTPServer");
|
|
6
|
+
const Extension = require("../extensions/Extension");
|
|
6
7
|
|
|
7
8
|
module.exports = class Kernel {
|
|
8
9
|
container;
|
|
9
10
|
#services;
|
|
10
11
|
#onSocketConnection;
|
|
11
12
|
#httpServer;
|
|
13
|
+
#extensions = [];
|
|
12
14
|
|
|
13
15
|
constructor({
|
|
14
|
-
services = []
|
|
16
|
+
services = [],
|
|
17
|
+
extensions = [],
|
|
15
18
|
} = { }) {
|
|
16
19
|
this.container = new ContainerBuilder();
|
|
17
20
|
this.#services = services;
|
|
21
|
+
this.#extensions = extensions;
|
|
18
22
|
|
|
19
23
|
// create src folder if not exists
|
|
20
24
|
if (!fs.existsSync(path.join(process.cwd(), "src"))) {
|
|
@@ -41,6 +45,16 @@ module.exports = class Kernel {
|
|
|
41
45
|
});
|
|
42
46
|
}
|
|
43
47
|
|
|
48
|
+
for (const extension of this.#extensions) {
|
|
49
|
+
if (!(extension instanceof Extension)) {
|
|
50
|
+
throw new Error(`Extension ${extension.name} is not an instance of Extension class`);
|
|
51
|
+
}
|
|
52
|
+
this.container.get('logger').debug(`Loading extension ${extension.name}`);
|
|
53
|
+
await extension.load(this.container);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
44
58
|
return this;
|
|
45
59
|
}
|
|
46
60
|
|
|
@@ -58,4 +72,8 @@ module.exports = class Kernel {
|
|
|
58
72
|
|
|
59
73
|
this.container.compile();
|
|
60
74
|
}
|
|
75
|
+
|
|
76
|
+
async #loadExtensions() {
|
|
77
|
+
// Load extensions if any
|
|
78
|
+
}
|
|
61
79
|
}
|
|
@@ -57,6 +57,24 @@ module.exports = class Database extends Service {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
async loadModel(model) {
|
|
61
|
+
if (typeof model !== 'function') {
|
|
62
|
+
this.#container.get('logger').error(`Model ${model} is not a function`);
|
|
63
|
+
throw new Error(`Model ${model} is not a function`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const modelInstance = model({sequelize: this.sequelize, container: this.#container, Sequelize});
|
|
67
|
+
if(this.models[modelInstance.name]) {
|
|
68
|
+
this.#container.get('logger').warn(`Model ${modelInstance.name} is already loaded, cannot load it again`);
|
|
69
|
+
throw new Error(`Model ${modelInstance.name} is already loaded, cannot load it again`);
|
|
70
|
+
}
|
|
71
|
+
this.models[modelInstance.name] = modelInstance;
|
|
72
|
+
|
|
73
|
+
if (modelInstance.associate) {
|
|
74
|
+
modelInstance.associate(this.models);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
60
78
|
#createModelsFolder() {
|
|
61
79
|
const path = 'src/models';
|
|
62
80
|
if (!fs.existsSync(path)) fs.mkdirSync(path);
|