Skip to content

Commit 6ac3c2d

Browse files
committed
feat: dynamically handles any third paty api and webhooks
1 parent bd2336a commit 6ac3c2d

File tree

1 file changed

+115
-23
lines changed

1 file changed

+115
-23
lines changed

src/server.js

Lines changed: 115 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ const vm = require('vm');
44
const Config = require("@cocreate/config");
55
const { URL } = require('url');
66

7-
// const organizations = {};
8-
97
class CoCreateLazyLoader {
108
constructor(server, crud, files) {
119
this.server = server
@@ -75,30 +73,37 @@ class CoCreateLazyLoader {
7573

7674
async executeScriptWithTimeout(name, data) {
7775
try {
78-
if (!this.modules[name].content) {
79-
if (this.modules[name].path)
80-
this.modules[name].content = await require(this.modules[name].path)
81-
else {
82-
try {
83-
const scriptPath = path.join(scriptsDirectory, `${name}.js`);
84-
await fs.access(scriptPath);
85-
this.modules[name].content = await fs.readFile(scriptPath, 'utf8');
86-
} catch {
87-
this.modules[name].content = await fetchScriptFromDatabaseAndSave(name, this.modules[name], data);
76+
if (this.modules[name].initialize) {
77+
if (data.req)
78+
data = await this.webhooks(data)
79+
else
80+
data = await this.api(this.modules[name], data)
81+
} else {
82+
if (!this.modules[name].content) {
83+
if (this.modules[name].path)
84+
this.modules[name].content = await require(this.modules[name].path)
85+
else {
86+
try {
87+
const scriptPath = path.join(scriptsDirectory, `${name}.js`);
88+
await fs.access(scriptPath);
89+
this.modules[name].content = await fs.readFile(scriptPath, 'utf8');
90+
} catch {
91+
this.modules[name].content = await fetchScriptFromDatabaseAndSave(name, this.modules[name], data);
92+
}
8893
}
8994
}
90-
}
9195

92-
if (this.modules[name].content) {
93-
data.apis = await this.getApiKey(data.organization_id, name)
94-
data.crud = this.crud
95-
data = await this.modules[name].content.send(data)
96-
delete data.apis
97-
delete data.crud
98-
if (data.socket)
99-
this.wsManager.send(data)
100-
} else
101-
return
96+
if (this.modules[name].content) {
97+
data.apis = await this.getApiKey(data.organization_id, name)
98+
data.crud = this.crud
99+
data = await this.modules[name].content.send(data)
100+
delete data.apis
101+
delete data.crud
102+
if (data.socket)
103+
this.wsManager.send(data)
104+
} else
105+
return
106+
}
102107

103108
if (this.modules[name].unload === false || this.modules[name].unload === 'false')
104109
return
@@ -144,6 +149,93 @@ class CoCreateLazyLoader {
144149
return organization.apis[name]
145150
}
146151

152+
async api(config, data) {
153+
try {
154+
const methodPath = data.method.split('.')
155+
const name = methodPath.shift()
156+
157+
const apis = await this.getApiKey(data.organization_id, name)
158+
if (apis.error)
159+
return data.error = apis.error
160+
161+
const environment = data.environment || 'production';
162+
const key = apis[environment];
163+
if (!key)
164+
return data.error = `Missing ${name} key in organization apis object`
165+
166+
const service = require(config.path);
167+
const instance = new service[config.initialize](key);
168+
169+
let method = instance
170+
for (let i = 0; i < methodPath.length; i++) {
171+
method = method[methodPath[i]]
172+
if (method === undefined) {
173+
return data.error = `Method ${methodPath[i]} not found using ${data.method}.`
174+
}
175+
}
176+
177+
if (typeof method !== 'function')
178+
return data.error = `Method ${data.method} is not a function.`
179+
180+
return data.postmark = await method.apply(instance, [data[name]]);
181+
} catch (error) {
182+
return data.error = error.message
183+
}
184+
}
185+
186+
async webhooks(config, data) {
187+
try {
188+
const apis = await this.getApiKey(data.organization_id, name)
189+
if (apis.error)
190+
return data.error = apis.error
191+
192+
let environment = data.environment || 'production';
193+
if (data.host.startsWith('dev.') || data.host.startsWith('test.'))
194+
environment = 'test'
195+
196+
const key = apis[environment];
197+
if (!key)
198+
return data.error = `Missing ${name} key in organization apis object`
199+
200+
let name = data.req.url.split('/');
201+
name = name[3] || name[2] || name[1]
202+
203+
// TODO: webhook secert could be a key pair
204+
const webhookSecret = data.apis[environment].webhooks[name];
205+
if (webhookSecret !== req.headers[name])
206+
return data.error = `Webhook secret failed for ${name}. Unauthorized access attempt.`;
207+
208+
let rawBody = '';
209+
await new Promise((resolve, reject) => {
210+
data.req.on('data', chunk => {
211+
rawBody += chunk.toString();
212+
});
213+
data.req.on('end', () => {
214+
resolve();
215+
});
216+
data.req.on('error', (err) => {
217+
reject(err);
218+
});
219+
});
220+
221+
// TODO: if decrypt and validation is builtin to service
222+
// const service = require(config.path);
223+
// const instance = new service[config.initialize](key);
224+
225+
// TODO: event may need to be handle by a built in service function
226+
const event = JSON.parse(rawBody)
227+
// TODO: using request.method and event.type get object and send socket.onMessage for proccessing
228+
229+
data.res.writeHead(200, { 'Content-Type': 'application/json' });
230+
data.res.end(JSON.stringify({ message: 'Webhook received and processed' }));
231+
return data
232+
} catch (error) {
233+
data.error = error.message
234+
data.res.writeHead(400, { 'Content-Type': 'text/plain' });
235+
data.res.end(`Webhook Error: ${err.message}`);
236+
return data
237+
}
238+
}
147239
}
148240

149241
function getModuleDependencies(modulePath) {

0 commit comments

Comments
 (0)