Skip to content

Commit 07b205c

Browse files
committed
feat: handle http and websocket requests
1 parent bac22b5 commit 07b205c

File tree

1 file changed

+137
-43
lines changed

1 file changed

+137
-43
lines changed

src/server.js

Lines changed: 137 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
1-
const EventEmitter = require('events');
2-
const eventEmitter = new EventEmitter();
31
const fs = require('fs').promises;
42
const path = require('path');
53
const vm = require('vm');
64
const Config = require("@cocreate/config");
5+
const { URL } = require('url');
6+
7+
const organizations = {};
8+
const hosts = {};
79

810
class CoCreateLazyLoader {
9-
constructor(crud) {
11+
constructor(server, crud, files) {
12+
this.server = server
1013
this.wsManager = crud.wsManager
1114
this.crud = crud
15+
this.files = files
1216
this.exclusion = { ...require.cache };
1317
this.modules = {};
1418
this.init()
@@ -30,70 +34,160 @@ class CoCreateLazyLoader {
3034
// Call this function at the start of your application
3135
createScriptsDirectory();
3236

33-
const config = await Config('lazyload', false, false)
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)
3441
if (!config)
3542
return
3643

37-
for (let key of Object.keys(config.lazyload)) {
38-
let moduleConfig = config.lazyload[key];
39-
eventEmitter.on(moduleConfig.event, async () => {
40-
this.executeScriptWithTimeout(key, moduleConfig)
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)
4148
});
4249
}
4350

44-
// eventEmitter.emit('openai');
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+
})
45101

46102
}
47103

48-
async executeScriptWithTimeout(moduleName, moduleConfig) {
104+
async executeScriptWithTimeout(name, data) {
49105
try {
50-
if (!moduleConfig.content) {
51-
if (moduleConfig.path)
52-
moduleConfig.content = await require(moduleConfig.path)
106+
if (!this.modules[name].content) {
107+
if (this.modules[name].path)
108+
this.modules[name].content = await require(this.modules[name].path)
53109
else {
54110
try {
55-
const scriptPath = path.join(scriptsDirectory, `${moduleName}.js`);
111+
const scriptPath = path.join(scriptsDirectory, `${name}.js`);
56112
await fs.access(scriptPath);
57-
moduleConfig.content = await fs.readFile(scriptPath, 'utf8');
113+
this.modules[name].content = await fs.readFile(scriptPath, 'utf8');
58114
} catch {
59-
moduleConfig.content = await fetchScriptFromDatabaseAndSave(moduleName, moduleConfig);
115+
this.modules[name].content = await fetchScriptFromDatabaseAndSave(name, this.modules[name], data);
60116
}
61117
}
62118
}
63119

64-
if (moduleConfig.unload === false || moduleConfig.unload === 'false')
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
65127
return
66-
else if (moduleConfig.unload === true || moduleConfig.unload === 'true')
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')
67132
console.log('config should unload after completeion ')
68-
else if (moduleConfig.unload = parseInt(moduleConfig.unload, 10)) {
133+
else if (this.modules[name].unload = parseInt(this.modules[name].unload, 10)) {
69134
// Check if the script is already loaded
70-
if (moduleConfig.timeout) {
71-
clearTimeout(moduleConfig.timeout);
72-
} else if (!moduleConfig.path) {
135+
if (this.modules[name].timeout) {
136+
clearTimeout(this.modules[name].timeout);
137+
} else if (!this.modules[name].path) {
73138
// Execute the script
74-
moduleConfig.context = new vm.createContext({});
75-
const script = new vm.Script(moduleConfig.context);
139+
this.modules[name].context = new vm.createContext({});
140+
const script = new vm.Script(this.modules[name].context);
76141
script.runInContext(context);
77142
}
78143

79144
// Reset or set the timeout
80145
const timeout = setTimeout(() => {
81-
delete this.modules[moduleName]
82-
delete moduleConfig.timeout
83-
delete moduleConfig.context
84-
delete moduleConfig.content
85-
console.log(`Module ${moduleName} removed due to inactivity.`);
86-
clearModuleCache(moduleName);
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);
87152

88-
}, moduleConfig.unload);
153+
}, this.modules[name].unload);
89154

90-
moduleConfig.timeout = timeout
155+
this.modules[name].timeout = timeout
91156
}
92157
} catch (error) {
93158
console.log(error)
94159
}
95160
}
96161

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+
97191
}
98192

99193
function getModuleDependencies(modulePath) {
@@ -106,7 +200,7 @@ function getModuleDependencies(modulePath) {
106200
return moduleObj.children.map(child => child.id);
107201
}
108202

109-
function isModuleUsedElsewhere(modulePath, moduleName) {
203+
function isModuleUsedElsewhere(modulePath, name) {
110204
return Object.keys(require.cache).some(path => {
111205
const moduleObj = require.cache[path];
112206
// return moduleObj.children.some(child => child.id === modulePath && path !== modulePath);
@@ -119,39 +213,39 @@ function isModuleUsedElsewhere(modulePath, moduleName) {
119213
});
120214
}
121215

122-
function clearModuleCache(moduleName) {
216+
function clearModuleCache(name) {
123217
try {
124-
const modulePath = require.resolve(moduleName);
218+
const modulePath = require.resolve(name);
125219
const dependencies = getModuleDependencies(modulePath);
126220

127221
// Check if the module is a dependency of other modules
128222
// const moduleObj = require.cache[modulePath];
129223
// if (moduleObj && moduleObj.parent) {
130-
// console.log(`Module ${moduleName} is a dependency of other modules.`);
224+
// console.log(`Module ${name} is a dependency of other modules.`);
131225
// return;
132226
// }
133227

134228
// Check if the module is used by other modules
135-
if (isModuleUsedElsewhere(modulePath, moduleName)) {
136-
console.log(`Module ${moduleName} is a dependency of other modules.`);
229+
if (isModuleUsedElsewhere(modulePath, name)) {
230+
console.log(`Module ${name} is a dependency of other modules.`);
137231
return;
138232
}
139233

140234
// Remove the module from the cache
141235
delete require.cache[modulePath];
142-
console.log(`Module ${moduleName} has been removed from cache.`);
236+
console.log(`Module ${name} has been removed from cache.`);
143237
// Recursively clear dependencies from cache
144238
dependencies.forEach(depPath => {
145239
clearModuleCache(depPath);
146240
});
147241

148242
} catch (error) {
149-
console.error(`Error clearing module cache for ${moduleName}: ${error.message}`);
243+
console.error(`Error clearing module cache for ${name}: ${error.message}`);
150244
}
151245
}
152246

153247
// Function to fetch script from database and save to disk
154-
async function fetchScriptFromDatabaseAndSave(moduleName, moduleConfig) {
248+
async function fetchScriptFromDatabaseAndSave(name, moduleConfig) {
155249
let data = {
156250
method: 'object.read',
157251
array: moduleConfig.array,
@@ -165,7 +259,7 @@ async function fetchScriptFromDatabaseAndSave(moduleName, moduleConfig) {
165259
organization_id
166260
};
167261

168-
let file = await crud.send(data);
262+
let file = await this.crud.send(data);
169263
let src;
170264

171265
if (file && file.object && file.object[0]) {
@@ -175,7 +269,7 @@ async function fetchScriptFromDatabaseAndSave(moduleName, moduleConfig) {
175269
}
176270

177271
// Save to disk for future use
178-
const scriptPath = path.join(scriptsDirectory, `${moduleName}.js`);
272+
const scriptPath = path.join(scriptsDirectory, `${name}.js`);
179273
await fs.writeFile(scriptPath, src);
180274

181275
return src;

0 commit comments

Comments
 (0)