@cocreate/api 1.29.0 → 1.31.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/package.json +8 -8
- package/src/server.js +51 -22
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cocreate/api",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.31.0",
|
|
4
4
|
"description": "A secure, multi-tenant API execution gateway and proxy layer that dynamically resolves endpoint actions, processes AST micro-operators, handles webhooks, and manages third-party integrations with strict tenant environment isolation.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"api-orchestration",
|
|
@@ -51,15 +51,15 @@
|
|
|
51
51
|
}
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@cocreate/actions": "^1.
|
|
55
|
-
"@cocreate/crud-client": "^1.36.
|
|
56
|
-
"@cocreate/element-prototype": "^1.
|
|
57
|
-
"@cocreate/render": "^1.
|
|
58
|
-
"@cocreate/socket-client": "^1.42.
|
|
59
|
-
"@cocreate/utils": "^1.
|
|
54
|
+
"@cocreate/actions": "^1.24.0",
|
|
55
|
+
"@cocreate/crud-client": "^1.36.1",
|
|
56
|
+
"@cocreate/element-prototype": "^1.35.0",
|
|
57
|
+
"@cocreate/render": "^1.50.0",
|
|
58
|
+
"@cocreate/socket-client": "^1.42.1",
|
|
59
|
+
"@cocreate/utils": "^1.52.0"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"@cocreate/webpack": "^1.
|
|
62
|
+
"@cocreate/webpack": "^1.8.0"
|
|
63
63
|
},
|
|
64
64
|
"allowScripts": {
|
|
65
65
|
"@cocreate/actions@1.21.4": true,
|
package/src/server.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/********************************************************************************
|
|
2
|
-
* Copyright (C) 2023 CoCreate and Contributors.
|
|
2
|
+
* Copyright (C) 2023-2026 CoCreate and Contributors.
|
|
3
3
|
*
|
|
4
4
|
* This program is free software: you can redistribute it and/or modify
|
|
5
5
|
* it under the terms of the GNU Affero General Public License as published
|
|
@@ -13,16 +13,10 @@
|
|
|
13
13
|
*
|
|
14
14
|
* You should have received a copy of the GNU Affero General Public License
|
|
15
15
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
-
*
|
|
17
16
|
********************************************************************************/
|
|
18
17
|
|
|
19
|
-
// Commercial Licensing Information:
|
|
20
|
-
// For commercial use of this software without the copyleft provisions of the AGPLv3,
|
|
21
|
-
// you must obtain a commercial license from CoCreate LLC.
|
|
22
|
-
// For details, visit <https://cocreate.app/licenses/> or contact us at sales@cocreate.app.
|
|
23
|
-
|
|
24
18
|
import { URL } from 'node:url';
|
|
25
|
-
import { getValueFromObject, objectToSearchParams, astAsync,
|
|
19
|
+
import { getValueFromObject, objectToSearchParams, astAsync, security } from '@cocreate/utils';
|
|
26
20
|
|
|
27
21
|
// Core module references bound dynamically upon initialization
|
|
28
22
|
let server = null;
|
|
@@ -33,13 +27,17 @@ const API = {
|
|
|
33
27
|
init,
|
|
34
28
|
executeApi,
|
|
35
29
|
request,
|
|
36
|
-
|
|
37
|
-
verifySignature,
|
|
30
|
+
security,
|
|
38
31
|
makeHttpRequest,
|
|
39
32
|
getApiConfig,
|
|
40
33
|
normalizeConfig
|
|
41
34
|
};
|
|
42
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Binds server core services and registers WebSocket event handlers.
|
|
38
|
+
* @param {Object} Server - Server core instance containing wsManager and crud
|
|
39
|
+
* @returns {Object} Exported API service interface
|
|
40
|
+
*/
|
|
43
41
|
export async function init(Server) {
|
|
44
42
|
server = Server;
|
|
45
43
|
wsManager = server.wsManager;
|
|
@@ -56,6 +54,12 @@ export async function init(Server) {
|
|
|
56
54
|
return API;
|
|
57
55
|
}
|
|
58
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Normalizes raw API configuration structures into a uniform key/url/headers shape.
|
|
59
|
+
* @param {string|Object} apiConfig - Raw configuration object or API key string
|
|
60
|
+
* @param {string} name - Provider identifier
|
|
61
|
+
* @returns {Object} Standardized configuration container
|
|
62
|
+
*/
|
|
59
63
|
export function normalizeConfig(apiConfig, name) {
|
|
60
64
|
let key = null;
|
|
61
65
|
let url = null;
|
|
@@ -85,11 +89,12 @@ export function normalizeConfig(apiConfig, name) {
|
|
|
85
89
|
}
|
|
86
90
|
|
|
87
91
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
92
|
+
* Constructs an isolated execution sandbox enforcing multi-tenant isolation via Proxy firewalls.
|
|
93
|
+
* @param {Object} context - Execution context containing organization, host, data, and request info
|
|
94
|
+
* @returns {Object} Secure sandbox context for AST execution
|
|
90
95
|
*/
|
|
91
96
|
function createSandbox(context) {
|
|
92
|
-
// SECURITY FIREWALL:
|
|
97
|
+
// SECURITY FIREWALL: Proxy database calls to enforce multi-tenant isolation.
|
|
93
98
|
const secureCrud = context.crud ? new Proxy(context.crud, {
|
|
94
99
|
get(target, prop) {
|
|
95
100
|
const original = target[prop];
|
|
@@ -111,6 +116,7 @@ function createSandbox(context) {
|
|
|
111
116
|
}
|
|
112
117
|
}) : undefined;
|
|
113
118
|
|
|
119
|
+
// SECURITY FIREWALL: Proxy WebSocket manager calls for multi-tenant isolation.
|
|
114
120
|
const secureWsManager = context.wsManager ? new Proxy(context.wsManager, {
|
|
115
121
|
get(target, prop) {
|
|
116
122
|
const original = target[prop];
|
|
@@ -148,6 +154,9 @@ function createSandbox(context) {
|
|
|
148
154
|
socket: secureWsManager,
|
|
149
155
|
wsManager: secureWsManager,
|
|
150
156
|
|
|
157
|
+
// Security Utility Instance
|
|
158
|
+
security,
|
|
159
|
+
|
|
151
160
|
// Host Context Identifiers
|
|
152
161
|
key: context.key,
|
|
153
162
|
config: context.config,
|
|
@@ -159,11 +168,6 @@ function createSandbox(context) {
|
|
|
159
168
|
send: async (apiData) => {
|
|
160
169
|
return await executeApi(apiData, context);
|
|
161
170
|
}
|
|
162
|
-
},
|
|
163
|
-
jwt: (args) => {
|
|
164
|
-
const privateKey = args?.privateKey || args?.secret;
|
|
165
|
-
if (!privateKey) throw new Error("Missing cryptographic signing key inside $jwt context parameters");
|
|
166
|
-
return generateJWT(args, privateKey, args.alg || 'HS256');
|
|
167
171
|
}
|
|
168
172
|
};
|
|
169
173
|
|
|
@@ -175,6 +179,12 @@ function createSandbox(context) {
|
|
|
175
179
|
return sandbox;
|
|
176
180
|
}
|
|
177
181
|
|
|
182
|
+
/**
|
|
183
|
+
* Resolves endpoint definitions, executes AST macros, or dispatches HTTP requests.
|
|
184
|
+
* @param {Object} data - API execution payload containing directives
|
|
185
|
+
* @param {Object} [parentContext=null] - Optional outer sandbox execution context
|
|
186
|
+
* @returns {Promise<any>} Execution response payload
|
|
187
|
+
*/
|
|
178
188
|
export async function executeApi(data, parentContext = null) {
|
|
179
189
|
try {
|
|
180
190
|
const orgId = parentContext?.organization_id || data.socket?.organization_id || data.organization_id;
|
|
@@ -267,7 +277,6 @@ export async function executeApi(data, parentContext = null) {
|
|
|
267
277
|
return;
|
|
268
278
|
}
|
|
269
279
|
|
|
270
|
-
// --- Standard Outbound Construction Path ---
|
|
271
280
|
if (!apiConfig.url) {
|
|
272
281
|
throw new Error(`Configuration missing 'url' base for provider '${providerName}'.`);
|
|
273
282
|
}
|
|
@@ -322,6 +331,11 @@ export async function executeApi(data, parentContext = null) {
|
|
|
322
331
|
}
|
|
323
332
|
}
|
|
324
333
|
|
|
334
|
+
/**
|
|
335
|
+
* Processes incoming HTTP webhook requests, validates security signatures, and executes pipelines.
|
|
336
|
+
* @param {Object} req - HTTP request object
|
|
337
|
+
* @param {Object} res - HTTP response object
|
|
338
|
+
*/
|
|
325
339
|
export async function request(req, res) {
|
|
326
340
|
try {
|
|
327
341
|
const urlObject = new URL(`http://${req.headers.host}${req.url}`);
|
|
@@ -400,7 +414,10 @@ export async function request(req, res) {
|
|
|
400
414
|
const signature = resolvedParams[1];
|
|
401
415
|
const secret = resolvedParams[2];
|
|
402
416
|
|
|
403
|
-
|
|
417
|
+
const computedSignature = security.createWebhookSignature(payload, secret, { format: 'github' });
|
|
418
|
+
const expectedSignature = String(signature);
|
|
419
|
+
|
|
420
|
+
if (!security.timingSafeEqual(computedSignature, expectedSignature)) {
|
|
404
421
|
res.writeHead(401, { "Content-Type": "application/json" });
|
|
405
422
|
return res.end(JSON.stringify({ error: "Unauthorized access: signature verification failed." }));
|
|
406
423
|
}
|
|
@@ -448,9 +465,15 @@ export async function request(req, res) {
|
|
|
448
465
|
}
|
|
449
466
|
}
|
|
450
467
|
|
|
451
|
-
//
|
|
452
|
-
export {
|
|
468
|
+
// Re-export security library instance
|
|
469
|
+
export { security };
|
|
453
470
|
|
|
471
|
+
/**
|
|
472
|
+
* Low-level HTTP dispatch utility wrapping global fetch with AbortController timeout handling.
|
|
473
|
+
* @param {string} url - Target URL endpoint
|
|
474
|
+
* @param {Object} options - Fetch options (method, headers, body, signal, timeout)
|
|
475
|
+
* @returns {Promise<Response>} Native fetch response instance
|
|
476
|
+
*/
|
|
454
477
|
export async function makeHttpRequest(url, options) {
|
|
455
478
|
let controller, timeoutId;
|
|
456
479
|
if (global.AbortController) {
|
|
@@ -485,6 +508,12 @@ export async function makeHttpRequest(url, options) {
|
|
|
485
508
|
}
|
|
486
509
|
}
|
|
487
510
|
|
|
511
|
+
/**
|
|
512
|
+
* Queries database storage for provider connection credentials and API configuration.
|
|
513
|
+
* @param {Object} data - Context query parameters (organization_id, host)
|
|
514
|
+
* @param {string} name - API provider name
|
|
515
|
+
* @returns {Promise<Object>} API provider configuration object
|
|
516
|
+
*/
|
|
488
517
|
export async function getApiConfig(data, name) {
|
|
489
518
|
let orgId = data.organization_id;
|
|
490
519
|
let host = data.host;
|