@cocreate/lazy-loader 1.10.0 → 1.11.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/CHANGELOG.md +12 -0
- package/package.json +2 -1
- package/src/server.js +137 -43
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
# [1.11.0](https://github.com/CoCreate-app/CoCreate-lazy-loader/compare/v1.10.0...v1.11.0) (2023-12-09)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* add "@cocreate/config" ([bac22b5](https://github.com/CoCreate-app/CoCreate-lazy-loader/commit/bac22b5d4e5bafd83e68fe1ca727b184f07e8231))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* handle http and websocket requests ([07b205c](https://github.com/CoCreate-app/CoCreate-lazy-loader/commit/07b205c31527e10c55f716a6ea1db243bebcccfd))
|
|
12
|
+
|
|
1
13
|
# [1.10.0](https://github.com/CoCreate-app/CoCreate-lazy-loader/compare/v1.9.0...v1.10.0) (2023-12-01)
|
|
2
14
|
|
|
3
15
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cocreate/lazy-loader",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"description": "A simple lazy-loader component in vanilla javascript. Easily configured using HTML5 data-attributes and/or JavaScript API.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"lazy-loader",
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
"webpack-log": "^3.0.1"
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
+
"@cocreate/config": "^1.10.0",
|
|
61
62
|
"@cocreate/observer": "^1.14.0"
|
|
62
63
|
}
|
|
63
64
|
}
|
package/src/server.js
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
const EventEmitter = require('events');
|
|
2
|
-
const eventEmitter = new EventEmitter();
|
|
3
1
|
const fs = require('fs').promises;
|
|
4
2
|
const path = require('path');
|
|
5
3
|
const vm = require('vm');
|
|
6
4
|
const Config = require("@cocreate/config");
|
|
5
|
+
const { URL } = require('url');
|
|
6
|
+
|
|
7
|
+
const organizations = {};
|
|
8
|
+
const hosts = {};
|
|
7
9
|
|
|
8
10
|
class CoCreateLazyLoader {
|
|
9
|
-
constructor(crud) {
|
|
11
|
+
constructor(server, crud, files) {
|
|
12
|
+
this.server = server
|
|
10
13
|
this.wsManager = crud.wsManager
|
|
11
14
|
this.crud = crud
|
|
15
|
+
this.files = files
|
|
12
16
|
this.exclusion = { ...require.cache };
|
|
13
17
|
this.modules = {};
|
|
14
18
|
this.init()
|
|
@@ -30,70 +34,160 @@ class CoCreateLazyLoader {
|
|
|
30
34
|
// Call this function at the start of your application
|
|
31
35
|
createScriptsDirectory();
|
|
32
36
|
|
|
33
|
-
|
|
37
|
+
// TODO: return the value so it can be applied directly to modules
|
|
38
|
+
// this.modules[key] = await Config('modules', false, false)
|
|
39
|
+
|
|
40
|
+
const config = await Config('modules', false, false)
|
|
34
41
|
if (!config)
|
|
35
42
|
return
|
|
36
43
|
|
|
37
|
-
for (let
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
this.executeScriptWithTimeout(
|
|
44
|
+
for (let name of Object.keys(config.modules)) {
|
|
45
|
+
this.modules[name] = config.modules[name];
|
|
46
|
+
this.wsManager.on(this.modules[name].event, async (data) => {
|
|
47
|
+
this.executeScriptWithTimeout(name, data)
|
|
41
48
|
});
|
|
42
49
|
}
|
|
43
50
|
|
|
44
|
-
|
|
51
|
+
this.server.on('request', async (req, res) => {
|
|
52
|
+
try {
|
|
53
|
+
const valideUrl = new URL(`http://${req.headers.host}${req.url}`);
|
|
54
|
+
const hostname = valideUrl.hostname;
|
|
55
|
+
|
|
56
|
+
let organization = hosts.get(hostname);
|
|
57
|
+
if (!organization) {
|
|
58
|
+
let org = await this.crud.send({
|
|
59
|
+
method: 'object.read',
|
|
60
|
+
array: 'organizations',
|
|
61
|
+
$filter: {
|
|
62
|
+
query: [
|
|
63
|
+
{ key: "host", value: [hostname], operator: "$in" }
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
organization_id: process.env.organization_id
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
if (!org || !org.object || !org.object[0]) {
|
|
70
|
+
if (!hostNotFound)
|
|
71
|
+
hostNotFound = await getDefaultFile('/hostNotFound.html')
|
|
72
|
+
return sendResponse(hostNotFound.object[0].src, 404, { 'Content-Type': 'text/html', 'storage': organization.storage })
|
|
73
|
+
} else {
|
|
74
|
+
organization = org.object[0]
|
|
75
|
+
organizations[organization._id] = organization
|
|
76
|
+
hosts[hostname] = organization
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (valideUrl.pathname.startsWith('/webhooks/')) {
|
|
81
|
+
let name = req.url.split('/')[2]; // Assuming URL structure is /webhook/name/...
|
|
82
|
+
if (this.modules[name]) {
|
|
83
|
+
this.executeScriptWithTimeout(name, { req, res, crud: this.crud, organization, valideUrl })
|
|
84
|
+
} else {
|
|
85
|
+
// Handle unknown module or missing webhook method
|
|
86
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
87
|
+
res.end(JSON.stringify({ error: 'Not found' }));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
} else {
|
|
91
|
+
this.files.send(req, res, this.crud, organization, valideUrl)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
} catch (error) {
|
|
95
|
+
res.writeHead(400, { 'Content-Type': 'text/plain' });
|
|
96
|
+
res.end('Invalid host format');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
})
|
|
45
101
|
|
|
46
102
|
}
|
|
47
103
|
|
|
48
|
-
async executeScriptWithTimeout(
|
|
104
|
+
async executeScriptWithTimeout(name, data) {
|
|
49
105
|
try {
|
|
50
|
-
if (!
|
|
51
|
-
if (
|
|
52
|
-
|
|
106
|
+
if (!this.modules[name].content) {
|
|
107
|
+
if (this.modules[name].path)
|
|
108
|
+
this.modules[name].content = await require(this.modules[name].path)
|
|
53
109
|
else {
|
|
54
110
|
try {
|
|
55
|
-
const scriptPath = path.join(scriptsDirectory, `${
|
|
111
|
+
const scriptPath = path.join(scriptsDirectory, `${name}.js`);
|
|
56
112
|
await fs.access(scriptPath);
|
|
57
|
-
|
|
113
|
+
this.modules[name].content = await fs.readFile(scriptPath, 'utf8');
|
|
58
114
|
} catch {
|
|
59
|
-
|
|
115
|
+
this.modules[name].content = await fetchScriptFromDatabaseAndSave(name, this.modules[name], data);
|
|
60
116
|
}
|
|
61
117
|
}
|
|
62
118
|
}
|
|
63
119
|
|
|
64
|
-
if (
|
|
120
|
+
if (this.modules[name].content) {
|
|
121
|
+
data.apis = await this.getApiKey(data.organization_id, name)
|
|
122
|
+
data = await this.modules[name].content.send(data)
|
|
123
|
+
delete data.apis
|
|
124
|
+
if (data.socket)
|
|
125
|
+
this.wsManager.send(data)
|
|
126
|
+
} else
|
|
65
127
|
return
|
|
66
|
-
|
|
128
|
+
|
|
129
|
+
if (this.modules[name].unload === false || this.modules[name].unload === 'false')
|
|
130
|
+
return
|
|
131
|
+
else if (this.modules[name].unload === true || this.modules[name].unload === 'true')
|
|
67
132
|
console.log('config should unload after completeion ')
|
|
68
|
-
else if (
|
|
133
|
+
else if (this.modules[name].unload = parseInt(this.modules[name].unload, 10)) {
|
|
69
134
|
// Check if the script is already loaded
|
|
70
|
-
if (
|
|
71
|
-
clearTimeout(
|
|
72
|
-
} else if (!
|
|
135
|
+
if (this.modules[name].timeout) {
|
|
136
|
+
clearTimeout(this.modules[name].timeout);
|
|
137
|
+
} else if (!this.modules[name].path) {
|
|
73
138
|
// Execute the script
|
|
74
|
-
|
|
75
|
-
const script = new vm.Script(
|
|
139
|
+
this.modules[name].context = new vm.createContext({});
|
|
140
|
+
const script = new vm.Script(this.modules[name].context);
|
|
76
141
|
script.runInContext(context);
|
|
77
142
|
}
|
|
78
143
|
|
|
79
144
|
// Reset or set the timeout
|
|
80
145
|
const timeout = setTimeout(() => {
|
|
81
|
-
delete this.modules[
|
|
82
|
-
delete
|
|
83
|
-
delete
|
|
84
|
-
delete
|
|
85
|
-
console.log(`Module ${
|
|
86
|
-
clearModuleCache(
|
|
146
|
+
// delete this.modules[name]
|
|
147
|
+
delete this.modules[name].timeout
|
|
148
|
+
delete this.modules[name].context
|
|
149
|
+
delete this.modules[name].content
|
|
150
|
+
console.log(`Module ${name} removed due to inactivity.`);
|
|
151
|
+
clearModuleCache(name);
|
|
87
152
|
|
|
88
|
-
},
|
|
153
|
+
}, this.modules[name].unload);
|
|
89
154
|
|
|
90
|
-
|
|
155
|
+
this.modules[name].timeout = timeout
|
|
91
156
|
}
|
|
92
157
|
} catch (error) {
|
|
93
158
|
console.log(error)
|
|
94
159
|
}
|
|
95
160
|
}
|
|
96
161
|
|
|
162
|
+
async getApiKey(organization_id, name) {
|
|
163
|
+
organizations[organization_id] = this.getOrganization(organization_id, name)
|
|
164
|
+
organizations[organization_id] = await organizations[organization_id]
|
|
165
|
+
return organizations[organization_id][name]
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async getOrganization(organization_id) {
|
|
169
|
+
let organization = await this.crud.send({
|
|
170
|
+
method: 'object.read',
|
|
171
|
+
database: organization_id,
|
|
172
|
+
array: 'organizations',
|
|
173
|
+
object: [{ _id: organization_id }],
|
|
174
|
+
organization_id
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
if (organization
|
|
178
|
+
&& organization.object
|
|
179
|
+
&& organization.object[0]) {
|
|
180
|
+
if (organization.object[0].apis) {
|
|
181
|
+
return organization.object[0].apis
|
|
182
|
+
} else
|
|
183
|
+
return { error: 'No apis defined could not be found' }
|
|
184
|
+
} else {
|
|
185
|
+
return { serverOrganization: false, error: 'An organization could not be found' }
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
|
|
97
191
|
}
|
|
98
192
|
|
|
99
193
|
function getModuleDependencies(modulePath) {
|
|
@@ -106,7 +200,7 @@ function getModuleDependencies(modulePath) {
|
|
|
106
200
|
return moduleObj.children.map(child => child.id);
|
|
107
201
|
}
|
|
108
202
|
|
|
109
|
-
function isModuleUsedElsewhere(modulePath,
|
|
203
|
+
function isModuleUsedElsewhere(modulePath, name) {
|
|
110
204
|
return Object.keys(require.cache).some(path => {
|
|
111
205
|
const moduleObj = require.cache[path];
|
|
112
206
|
// return moduleObj.children.some(child => child.id === modulePath && path !== modulePath);
|
|
@@ -119,39 +213,39 @@ function isModuleUsedElsewhere(modulePath, moduleName) {
|
|
|
119
213
|
});
|
|
120
214
|
}
|
|
121
215
|
|
|
122
|
-
function clearModuleCache(
|
|
216
|
+
function clearModuleCache(name) {
|
|
123
217
|
try {
|
|
124
|
-
const modulePath = require.resolve(
|
|
218
|
+
const modulePath = require.resolve(name);
|
|
125
219
|
const dependencies = getModuleDependencies(modulePath);
|
|
126
220
|
|
|
127
221
|
// Check if the module is a dependency of other modules
|
|
128
222
|
// const moduleObj = require.cache[modulePath];
|
|
129
223
|
// if (moduleObj && moduleObj.parent) {
|
|
130
|
-
// console.log(`Module ${
|
|
224
|
+
// console.log(`Module ${name} is a dependency of other modules.`);
|
|
131
225
|
// return;
|
|
132
226
|
// }
|
|
133
227
|
|
|
134
228
|
// Check if the module is used by other modules
|
|
135
|
-
if (isModuleUsedElsewhere(modulePath,
|
|
136
|
-
console.log(`Module ${
|
|
229
|
+
if (isModuleUsedElsewhere(modulePath, name)) {
|
|
230
|
+
console.log(`Module ${name} is a dependency of other modules.`);
|
|
137
231
|
return;
|
|
138
232
|
}
|
|
139
233
|
|
|
140
234
|
// Remove the module from the cache
|
|
141
235
|
delete require.cache[modulePath];
|
|
142
|
-
console.log(`Module ${
|
|
236
|
+
console.log(`Module ${name} has been removed from cache.`);
|
|
143
237
|
// Recursively clear dependencies from cache
|
|
144
238
|
dependencies.forEach(depPath => {
|
|
145
239
|
clearModuleCache(depPath);
|
|
146
240
|
});
|
|
147
241
|
|
|
148
242
|
} catch (error) {
|
|
149
|
-
console.error(`Error clearing module cache for ${
|
|
243
|
+
console.error(`Error clearing module cache for ${name}: ${error.message}`);
|
|
150
244
|
}
|
|
151
245
|
}
|
|
152
246
|
|
|
153
247
|
// Function to fetch script from database and save to disk
|
|
154
|
-
async function fetchScriptFromDatabaseAndSave(
|
|
248
|
+
async function fetchScriptFromDatabaseAndSave(name, moduleConfig) {
|
|
155
249
|
let data = {
|
|
156
250
|
method: 'object.read',
|
|
157
251
|
array: moduleConfig.array,
|
|
@@ -165,7 +259,7 @@ async function fetchScriptFromDatabaseAndSave(moduleName, moduleConfig) {
|
|
|
165
259
|
organization_id
|
|
166
260
|
};
|
|
167
261
|
|
|
168
|
-
let file = await crud.send(data);
|
|
262
|
+
let file = await this.crud.send(data);
|
|
169
263
|
let src;
|
|
170
264
|
|
|
171
265
|
if (file && file.object && file.object[0]) {
|
|
@@ -175,7 +269,7 @@ async function fetchScriptFromDatabaseAndSave(moduleName, moduleConfig) {
|
|
|
175
269
|
}
|
|
176
270
|
|
|
177
271
|
// Save to disk for future use
|
|
178
|
-
const scriptPath = path.join(scriptsDirectory, `${
|
|
272
|
+
const scriptPath = path.join(scriptsDirectory, `${name}.js`);
|
|
179
273
|
await fs.writeFile(scriptPath, src);
|
|
180
274
|
|
|
181
275
|
return src;
|