zyket 1.0.32 → 1.0.34

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zyket",
3
- "version": "1.0.32",
3
+ "version": "1.0.34",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -1,26 +1,26 @@
1
1
  const Extension = require('../Extension');
2
- const multer = require('multer');
3
2
 
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');
3
+ // Import route classes
4
+ const UploadRoute = require('./routes/upload');
5
+ const BrowseRoute = require('./routes/browse');
6
+ const DownloadRoute = require('./routes/download');
7
+ const InfoRoute = require('./routes/info');
8
+ const DeleteRoute = require('./routes/delete');
9
+ const CreateFolderRoute = require('./routes/create-folder');
10
+ const DeleteFolderRoute = require('./routes/delete-folder');
11
+ const MulterMiddleware = require('./middlewares/MulterMiddleware');
12
12
 
13
13
  module.exports = class InteractiveStorageExtension extends Extension {
14
+ static bucketName;
14
15
  path;
15
- bucketName;
16
16
  maxFileSize;
17
17
  middlewares;
18
18
 
19
19
  constructor({ path = '/storage', bucketName = 'dropbox', maxFileSize = 100 * 1024 * 1024, middlewares = [] } = {}) {
20
20
  super("InteractiveStorageExtension");
21
21
  this.path = path || '/storage';
22
- this.bucketName = bucketName;
23
- this.maxFileSize = maxFileSize; // Default 100MB
22
+ InteractiveStorageExtension.bucketName = bucketName;
23
+ this.maxFileSize = maxFileSize;
24
24
  this.middlewares = middlewares || [];
25
25
  }
26
26
 
@@ -28,35 +28,37 @@ module.exports = class InteractiveStorageExtension extends Extension {
28
28
  if (!container.get('s3')) return container.get('logger').warn('InteractiveStorageExtension: s3 service not found, skipping InteractiveStorage setup');
29
29
  if (!container.get('express')) return container.get('logger').warn('InteractiveStorageExtension: express service not found, skipping InteractiveStorage setup');
30
30
 
31
- const app = container.get('express').app();
31
+ const express = container.get('express');
32
32
  const s3 = container.get('s3');
33
33
  const logger = container.get('logger');
34
34
 
35
35
  // Ensure bucket exists
36
36
  this.#ensureBucket(s3, logger);
37
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
38
  // Bind helper methods
47
39
  const normalizePath = this.#normalizePath.bind(this);
48
40
  const listFiles = this.#listFiles.bind(this);
49
41
  const listFilesAndFolders = this.#listFilesAndFolders.bind(this);
50
42
  const getFileStat = this.#getFileStat.bind(this);
51
43
 
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));
44
+ // Create route instances
45
+ const routes = [
46
+ new UploadRoute(`${this.path}/upload`, s3, InteractiveStorageExtension.bucketName, normalizePath),
47
+ new BrowseRoute(`${this.path}/browse`, s3, InteractiveStorageExtension.bucketName, normalizePath, listFilesAndFolders),
48
+ new DownloadRoute(`${this.path}/download/:fileName`, s3, InteractiveStorageExtension.bucketName, getFileStat),
49
+ new InfoRoute(`${this.path}/info/:fileName`, s3, InteractiveStorageExtension.bucketName, getFileStat),
50
+ new DeleteRoute(`${this.path}/delete`, s3, InteractiveStorageExtension.bucketName),
51
+ new CreateFolderRoute(`${this.path}/create-folder`, s3, InteractiveStorageExtension.bucketName, normalizePath),
52
+ new DeleteFolderRoute(`${this.path}/delete-folder`, s3, InteractiveStorageExtension.bucketName, normalizePath, listFiles)
53
+ ];
54
+
55
+ // Add multer middleware to upload route
56
+ routes[0].middlewares = {
57
+ post: [new MulterMiddleware(this.maxFileSize)]
58
+ };
59
+
60
+ // Register routes using the express service pattern
61
+ express.registerRoutes(routes);
60
62
 
61
63
  logger.info(`InteractiveStorage extension loaded at ${this.path}`);
62
64
  }
@@ -64,11 +66,11 @@ module.exports = class InteractiveStorageExtension extends Extension {
64
66
  async #ensureBucket(s3, logger) {
65
67
  try {
66
68
  const buckets = await s3.listBuckets();
67
- const bucketExists = buckets.some(bucket => bucket.name === this.bucketName);
69
+ const bucketExists = buckets.some(bucket => bucket.name === InteractiveStorageExtension.bucketName);
68
70
 
69
71
  if (!bucketExists) {
70
- await s3.createBucket(this.bucketName);
71
- logger.info(`Created S3 bucket: ${this.bucketName}`);
72
+ await s3.createBucket(InteractiveStorageExtension.bucketName);
73
+ logger.info(`Created S3 bucket: ${InteractiveStorageExtension.bucketName}`);
72
74
  }
73
75
  } catch (error) {
74
76
  logger.error(`Error ensuring bucket exists: ${error.message}`);
@@ -78,7 +80,7 @@ module.exports = class InteractiveStorageExtension extends Extension {
78
80
  async #listFiles(s3, prefix = '') {
79
81
  return new Promise((resolve, reject) => {
80
82
  const files = [];
81
- const stream = s3.client.listObjectsV2(this.bucketName, prefix, true);
83
+ const stream = s3.client.listObjectsV2(InteractiveStorageExtension.bucketName, prefix, true);
82
84
 
83
85
  stream.on('data', (obj) => {
84
86
  files.push({
@@ -99,7 +101,7 @@ module.exports = class InteractiveStorageExtension extends Extension {
99
101
  const items = [];
100
102
  const folders = new Set();
101
103
 
102
- const stream = s3.client.listObjectsV2(this.bucketName, prefix, false);
104
+ const stream = s3.client.listObjectsV2(InteractiveStorageExtension.bucketName, prefix, false);
103
105
 
104
106
  stream.on('data', (obj) => {
105
107
  if (obj.prefix) {
@@ -152,7 +154,7 @@ module.exports = class InteractiveStorageExtension extends Extension {
152
154
 
153
155
  async #getFileStat(s3, fileName) {
154
156
  return new Promise((resolve, reject) => {
155
- s3.client.statObject(this.bucketName, fileName, (err, stat) => {
157
+ s3.client.statObject(InteractiveStorageExtension.bucketName, fileName, (err, stat) => {
156
158
  if (err) return reject(err);
157
159
  resolve(stat);
158
160
  });
@@ -0,0 +1,31 @@
1
+ const { Middleware } = require('../../../services/express');
2
+ const multer = require('multer');
3
+
4
+ module.exports = class MulterMiddleware extends Middleware {
5
+ upload;
6
+
7
+ constructor(maxFileSize = 100 * 1024 * 1024) {
8
+ super();
9
+ this.upload = multer({
10
+ storage: multer.memoryStorage(),
11
+ limits: {
12
+ fileSize: maxFileSize
13
+ }
14
+ });
15
+ }
16
+
17
+ async handle({ container, request, response, next }) {
18
+ const uploadMiddleware = this.upload.array('files');
19
+
20
+ return new Promise((resolve, reject) => {
21
+ uploadMiddleware(request, response, (err) => {
22
+ if (err) {
23
+ container.get('logger').error(`Multer error: ${err.message}`);
24
+ return reject(err);
25
+ }
26
+ next();
27
+ resolve();
28
+ });
29
+ });
30
+ }
31
+ };
@@ -1,18 +1,31 @@
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) + '/' : '';
1
+ const { Route } = require('../../../services/express');
2
+
3
+ module.exports = class BrowseRoute extends Route {
4
+ s3;
5
+ bucketName;
6
+ normalizePath;
7
+ listFilesAndFolders;
8
+
9
+ constructor(path, s3, bucketName, normalizePath, listFilesAndFolders) {
10
+ super(path);
11
+ this.s3 = s3;
12
+ this.bucketName = bucketName;
13
+ this.normalizePath = normalizePath;
14
+ this.listFilesAndFolders = listFilesAndFolders;
15
+ }
16
+
17
+ async get({ container, request }) {
18
+ const logger = container.get('logger');
19
+ const folder = request.query.folder || '';
20
+ const prefix = folder ? this.normalizePath(folder) + '/' : '';
5
21
 
6
- const items = await listFilesAndFolders(s3, prefix);
22
+ const items = await this.listFilesAndFolders(this.s3, prefix);
7
23
 
8
- res.json({
24
+ return {
9
25
  success: true,
10
- bucket: bucketName,
26
+ bucket: this.bucketName,
11
27
  currentPath: folder,
12
28
  items: items
13
- });
14
- } catch (error) {
15
- logger.error(`Error browsing files: ${error.message}`);
16
- res.status(500).json({ success: false, message: error.message });
29
+ };
17
30
  }
18
31
  };
@@ -1,26 +1,37 @@
1
- module.exports = (s3, bucketName, logger, normalizePath) => async (req, res) => {
2
- try {
3
- const { folderPath } = req.body;
1
+ const { Route } = require('../../../services/express');
2
+
3
+ module.exports = class CreateFolderRoute extends Route {
4
+ s3;
5
+ bucketName;
6
+ normalizePath;
7
+
8
+ constructor(path, s3, bucketName, normalizePath) {
9
+ super(path);
10
+ this.s3 = s3;
11
+ this.bucketName = bucketName;
12
+ this.normalizePath = normalizePath;
13
+ }
14
+
15
+ async post({ container, request }) {
16
+ const logger = container.get('logger');
17
+ const { folderPath } = request.body;
4
18
 
5
19
  if (!folderPath) {
6
- return res.status(400).json({ success: false, message: 'Folder path is required' });
20
+ return { success: false, message: 'Folder path is required', status: 400 };
7
21
  }
8
22
 
9
- const normalizedPath = normalizePath(folderPath);
23
+ const normalizedPath = this.normalizePath(folderPath);
10
24
  const folderMarker = `${normalizedPath}/.folder`;
11
25
 
12
26
  // Create an empty marker file to represent the folder
13
- await s3.saveFile(bucketName, folderMarker, Buffer.from(''), 'text/plain');
27
+ await this.s3.saveFile(this.bucketName, folderMarker, Buffer.from(''), 'text/plain');
14
28
 
15
29
  logger.info(`Created folder: ${normalizedPath}`);
16
30
 
17
- res.json({
31
+ return {
18
32
  success: true,
19
33
  message: 'Folder created successfully',
20
34
  folderPath: normalizedPath
21
- });
22
- } catch (error) {
23
- logger.error(`Error creating folder: ${error.message}`);
24
- res.status(500).json({ success: false, message: error.message });
35
+ };
25
36
  }
26
37
  };
@@ -1,28 +1,44 @@
1
- module.exports = (s3, bucketName, logger, normalizePath, listFiles) => async (req, res) => {
2
- try {
3
- const { folderPath } = req.body;
1
+ const { Route } = require('../../../services/express');
2
+
3
+ module.exports = class DeleteFolderRoute extends Route {
4
+ s3;
5
+ bucketName;
6
+ normalizePath;
7
+ listFiles;
8
+
9
+ constructor(path, s3, bucketName, normalizePath, listFiles) {
10
+ super(path);
11
+ this.s3 = s3;
12
+ this.bucketName = bucketName;
13
+ this.normalizePath = normalizePath;
14
+ this.listFiles = listFiles;
15
+ }
16
+
17
+ async delete({ container, request }) {
18
+ const logger = container.get('logger');
19
+ const { folderPath } = request.body;
4
20
 
5
21
  if (!folderPath) {
6
- return res.status(400).json({ success: false, message: 'Folder path is required' });
22
+ return { success: false, message: 'Folder path is required', status: 400 };
7
23
  }
8
24
 
9
- const normalizedPath = normalizePath(folderPath);
25
+ const normalizedPath = this.normalizePath(folderPath);
10
26
  const prefix = `${normalizedPath}/`;
11
27
 
12
28
  // List all files in the folder
13
- const files = await listFiles(s3, prefix);
29
+ const files = await this.listFiles(this.s3, prefix);
14
30
 
15
31
  if (files.length === 0) {
16
- return res.json({
32
+ return {
17
33
  success: true,
18
34
  message: 'Folder is empty or does not exist',
19
35
  deletedCount: 0
20
- });
36
+ };
21
37
  }
22
38
 
23
39
  // Delete all files
24
40
  const deletePromises = files.map(file =>
25
- s3.removeFile(bucketName, file.name).catch(err => ({ error: err.message, fileName: file.name }))
41
+ this.s3.removeFile(this.bucketName, file.name).catch(err => ({ error: err.message, fileName: file.name }))
26
42
  );
27
43
 
28
44
  const results = await Promise.all(deletePromises);
@@ -31,14 +47,11 @@ module.exports = (s3, bucketName, logger, normalizePath, listFiles) => async (re
31
47
 
32
48
  logger.info(`Deleted folder ${normalizedPath} with ${successCount} files`);
33
49
 
34
- res.json({
50
+ return {
35
51
  success: errors.length === 0,
36
52
  message: `Deleted folder and ${successCount} files`,
37
53
  deletedCount: successCount,
38
54
  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 });
55
+ };
43
56
  }
44
57
  };
@@ -1,16 +1,28 @@
1
- module.exports = (s3, bucketName, logger) => async (req, res) => {
2
- try {
3
- const { fileName, fileNames } = req.body;
1
+ const { Route } = require('../../../services/express');
2
+
3
+ module.exports = class DeleteRoute extends Route {
4
+ s3;
5
+ bucketName;
6
+
7
+ constructor(path, s3, bucketName) {
8
+ super(path);
9
+ this.s3 = s3;
10
+ this.bucketName = bucketName;
11
+ }
12
+
13
+ async delete({ container, request }) {
14
+ const logger = container.get('logger');
15
+ const { fileName, fileNames } = request.body;
4
16
 
5
17
  // Support both single file and multiple files
6
18
  const filesToDelete = fileNames || (fileName ? [fileName] : []);
7
19
 
8
20
  if (!Array.isArray(filesToDelete) || filesToDelete.length === 0) {
9
- return res.status(400).json({ success: false, message: 'fileName or fileNames array is required' });
21
+ return { success: false, message: 'fileName or fileNames array is required', status: 400 };
10
22
  }
11
23
 
12
24
  const deletePromises = filesToDelete.map(file =>
13
- s3.removeFile(bucketName, file).catch(err => ({ error: err.message, fileName: file }))
25
+ this.s3.removeFile(this.bucketName, file).catch(err => ({ error: err.message, fileName: file }))
14
26
  );
15
27
 
16
28
  const results = await Promise.all(deletePromises);
@@ -19,14 +31,11 @@ module.exports = (s3, bucketName, logger) => async (req, res) => {
19
31
 
20
32
  logger.info(`Deleted ${successCount} file(s) from S3 dropbox`);
21
33
 
22
- res.json({
34
+ return {
23
35
  success: errors.length === 0,
24
36
  message: `Deleted ${successCount} of ${filesToDelete.length} file(s)`,
25
37
  deletedCount: successCount,
26
38
  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 });
39
+ };
31
40
  }
32
41
  };
@@ -1,30 +1,47 @@
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);
1
+ const { Route } = require('../../../services/express');
2
+
3
+ module.exports = class DownloadRoute extends Route {
4
+ s3;
5
+ bucketName;
6
+ getFileStat;
7
+
8
+ constructor(path, s3, bucketName, getFileStat) {
9
+ super(path);
10
+ this.s3 = s3;
11
+ this.bucketName = bucketName;
12
+ this.getFileStat = getFileStat;
13
+ }
14
+
15
+ async get({ container, request, response }) {
16
+ const logger = container.get('logger');
17
+ const { fileName } = request.params;
7
18
 
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);
19
+ try {
20
+ // Get file info first
21
+ const stat = await this.getFileStat(this.s3, fileName);
22
+
23
+ // Set response headers
24
+ response.setHeader('Content-Type', stat.metaData?.['content-type'] || 'application/octet-stream');
25
+ response.setHeader('Content-Disposition', `attachment; filename="${fileName}"`);
26
+ response.setHeader('Content-Length', stat.size);
12
27
 
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);
28
+ // Stream the file
29
+ await new Promise((resolve, reject) => {
30
+ this.s3.client.getObject(this.bucketName, fileName, (err, stream) => {
31
+ if (err) return reject(err);
32
+ stream.pipe(response);
33
+ stream.on('end', resolve);
34
+ stream.on('error', reject);
35
+ });
20
36
  });
21
- });
22
37
 
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' });
38
+ logger.info(`Downloaded file: ${fileName}`);
39
+
40
+ // Return null to indicate response was handled manually
41
+ return null;
42
+ } catch (error) {
43
+ logger.error(`Error downloading file: ${error.message}`);
44
+ return { success: false, message: 'File not found', status: 404 };
28
45
  }
29
46
  }
30
47
  };
@@ -1,20 +1,37 @@
1
- module.exports = (s3, bucketName, logger, getFileStat) => async (req, res) => {
2
- try {
3
- const { fileName } = req.params;
4
- const stat = await getFileStat(s3, fileName);
1
+ const { Route } = require('../../../services/express');
2
+
3
+ module.exports = class InfoRoute extends Route {
4
+ s3;
5
+ bucketName;
6
+ getFileStat;
7
+
8
+ constructor(path, s3, bucketName, getFileStat) {
9
+ super(path);
10
+ this.s3 = s3;
11
+ this.bucketName = bucketName;
12
+ this.getFileStat = getFileStat;
13
+ }
14
+
15
+ async get({ container, request }) {
16
+ const logger = container.get('logger');
5
17
 
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' });
18
+ try {
19
+ const { fileName } = request.params;
20
+ const stat = await this.getFileStat(this.s3, fileName);
21
+
22
+ return {
23
+ success: true,
24
+ file: {
25
+ name: fileName,
26
+ size: stat.size,
27
+ lastModified: stat.lastModified,
28
+ etag: stat.etag,
29
+ contentType: stat.metaData?.['content-type']
30
+ }
31
+ };
32
+ } catch (error) {
33
+ logger.error(`Error getting file info: ${error.message}`);
34
+ return { success: false, message: 'File not found', status: 404 };
35
+ }
19
36
  }
20
37
  };
@@ -1,15 +1,30 @@
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' });
1
+ const { Route } = require('../../../services/express');
2
+
3
+ module.exports = class UploadRoute extends Route {
4
+ s3;
5
+ bucketName;
6
+ normalizePath;
7
+
8
+ constructor(path, s3, bucketName, normalizePath) {
9
+ super(path);
10
+ this.s3 = s3;
11
+ this.bucketName = bucketName;
12
+ this.normalizePath = normalizePath;
13
+ }
14
+
15
+ async post({ container, request }) {
16
+ const logger = container.get('logger');
17
+
18
+ if (!request.files || request.files.length === 0) {
19
+ return { success: false, message: 'No files uploaded', status: 400 };
5
20
  }
6
21
 
7
- const folder = req.body.folder || '';
8
- const folderPath = folder ? normalizePath(folder) + '/' : '';
22
+ const folder = request.body.folder || '';
23
+ const folderPath = folder ? this.normalizePath(folder) + '/' : '';
9
24
 
10
- const uploadPromises = req.files.map(async (file) => {
25
+ const uploadPromises = request.files.map(async (file) => {
11
26
  const fileName = `${folderPath}${Date.now()}-${file.originalname}`;
12
- await s3.saveFile(bucketName, fileName, file.buffer, file.mimetype);
27
+ await this.s3.saveFile(this.bucketName, fileName, file.buffer, file.mimetype);
13
28
  return {
14
29
  originalName: file.originalname,
15
30
  fileName: fileName,
@@ -22,13 +37,10 @@ module.exports = (s3, bucketName, logger, normalizePath) => async (req, res) =>
22
37
  const uploadedFiles = await Promise.all(uploadPromises);
23
38
  logger.info(`Uploaded ${uploadedFiles.length} file(s) to S3 dropbox`);
24
39
 
25
- res.json({
40
+ return {
26
41
  success: true,
27
42
  message: 'Files uploaded successfully',
28
43
  files: uploadedFiles
29
- });
30
- } catch (error) {
31
- logger.error(`Error uploading files: ${error.message}`);
32
- res.status(500).json({ success: false, message: error.message });
44
+ };
33
45
  }
34
46
  };
@@ -1,4 +1,4 @@
1
- const { Sequelize } = require("sequelize");
1
+ const { Sequelize, Op } = require("sequelize");
2
2
  const Service = require("../Service");
3
3
  const fg = require('fast-glob');
4
4
  const fs = require('fs');
@@ -8,6 +8,9 @@ module.exports = class Database extends Service {
8
8
  #container;
9
9
  #databaseUrl;
10
10
  sequelize
11
+ Op = Op;
12
+ Sequelize = Sequelize;
13
+
11
14
  models = {}
12
15
 
13
16
  constructor(container, databaseUrl) {
@@ -94,11 +94,57 @@ module.exports = class Express extends Service {
94
94
  }
95
95
  });
96
96
 
97
+ // Attach Express to HTTP server - this allows dynamic route registration
98
+ this.#httpServer.removeAllListeners("request");
97
99
  this.#httpServer.on("request", this.#app);
98
100
 
99
101
  this.#container.get('logger').info(`Express is running on http://localhost:${httpServer.address().port}`);
100
102
  }
101
103
 
104
+ async registerRoutes(routes) {
105
+ const methods = ['post', 'get', 'put', 'delete']
106
+ for (const route of routes) {
107
+ for (const methodName of methods) {
108
+ const method = route[methodName];
109
+ if(!method) continue;
110
+ this.#container.get('logger').debug(`Registering route: [${methodName}] ${route.path}`);
111
+ const middlewares = route?.middlewares?.[methodName] || [];
112
+ for (const mw of middlewares) {
113
+ if (!(mw instanceof Middleware)) {
114
+ throw new Error(`Middleware for route ${route.path} is not an instance of Middleware`);
115
+ }
116
+ }
117
+ this.#app[methodName](
118
+ route.path,
119
+ ...middlewares.map(mw => async (req, res, next) => {
120
+ try {
121
+ await mw.handle({ container: this.#container, request: req, response: res, next })
122
+ } catch (error) {
123
+ this.#container.get('logger').error(`Error in middleware for route [${methodName}] ${route.path}: ${error.message}`);
124
+ return res.status(500).json({ success: false, message: error.message || 'Internal Server Error' });
125
+ }
126
+ }),
127
+ async (req, res) => {
128
+ try {
129
+ const routeResponse = await route[methodName]({ container: this.#container, request: req, response: res });
130
+ const status = routeResponse?.status || 200;
131
+ return res.status(status).json({
132
+ ...routeResponse,
133
+ success: routeResponse?.success !== false,
134
+ });
135
+ } catch (error) {
136
+ this.#container.get('logger').error(`Error in route [${methodName}] ${route.path}: ${error.message}`);
137
+ return res.status(500).json({ success: false, message: error.message || 'Internal Server Error' });
138
+ }
139
+ });
140
+ }
141
+ }
142
+
143
+ this.#httpServer.removeAllListeners("request");
144
+ this.#httpServer.on("request", this.#app);
145
+
146
+ }
147
+
102
148
  async #loadRoutesFromFolder(routesFolder) {
103
149
  this.#createRoutesFolder(routesFolder);
104
150
  const routes = (await fg('**/*.js', { cwd: routesFolder })).map((rt) => {