streamby-core 0.29.11 → 0.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/README.md +283 -283
- package/dist/adapters/database/connectionManager.js +78 -80
- package/dist/adapters/database/sql.js +1 -1
- package/dist/middleware/createRouter.js +0 -2
- package/dist/middleware/routes/ai.js +1 -1
- package/dist/middleware/routes/connection.js +35 -50
- package/dist/middleware/routes/dbConnection.js +17 -15
- package/dist/middleware/routes/distribution.js +5 -4
- package/dist/middleware/routes/export.js +10 -17
- package/dist/middleware/routes/project.js +1 -1
- package/dist/middleware/routes/renderFarm.js +5 -4
- package/dist/middleware/routes/storage.js +7 -5
- package/dist/middleware/routes/storageConnection.js +15 -12
- package/dist/models/Model.js +9 -9
- package/dist/services/backfillBuiltinConnections.js +0 -2
- package/dist/services/connection.d.ts +18 -0
- package/dist/services/connection.js +78 -0
- package/dist/services/executor.js +9 -12
- package/dist/services/projectIntegration.js +4 -4
- package/dist/types/index.d.ts +10 -16
- package/dist/utils/crypto.d.ts +3 -0
- package/dist/utils/crypto.js +32 -0
- package/dist/utils/sanitize.d.ts +10 -0
- package/dist/utils/sanitize.js +16 -1
- package/package.json +63 -63
|
@@ -43,105 +43,103 @@ const getConnection = (id) => {
|
|
|
43
43
|
exports.getConnection = getConnection;
|
|
44
44
|
const ensureTablesExist = async (pool) => {
|
|
45
45
|
try {
|
|
46
|
-
await pool.query(`
|
|
47
|
-
CREATE SCHEMA IF NOT EXISTS streamby;
|
|
48
|
-
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
|
46
|
+
await pool.query(`
|
|
47
|
+
CREATE SCHEMA IF NOT EXISTS streamby;
|
|
48
|
+
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
|
49
49
|
`);
|
|
50
50
|
// Migrate id column from UUID to TEXT if it was created with the old schema
|
|
51
|
-
await pool.query(`
|
|
52
|
-
DO $$ BEGIN
|
|
53
|
-
ALTER TABLE streamby.projects ALTER COLUMN id TYPE TEXT USING id::text;
|
|
54
|
-
ALTER TABLE streamby.projects ALTER COLUMN id SET DEFAULT uuid_generate_v4()::text;
|
|
55
|
-
EXCEPTION WHEN OTHERS THEN NULL; END $$;
|
|
51
|
+
await pool.query(`
|
|
52
|
+
DO $$ BEGIN
|
|
53
|
+
ALTER TABLE streamby.projects ALTER COLUMN id TYPE TEXT USING id::text;
|
|
54
|
+
ALTER TABLE streamby.projects ALTER COLUMN id SET DEFAULT uuid_generate_v4()::text;
|
|
55
|
+
EXCEPTION WHEN OTHERS THEN NULL; END $$;
|
|
56
56
|
`);
|
|
57
|
-
await pool.query(`
|
|
58
|
-
CREATE TABLE IF NOT EXISTS streamby.projects (
|
|
59
|
-
id TEXT PRIMARY KEY DEFAULT uuid_generate_v4()::text,
|
|
60
|
-
name VARCHAR(255) NOT NULL,
|
|
61
|
-
description TEXT,
|
|
62
|
-
image TEXT,
|
|
63
|
-
"allowedOrigin" JSONB DEFAULT '[]',
|
|
64
|
-
exports JSONB DEFAULT '[]',
|
|
65
|
-
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
|
|
71
|
-
);
|
|
57
|
+
await pool.query(`
|
|
58
|
+
CREATE TABLE IF NOT EXISTS streamby.projects (
|
|
59
|
+
id TEXT PRIMARY KEY DEFAULT uuid_generate_v4()::text,
|
|
60
|
+
name VARCHAR(255) NOT NULL,
|
|
61
|
+
description TEXT,
|
|
62
|
+
image TEXT,
|
|
63
|
+
"allowedOrigin" JSONB DEFAULT '[]',
|
|
64
|
+
exports JSONB DEFAULT '[]',
|
|
65
|
+
connections JSONB DEFAULT '[]',
|
|
66
|
+
"dbConnections" JSONB DEFAULT '[]',
|
|
67
|
+
"dbType" VARCHAR(50) NOT NULL DEFAULT 'sql',
|
|
68
|
+
"createdAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
69
|
+
"updatedAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
70
|
+
);
|
|
72
71
|
`);
|
|
73
72
|
console.log('✅ "projects" table ensured to exist.');
|
|
74
73
|
// Add JSONB columns to existing tables that predate this schema version
|
|
75
|
-
await pool.query(`
|
|
76
|
-
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "allowedOrigin" JSONB DEFAULT '[]';
|
|
77
|
-
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS exports JSONB DEFAULT '[]';
|
|
78
|
-
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS
|
|
79
|
-
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "
|
|
80
|
-
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "
|
|
81
|
-
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS
|
|
82
|
-
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS
|
|
83
|
-
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS
|
|
84
|
-
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "builtinBackfilledAt" TIMESTAMP WITH TIME ZONE;
|
|
74
|
+
await pool.query(`
|
|
75
|
+
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "allowedOrigin" JSONB DEFAULT '[]';
|
|
76
|
+
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS exports JSONB DEFAULT '[]';
|
|
77
|
+
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS connections JSONB DEFAULT '[]';
|
|
78
|
+
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "dbConnections" JSONB DEFAULT '[]';
|
|
79
|
+
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "storageConnections" JSONB DEFAULT '[]';
|
|
80
|
+
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS workflow JSONB;
|
|
81
|
+
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS pipelines JSONB DEFAULT '[]';
|
|
82
|
+
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "builtinBackfilledAt" TIMESTAMP WITH TIME ZONE;
|
|
85
83
|
`);
|
|
86
84
|
// Ensure dbType exists with a non-null default
|
|
87
|
-
await pool.query(`
|
|
88
|
-
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "dbType" VARCHAR(50) NOT NULL DEFAULT 'sql';
|
|
85
|
+
await pool.query(`
|
|
86
|
+
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "dbType" VARCHAR(50) NOT NULL DEFAULT 'sql';
|
|
89
87
|
`);
|
|
90
|
-
await pool.query(`
|
|
91
|
-
CREATE TABLE IF NOT EXISTS streamby.project_members (
|
|
92
|
-
id TEXT PRIMARY KEY DEFAULT uuid_generate_v4()::text,
|
|
93
|
-
"projectId" TEXT NOT NULL REFERENCES streamby.projects(id) ON DELETE CASCADE,
|
|
94
|
-
"userId" VARCHAR(255) NOT NULL,
|
|
95
|
-
role VARCHAR(255) NOT NULL DEFAULT 'member',
|
|
96
|
-
status VARCHAR(50) NOT NULL DEFAULT 'active',
|
|
97
|
-
archived BOOLEAN NOT NULL DEFAULT FALSE,
|
|
98
|
-
"archivedBy" VARCHAR(255),
|
|
99
|
-
"archivedAt" TIMESTAMP WITH TIME ZONE,
|
|
100
|
-
"createdAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
101
|
-
"updatedAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
102
|
-
UNIQUE ("projectId", "userId")
|
|
103
|
-
);
|
|
88
|
+
await pool.query(`
|
|
89
|
+
CREATE TABLE IF NOT EXISTS streamby.project_members (
|
|
90
|
+
id TEXT PRIMARY KEY DEFAULT uuid_generate_v4()::text,
|
|
91
|
+
"projectId" TEXT NOT NULL REFERENCES streamby.projects(id) ON DELETE CASCADE,
|
|
92
|
+
"userId" VARCHAR(255) NOT NULL,
|
|
93
|
+
role VARCHAR(255) NOT NULL DEFAULT 'member',
|
|
94
|
+
status VARCHAR(50) NOT NULL DEFAULT 'active',
|
|
95
|
+
archived BOOLEAN NOT NULL DEFAULT FALSE,
|
|
96
|
+
"archivedBy" VARCHAR(255),
|
|
97
|
+
"archivedAt" TIMESTAMP WITH TIME ZONE,
|
|
98
|
+
"createdAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
99
|
+
"updatedAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
100
|
+
UNIQUE ("projectId", "userId")
|
|
101
|
+
);
|
|
104
102
|
`);
|
|
105
|
-
await pool.query(`
|
|
106
|
-
ALTER TABLE streamby.project_members ADD COLUMN IF NOT EXISTS status VARCHAR(50) NOT NULL DEFAULT 'active';
|
|
103
|
+
await pool.query(`
|
|
104
|
+
ALTER TABLE streamby.project_members ADD COLUMN IF NOT EXISTS status VARCHAR(50) NOT NULL DEFAULT 'active';
|
|
107
105
|
`);
|
|
108
106
|
console.log('✅ "project_members" table ensured to exist.');
|
|
109
107
|
// Migrate projectId FK column from UUID to TEXT if table predates this schema version
|
|
110
|
-
await pool.query(`
|
|
111
|
-
DO $$ BEGIN
|
|
112
|
-
ALTER TABLE streamby.project_members DROP CONSTRAINT IF EXISTS project_members_projectId_fkey;
|
|
113
|
-
ALTER TABLE streamby.project_members ALTER COLUMN "projectId" TYPE TEXT;
|
|
114
|
-
ALTER TABLE streamby.project_members ADD CONSTRAINT project_members_projectid_fkey
|
|
115
|
-
FOREIGN KEY ("projectId") REFERENCES streamby.projects(id) ON DELETE CASCADE;
|
|
116
|
-
EXCEPTION WHEN OTHERS THEN NULL; END $$;
|
|
108
|
+
await pool.query(`
|
|
109
|
+
DO $$ BEGIN
|
|
110
|
+
ALTER TABLE streamby.project_members DROP CONSTRAINT IF EXISTS project_members_projectId_fkey;
|
|
111
|
+
ALTER TABLE streamby.project_members ALTER COLUMN "projectId" TYPE TEXT;
|
|
112
|
+
ALTER TABLE streamby.project_members ADD CONSTRAINT project_members_projectid_fkey
|
|
113
|
+
FOREIGN KEY ("projectId") REFERENCES streamby.projects(id) ON DELETE CASCADE;
|
|
114
|
+
EXCEPTION WHEN OTHERS THEN NULL; END $$;
|
|
117
115
|
`);
|
|
118
|
-
await pool.query(`
|
|
119
|
-
CREATE TABLE IF NOT EXISTS streamby.exports (
|
|
120
|
-
id TEXT PRIMARY KEY DEFAULT uuid_generate_v4()::text,
|
|
121
|
-
"projectId" TEXT NOT NULL REFERENCES streamby.projects(id) ON DELETE CASCADE,
|
|
122
|
-
status VARCHAR(50) NOT NULL DEFAULT 'pending',
|
|
123
|
-
"filePath" TEXT,
|
|
124
|
-
"createdAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
125
|
-
"updatedAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
126
|
-
);
|
|
116
|
+
await pool.query(`
|
|
117
|
+
CREATE TABLE IF NOT EXISTS streamby.exports (
|
|
118
|
+
id TEXT PRIMARY KEY DEFAULT uuid_generate_v4()::text,
|
|
119
|
+
"projectId" TEXT NOT NULL REFERENCES streamby.projects(id) ON DELETE CASCADE,
|
|
120
|
+
status VARCHAR(50) NOT NULL DEFAULT 'pending',
|
|
121
|
+
"filePath" TEXT,
|
|
122
|
+
"createdAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
123
|
+
"updatedAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
124
|
+
);
|
|
127
125
|
`);
|
|
128
126
|
console.log('✅ "exports" table ensured to exist.');
|
|
129
127
|
// user_integrations is registered against the main db only (see createRouter.ts), but
|
|
130
128
|
// this function runs per-SQL-connection like projects/exports above — harmless on
|
|
131
129
|
// non-main connections since the model never queries them there.
|
|
132
|
-
await pool.query(`
|
|
133
|
-
CREATE TABLE IF NOT EXISTS streamby.user_integrations (
|
|
134
|
-
id TEXT PRIMARY KEY DEFAULT uuid_generate_v4()::text,
|
|
135
|
-
"userId" VARCHAR(255) NOT NULL,
|
|
136
|
-
kind VARCHAR(20) NOT NULL,
|
|
137
|
-
provider VARCHAR(20) NOT NULL,
|
|
138
|
-
name VARCHAR(255) NOT NULL,
|
|
139
|
-
description TEXT,
|
|
140
|
-
"encryptedCredential" TEXT NOT NULL,
|
|
141
|
-
"createdAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
142
|
-
"updatedAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
143
|
-
);
|
|
144
|
-
CREATE INDEX IF NOT EXISTS user_integrations_userid_idx ON streamby.user_integrations ("userId");
|
|
130
|
+
await pool.query(`
|
|
131
|
+
CREATE TABLE IF NOT EXISTS streamby.user_integrations (
|
|
132
|
+
id TEXT PRIMARY KEY DEFAULT uuid_generate_v4()::text,
|
|
133
|
+
"userId" VARCHAR(255) NOT NULL,
|
|
134
|
+
kind VARCHAR(20) NOT NULL,
|
|
135
|
+
provider VARCHAR(20) NOT NULL,
|
|
136
|
+
name VARCHAR(255) NOT NULL,
|
|
137
|
+
description TEXT,
|
|
138
|
+
"encryptedCredential" TEXT NOT NULL,
|
|
139
|
+
"createdAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
140
|
+
"updatedAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
141
|
+
);
|
|
142
|
+
CREATE INDEX IF NOT EXISTS user_integrations_userid_idx ON streamby.user_integrations ("userId");
|
|
145
143
|
`);
|
|
146
144
|
console.log('✅ "user_integrations" table ensured to exist.');
|
|
147
145
|
}
|
|
@@ -10,7 +10,7 @@ function serializeForPg(v) {
|
|
|
10
10
|
}
|
|
11
11
|
const createSQLExportTable = async (connection, projectId, exportName, nodeSchema) => {
|
|
12
12
|
const exportId = (0, crypto_1.randomUUID)();
|
|
13
|
-
await connection.query(`INSERT INTO "exports" (id, "projectId", node_schema, name, method, created_at, updated_at)
|
|
13
|
+
await connection.query(`INSERT INTO "exports" (id, "projectId", node_schema, name, method, created_at, updated_at)
|
|
14
14
|
VALUES ($1, $2, $3, $4, 'GET', NOW(), NOW())`, [exportId, projectId, nodeSchema ? JSON.stringify(nodeSchema) : null, exportName]);
|
|
15
15
|
return { exportId };
|
|
16
16
|
};
|
|
@@ -19,7 +19,6 @@ const member_1 = require("./routes/member");
|
|
|
19
19
|
const storage_2 = require("./routes/storage");
|
|
20
20
|
const project_1 = require("./routes/project");
|
|
21
21
|
const database_1 = require("./routes/database");
|
|
22
|
-
const credential_1 = require("./routes/credential");
|
|
23
22
|
const connection_1 = require("./routes/connection");
|
|
24
23
|
const dbConnection_1 = require("./routes/dbConnection");
|
|
25
24
|
const storageConnection_1 = require("./routes/storageConnection");
|
|
@@ -98,7 +97,6 @@ function createStreamByRouter(config) {
|
|
|
98
97
|
router.use((0, storage_2.storageRouter)(config));
|
|
99
98
|
router.use((0, project_1.projectRouter)(config));
|
|
100
99
|
router.use((0, database_1.databaseRouter)(config));
|
|
101
|
-
router.use((0, credential_1.credentialRouter)(config));
|
|
102
100
|
router.use((0, connection_1.connectionRouter)(config));
|
|
103
101
|
router.use((0, dbConnection_1.dbConnectionRouter)(config));
|
|
104
102
|
router.use((0, storageConnection_1.storageConnectionRouter)(config));
|
|
@@ -7,7 +7,7 @@ const auth_1 = require("../../utils/auth");
|
|
|
7
7
|
const jobQueue_1 = require("../../services/jobQueue");
|
|
8
8
|
const aiProcessor_1 = require("../../services/aiProcessor");
|
|
9
9
|
const AVAILABLE_NODE_TYPES = [
|
|
10
|
-
'dataSourceNode', 'jsonInputNode', '
|
|
10
|
+
'dataSourceNode', 'jsonInputNode', 'connectionNode',
|
|
11
11
|
'processNode', 'filterNode',
|
|
12
12
|
'ingestNode', 'transcodeNode', 'captionNode', 'thumbnailNode',
|
|
13
13
|
'renderJobNode', 'formatConvertNode', 'lodNode', 'assetDependencyNode',
|
|
@@ -37,13 +37,14 @@ exports.connectionRouter = connectionRouter;
|
|
|
37
37
|
const express_1 = require("express");
|
|
38
38
|
const manager_1 = require("../../models/manager");
|
|
39
39
|
const auth_1 = require("../../utils/auth");
|
|
40
|
-
const
|
|
40
|
+
const connection_1 = require("../../services/connection");
|
|
41
|
+
const sanitize_1 = require("../../utils/sanitize");
|
|
41
42
|
const VALID_METHODS = ['GET', 'POST', 'PATCH', 'PUT', 'DELETE'];
|
|
42
43
|
function connectionRouter(config) {
|
|
43
44
|
const router = (0, express_1.Router)();
|
|
44
45
|
const Project = (0, manager_1.getModel)('projects');
|
|
45
|
-
// List
|
|
46
|
-
router.get('/projects/:id/connections
|
|
46
|
+
// List connections for a project
|
|
47
|
+
router.get('/projects/:id/connections', async (req, res) => {
|
|
47
48
|
try {
|
|
48
49
|
const auth = req.auth;
|
|
49
50
|
const projectId = req.params.id;
|
|
@@ -54,21 +55,21 @@ function connectionRouter(config) {
|
|
|
54
55
|
if (!(0, auth_1.isProjectMember)(project, auth.userId)) {
|
|
55
56
|
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
56
57
|
}
|
|
57
|
-
return res.status(200).json({ data: project.
|
|
58
|
+
return res.status(200).json({ data: (0, sanitize_1.sanitizeConnections)(project.connections) });
|
|
58
59
|
}
|
|
59
60
|
catch (err) {
|
|
60
|
-
res.status(500).json({ message: 'Failed to fetch
|
|
61
|
+
res.status(500).json({ message: 'Failed to fetch connections', details: err.message });
|
|
61
62
|
}
|
|
62
63
|
});
|
|
63
|
-
// Add a new
|
|
64
|
-
router.post('/projects/:id/connections
|
|
64
|
+
// Add a new connection to a project
|
|
65
|
+
router.post('/projects/:id/connections', async (req, res) => {
|
|
65
66
|
try {
|
|
66
67
|
const auth = req.auth;
|
|
67
68
|
if (auth.role !== 'admin' && auth.role !== 'editor') {
|
|
68
69
|
return res.status(403).json({ message: 'Permission denied' });
|
|
69
70
|
}
|
|
70
71
|
const projectId = req.params.id;
|
|
71
|
-
const { name, apiUrl, method, description,
|
|
72
|
+
const { name, apiUrl, method, description, token, prefix } = req.body;
|
|
72
73
|
if (!name || !apiUrl) {
|
|
73
74
|
return res.status(400).json({ message: 'name and apiUrl are required' });
|
|
74
75
|
}
|
|
@@ -82,35 +83,29 @@ function connectionRouter(config) {
|
|
|
82
83
|
if (!(0, auth_1.isProjectMember)(project, auth.userId)) {
|
|
83
84
|
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
84
85
|
}
|
|
85
|
-
|
|
86
|
-
const credExists = project.credentials?.some((c) => c.id === credentialId);
|
|
87
|
-
if (!credExists) {
|
|
88
|
-
return res.status(400).json({ message: 'Credential not found in project' });
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
const connection = await (0, apiConnection_1.addApiConnection)(config, projectId, {
|
|
86
|
+
const connection = await (0, connection_1.addConnection)(config, projectId, {
|
|
92
87
|
name,
|
|
93
88
|
apiUrl,
|
|
94
89
|
method,
|
|
95
90
|
prefix,
|
|
96
91
|
...(description !== undefined && { description }),
|
|
97
|
-
...(
|
|
92
|
+
...(token !== undefined && { token }),
|
|
98
93
|
});
|
|
99
|
-
return res.status(201).json({ data: connection });
|
|
94
|
+
return res.status(201).json({ data: (0, sanitize_1.sanitizeConnection)(connection) });
|
|
100
95
|
}
|
|
101
96
|
catch (err) {
|
|
102
|
-
res.status(500).json({ message: 'Failed to add
|
|
97
|
+
res.status(500).json({ message: 'Failed to add connection', details: err.message });
|
|
103
98
|
}
|
|
104
99
|
});
|
|
105
|
-
// Update
|
|
106
|
-
router.patch('/projects/:id/connections
|
|
100
|
+
// Update a connection (name, url, method, prefix, token, description)
|
|
101
|
+
router.patch('/projects/:id/connections/:connectionId', async (req, res) => {
|
|
107
102
|
try {
|
|
108
103
|
const auth = req.auth;
|
|
109
104
|
if (auth.role !== 'admin' && auth.role !== 'editor') {
|
|
110
105
|
return res.status(403).json({ message: 'Permission denied' });
|
|
111
106
|
}
|
|
112
107
|
const { id: projectId, connectionId } = req.params;
|
|
113
|
-
const { name, apiUrl, method, description,
|
|
108
|
+
const { name, apiUrl, method, description, token, prefix } = req.body;
|
|
114
109
|
if (method && !VALID_METHODS.includes(method)) {
|
|
115
110
|
return res.status(400).json({ message: `method must be one of: ${VALID_METHODS.join(', ')}` });
|
|
116
111
|
}
|
|
@@ -120,27 +115,21 @@ function connectionRouter(config) {
|
|
|
120
115
|
if (!(0, auth_1.isProjectMember)(project, auth.userId)) {
|
|
121
116
|
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
122
117
|
}
|
|
123
|
-
|
|
124
|
-
const credExists = project.credentials?.some((c) => c.id === credentialId);
|
|
125
|
-
if (!credExists) {
|
|
126
|
-
return res.status(400).json({ message: 'Credential not found in project' });
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
const updated = await (0, apiConnection_1.updateApiConnection)(config, projectId, connectionId, {
|
|
118
|
+
const updated = await (0, connection_1.updateConnection)(config, projectId, connectionId, {
|
|
130
119
|
...(name !== undefined && { name }),
|
|
131
120
|
...(apiUrl !== undefined && { apiUrl }),
|
|
132
121
|
...(method !== undefined && { method }),
|
|
133
122
|
...(description !== undefined && { description }),
|
|
134
123
|
...(prefix !== undefined && { prefix }),
|
|
135
|
-
...(
|
|
124
|
+
...(token !== undefined && { token }),
|
|
136
125
|
});
|
|
137
|
-
return res.status(200).json({ data: updated });
|
|
126
|
+
return res.status(200).json({ data: (0, sanitize_1.sanitizeConnection)(updated) });
|
|
138
127
|
}
|
|
139
128
|
catch (err) {
|
|
140
|
-
res.status(500).json({ message: 'Failed to update
|
|
129
|
+
res.status(500).json({ message: 'Failed to update connection', details: err.message });
|
|
141
130
|
}
|
|
142
131
|
});
|
|
143
|
-
// Execute the request represented by
|
|
132
|
+
// Execute the request represented by a connection
|
|
144
133
|
router.get('/projects/:id/get-connection/:connectionId', async (req, res) => {
|
|
145
134
|
try {
|
|
146
135
|
const auth = req.auth;
|
|
@@ -152,25 +141,21 @@ function connectionRouter(config) {
|
|
|
152
141
|
if (!(0, auth_1.isProjectMember)(project, auth.userId)) {
|
|
153
142
|
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
154
143
|
}
|
|
155
|
-
const
|
|
156
|
-
if (!
|
|
157
|
-
return res.status(404).json({ message: '
|
|
144
|
+
const connection = project.connections?.find((c) => c.id === connectionId);
|
|
145
|
+
if (!connection) {
|
|
146
|
+
return res.status(404).json({ message: 'Connection not found' });
|
|
158
147
|
}
|
|
159
148
|
const headers = { 'Content-Type': 'application/json' };
|
|
160
|
-
if (
|
|
149
|
+
if (connection.encryptedCredential) {
|
|
161
150
|
const { decrypt, isEncryptionKeySet } = await Promise.resolve().then(() => __importStar(require('../../utils/encryption')));
|
|
162
151
|
if (!isEncryptionKeySet()) {
|
|
163
152
|
return res.status(500).json({ message: 'Encryption key is not set' });
|
|
164
153
|
}
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
return res.status(400).json({ message: 'Credential not found in project' });
|
|
168
|
-
}
|
|
169
|
-
const decrypted = decrypt(credential.encryptedValue);
|
|
170
|
-
const prefix = apiConnection.prefix ? `${apiConnection.prefix} ` : '';
|
|
154
|
+
const decrypted = decrypt(connection.encryptedCredential);
|
|
155
|
+
const prefix = connection.prefix ? `${connection.prefix} ` : '';
|
|
171
156
|
headers['Authorization'] = `${prefix}${decrypted}`;
|
|
172
157
|
}
|
|
173
|
-
const response = await fetch(
|
|
158
|
+
const response = await fetch(connection.apiUrl, { method: connection.method || 'GET', headers });
|
|
174
159
|
if (!response.ok) {
|
|
175
160
|
return res.status(response.status).json({ message: `External API error: ${response.statusText}` });
|
|
176
161
|
}
|
|
@@ -181,8 +166,8 @@ function connectionRouter(config) {
|
|
|
181
166
|
res.status(500).json({ message: 'Failed to fetch connection data', details: err.message });
|
|
182
167
|
}
|
|
183
168
|
});
|
|
184
|
-
// Delete
|
|
185
|
-
router.delete('/projects/:id/connections
|
|
169
|
+
// Delete a connection from a project
|
|
170
|
+
router.delete('/projects/:id/connections/:connectionId', async (req, res) => {
|
|
186
171
|
try {
|
|
187
172
|
const auth = req.auth;
|
|
188
173
|
if (auth.role !== 'admin' && auth.role !== 'editor') {
|
|
@@ -197,15 +182,15 @@ function connectionRouter(config) {
|
|
|
197
182
|
if (!(0, auth_1.isProjectMember)(project, auth.userId)) {
|
|
198
183
|
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
199
184
|
}
|
|
200
|
-
const exists = project.
|
|
185
|
+
const exists = project.connections?.some((c) => c.id === connectionId);
|
|
201
186
|
if (!exists) {
|
|
202
|
-
return res.status(404).json({ message: '
|
|
187
|
+
return res.status(404).json({ message: 'Connection not found' });
|
|
203
188
|
}
|
|
204
|
-
await (0,
|
|
205
|
-
return res.status(200).json({ message: '
|
|
189
|
+
await (0, connection_1.deleteConnection)(config, projectId, connectionId);
|
|
190
|
+
return res.status(200).json({ message: 'Connection deleted successfully.' });
|
|
206
191
|
}
|
|
207
192
|
catch (err) {
|
|
208
|
-
res.status(500).json({ message: 'Failed to delete
|
|
193
|
+
res.status(500).json({ message: 'Failed to delete connection', details: err.message });
|
|
209
194
|
}
|
|
210
195
|
});
|
|
211
196
|
return router;
|
|
@@ -41,6 +41,8 @@ const auth_1 = require("../../utils/auth");
|
|
|
41
41
|
const connectionManager_1 = require("../../adapters/database/connectionManager");
|
|
42
42
|
const builtinAccess_1 = require("../../utils/builtinAccess");
|
|
43
43
|
const userIntegration_1 = require("../../services/userIntegration");
|
|
44
|
+
const sanitize_1 = require("../../utils/sanitize");
|
|
45
|
+
const encryption_1 = require("../../utils/encryption");
|
|
44
46
|
const dbConnection_1 = require("../../services/dbConnection");
|
|
45
47
|
const VALID_DB_TYPES = ['postgresql', 'mongodb'];
|
|
46
48
|
// `builtinId` is config.databases[].id (e.g. 'mongo'), NOT the DbConnection row's own id —
|
|
@@ -78,10 +80,9 @@ async function getDecryptedConnectionString(project, connId) {
|
|
|
78
80
|
const { decrypt, isEncryptionKeySet } = await Promise.resolve().then(() => __importStar(require('../../utils/encryption')));
|
|
79
81
|
if (!isEncryptionKeySet())
|
|
80
82
|
return { error: 'Encryption key is not set', status: 500 };
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
return { connectionString: decrypt(credential.encryptedValue), conn };
|
|
83
|
+
if (!conn.encryptedCredential)
|
|
84
|
+
return { error: 'Connection has no stored credential', status: 400 };
|
|
85
|
+
return { connectionString: decrypt(conn.encryptedCredential), conn };
|
|
85
86
|
}
|
|
86
87
|
function dbConnectionRouter(config) {
|
|
87
88
|
const router = (0, express_1.Router)();
|
|
@@ -95,7 +96,7 @@ function dbConnectionRouter(config) {
|
|
|
95
96
|
return res.status(404).json({ message: 'Project not found' });
|
|
96
97
|
if (!(0, auth_1.isProjectMember)(project, auth.userId))
|
|
97
98
|
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
98
|
-
return res.status(200).json({ data: project.dbConnections
|
|
99
|
+
return res.status(200).json({ data: (0, sanitize_1.sanitizeConnections)(project.dbConnections) });
|
|
99
100
|
}
|
|
100
101
|
catch (err) {
|
|
101
102
|
res.status(500).json({ message: 'Failed to fetch DB connections', details: err.message });
|
|
@@ -108,15 +109,15 @@ function dbConnectionRouter(config) {
|
|
|
108
109
|
if (auth.role !== 'admin' && auth.role !== 'editor') {
|
|
109
110
|
return res.status(403).json({ message: 'Permission denied' });
|
|
110
111
|
}
|
|
111
|
-
const { name, dbType,
|
|
112
|
+
const { name, dbType, credential, integrationId, description } = req.body;
|
|
112
113
|
if (!name || !dbType) {
|
|
113
114
|
return res.status(400).json({ message: 'name and dbType are required' });
|
|
114
115
|
}
|
|
115
|
-
if (!
|
|
116
|
-
return res.status(400).json({ message: 'Either
|
|
116
|
+
if (!credential && !integrationId) {
|
|
117
|
+
return res.status(400).json({ message: 'Either credential or integrationId is required' });
|
|
117
118
|
}
|
|
118
|
-
if (
|
|
119
|
-
return res.status(400).json({ message: 'Provide only one of
|
|
119
|
+
if (credential && integrationId) {
|
|
120
|
+
return res.status(400).json({ message: 'Provide only one of credential or integrationId' });
|
|
120
121
|
}
|
|
121
122
|
if (!VALID_DB_TYPES.includes(dbType)) {
|
|
122
123
|
return res.status(400).json({ message: `dbType must be one of: ${VALID_DB_TYPES.join(', ')}` });
|
|
@@ -126,6 +127,7 @@ function dbConnectionRouter(config) {
|
|
|
126
127
|
return res.status(404).json({ message: 'Project not found' });
|
|
127
128
|
if (!(0, auth_1.isProjectMember)(project, auth.userId))
|
|
128
129
|
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
130
|
+
let encryptedCredential;
|
|
129
131
|
if (integrationId) {
|
|
130
132
|
const UserIntegrationModel = (0, manager_1.getModel)('user_integrations');
|
|
131
133
|
const integration = await UserIntegrationModel.findOne({ id: integrationId, userId: auth.userId });
|
|
@@ -135,23 +137,23 @@ function dbConnectionRouter(config) {
|
|
|
135
137
|
return res.status(400).json({ message: 'Integration is not a database integration' });
|
|
136
138
|
}
|
|
137
139
|
else {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
140
|
+
if (!(0, encryption_1.isEncryptionKeySet)())
|
|
141
|
+
return res.status(500).json({ message: 'Encryption key is not set' });
|
|
142
|
+
encryptedCredential = (0, encryption_1.encrypt)(credential);
|
|
141
143
|
}
|
|
142
144
|
const connection = {
|
|
143
145
|
id: new mongodb_1.ObjectId().toHexString(),
|
|
144
146
|
name,
|
|
145
147
|
dbType,
|
|
146
|
-
credentialId: credentialId ?? '',
|
|
147
148
|
projectId: req.params.id,
|
|
148
149
|
createdAt: new Date(),
|
|
149
150
|
source: integrationId ? 'integration' : 'manual',
|
|
150
151
|
...(integrationId && { integrationId }),
|
|
152
|
+
...(encryptedCredential !== undefined && { encryptedCredential }),
|
|
151
153
|
...(description !== undefined && { description }),
|
|
152
154
|
};
|
|
153
155
|
await Project.update({ _id: req.params.id }, { $push: { dbConnections: connection } });
|
|
154
|
-
return res.status(201).json({ data: connection });
|
|
156
|
+
return res.status(201).json({ data: (0, sanitize_1.sanitizeConnection)(connection) });
|
|
155
157
|
}
|
|
156
158
|
catch (err) {
|
|
157
159
|
res.status(500).json({ message: 'Failed to add DB connection', details: err.message });
|
|
@@ -9,6 +9,7 @@ const manager_1 = require("../../models/manager");
|
|
|
9
9
|
const auth_1 = require("../../utils/auth");
|
|
10
10
|
const wsHub_1 = require("../../services/wsHub");
|
|
11
11
|
const connectionManager_1 = require("../../adapters/database/connectionManager");
|
|
12
|
+
const sanitize_1 = require("../../utils/sanitize");
|
|
12
13
|
const crypto_1 = __importDefault(require("crypto"));
|
|
13
14
|
function getDb() {
|
|
14
15
|
const ids = (0, connectionManager_1.getConnectedIds)().filter(id => (0, connectionManager_1.getConnection)(id).type === 'nosql');
|
|
@@ -33,7 +34,7 @@ function distributionRouter(_config) {
|
|
|
33
34
|
return res.status(404).json({ message: 'Project not found' });
|
|
34
35
|
if (!(0, auth_1.isProjectMember)(project, auth.userId))
|
|
35
36
|
return res.status(403).json({ message: 'Unauthorized' });
|
|
36
|
-
res.json({ connections: project.distributionConnections
|
|
37
|
+
res.json({ connections: (0, sanitize_1.sanitizeConnections)(project.distributionConnections) });
|
|
37
38
|
}
|
|
38
39
|
catch (err) {
|
|
39
40
|
res.status(500).json({ message: err.message });
|
|
@@ -43,7 +44,7 @@ function distributionRouter(_config) {
|
|
|
43
44
|
try {
|
|
44
45
|
const auth = req.auth;
|
|
45
46
|
const { projectId } = req.params;
|
|
46
|
-
const { name, target,
|
|
47
|
+
const { name, target, encryptedCredential, config, description } = req.body;
|
|
47
48
|
if (!name || !target)
|
|
48
49
|
return res.status(400).json({ message: 'name and target are required' });
|
|
49
50
|
const project = await Project.findOne({ _id: projectId });
|
|
@@ -55,14 +56,14 @@ function distributionRouter(_config) {
|
|
|
55
56
|
id: crypto_1.default.randomUUID(),
|
|
56
57
|
name,
|
|
57
58
|
target,
|
|
58
|
-
|
|
59
|
+
encryptedCredential,
|
|
59
60
|
config: config ?? {},
|
|
60
61
|
description,
|
|
61
62
|
projectId,
|
|
62
63
|
createdAt: new Date(),
|
|
63
64
|
};
|
|
64
65
|
await Project.update({ _id: projectId }, { $push: { distributionConnections: connection } });
|
|
65
|
-
res.status(201).json({ connection });
|
|
66
|
+
res.status(201).json({ connection: (0, sanitize_1.sanitizeConnection)(connection) });
|
|
66
67
|
}
|
|
67
68
|
catch (err) {
|
|
68
69
|
res.status(500).json({ message: err.message });
|
|
@@ -120,19 +120,16 @@ function exportRouter(config) {
|
|
|
120
120
|
return res.status(404).json({ message: 'Project not found.' });
|
|
121
121
|
}
|
|
122
122
|
let headers = {};
|
|
123
|
-
if (exportMetadata.
|
|
123
|
+
if (exportMetadata.encryptedCredential) {
|
|
124
124
|
if (!(0, encryption_1.isEncryptionKeySet)()) {
|
|
125
125
|
throw new Error('Encryption key is not set. Cannot use encrypted credentials.');
|
|
126
126
|
}
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
'Content-Type': 'application/json',
|
|
134
|
-
};
|
|
135
|
-
}
|
|
127
|
+
const decryptedValue = (0, encryption_1.decrypt)(exportMetadata.encryptedCredential);
|
|
128
|
+
const authPrefix = exportMetadata.prefix ? `${exportMetadata.prefix} ` : '';
|
|
129
|
+
headers = {
|
|
130
|
+
'Authorization': `${authPrefix}${decryptedValue}`,
|
|
131
|
+
'Content-Type': 'application/json',
|
|
132
|
+
};
|
|
136
133
|
}
|
|
137
134
|
const externalApiData = await fetch(exportMetadata.apiUrl, { headers });
|
|
138
135
|
const apiResponse = await externalApiData.json();
|
|
@@ -168,7 +165,7 @@ function exportRouter(config) {
|
|
|
168
165
|
createdAt: rawData?.createdAt,
|
|
169
166
|
updatedAt: rawData?.updatedAt,
|
|
170
167
|
type: exportMetadata.type,
|
|
171
|
-
|
|
168
|
+
hasCredential: !!exportMetadata.encryptedCredential,
|
|
172
169
|
description: rawData?.description,
|
|
173
170
|
fields: exportMetadata.fields,
|
|
174
171
|
};
|
|
@@ -372,15 +369,11 @@ function exportRouter(config) {
|
|
|
372
369
|
}
|
|
373
370
|
else if (exportMetadata.type === 'externalApi') {
|
|
374
371
|
let headers = {};
|
|
375
|
-
if (exportMetadata.
|
|
372
|
+
if (exportMetadata.encryptedCredential) {
|
|
376
373
|
if (!(0, encryption_1.isEncryptionKeySet)()) {
|
|
377
374
|
throw new Error('Encryption key is not set. Cannot use encrypted credentials.');
|
|
378
375
|
}
|
|
379
|
-
const
|
|
380
|
-
if (!credential) {
|
|
381
|
-
throw new Error(`Credential with ID ${exportMetadata.credentialId} not found.`);
|
|
382
|
-
}
|
|
383
|
-
const decryptedValue = (0, encryption_1.decrypt)(credential.encryptedValue);
|
|
376
|
+
const decryptedValue = (0, encryption_1.decrypt)(exportMetadata.encryptedCredential);
|
|
384
377
|
const authPrefix = exportMetadata.prefix ? `${exportMetadata.prefix} ` : '';
|
|
385
378
|
headers = {
|
|
386
379
|
'Authorization': `${authPrefix}${decryptedValue}`,
|
|
@@ -193,7 +193,7 @@ function projectRouter(config) {
|
|
|
193
193
|
useConnections: e.useConnections,
|
|
194
194
|
useCredentials: e.useCredentials,
|
|
195
195
|
}));
|
|
196
|
-
const sanitized = (0, sanitize_1.sanitizeProject)({ ...project, id: project._id || project.id, _id: undefined,
|
|
196
|
+
const sanitized = (0, sanitize_1.sanitizeProject)({ ...project, id: project._id || project.id, _id: undefined, connections: project.connections || [], exports });
|
|
197
197
|
res.json({ project: sanitized, message: 'Project fetched successfully' });
|
|
198
198
|
}
|
|
199
199
|
catch (err) {
|