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.
@@ -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
- credentials JSONB DEFAULT '[]',
66
- "apiConnections" JSONB DEFAULT '[]',
67
- "dbConnections" JSONB DEFAULT '[]',
68
- "dbType" VARCHAR(50) NOT NULL DEFAULT 'sql',
69
- "createdAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
70
- "updatedAt" TIMESTAMP WITH TIME ZONE DEFAULT NOW()
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 credentials JSONB DEFAULT '[]';
79
- ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "apiConnections" JSONB DEFAULT '[]';
80
- ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "dbConnections" JSONB DEFAULT '[]';
81
- ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "storageConnections" JSONB DEFAULT '[]';
82
- ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS workflow JSONB;
83
- ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS pipelines JSONB DEFAULT '[]';
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', 'apiConnectionNode',
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 apiConnection_1 = require("../../services/apiConnection");
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 API connections for a project
46
- router.get('/projects/:id/connections/api', async (req, res) => {
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.apiConnections || [] });
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 API connections', details: err.message });
61
+ res.status(500).json({ message: 'Failed to fetch connections', details: err.message });
61
62
  }
62
63
  });
63
- // Add a new API connection to a project
64
- router.post('/projects/:id/connections/api', async (req, res) => {
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, credentialId, prefix } = req.body;
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
- if (credentialId) {
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
- ...(credentialId !== undefined && { credentialId }),
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 API connection', details: err.message });
97
+ res.status(500).json({ message: 'Failed to add connection', details: err.message });
103
98
  }
104
99
  });
105
- // Update an API connection (name, url, method, prefix, credentialId, description)
106
- router.patch('/projects/:id/connections/api/:connectionId', async (req, res) => {
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, credentialId, prefix } = req.body;
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
- if (credentialId) {
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
- ...(credentialId !== undefined && { credentialId }),
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 API connection', details: err.message });
129
+ res.status(500).json({ message: 'Failed to update connection', details: err.message });
141
130
  }
142
131
  });
143
- // Execute the request represented by an API connection
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 apiConnection = project.apiConnections?.find((c) => c.id === connectionId);
156
- if (!apiConnection) {
157
- return res.status(404).json({ message: 'API connection not found' });
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 (apiConnection.credentialId) {
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 credential = project.credentials?.find((c) => c.id === apiConnection.credentialId);
166
- if (!credential) {
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(apiConnection.apiUrl, { method: apiConnection.method || 'GET', headers });
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 an API connection from a project
185
- router.delete('/projects/:id/connections/api/:connectionId', async (req, res) => {
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.apiConnections?.some((c) => c.id === connectionId);
185
+ const exists = project.connections?.some((c) => c.id === connectionId);
201
186
  if (!exists) {
202
- return res.status(404).json({ message: 'API connection not found' });
187
+ return res.status(404).json({ message: 'Connection not found' });
203
188
  }
204
- await (0, apiConnection_1.deleteApiConnection)(config, projectId, connectionId);
205
- return res.status(200).json({ message: 'API connection deleted successfully.' });
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 API connection', details: err.message });
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
- const credential = project.credentials?.find((c) => c.id === conn.credentialId);
82
- if (!credential)
83
- return { error: 'Credential not found in project', status: 400 };
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, credentialId, integrationId, description } = req.body;
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 (!credentialId && !integrationId) {
116
- return res.status(400).json({ message: 'Either credentialId or integrationId is required' });
116
+ if (!credential && !integrationId) {
117
+ return res.status(400).json({ message: 'Either credential or integrationId is required' });
117
118
  }
118
- if (credentialId && integrationId) {
119
- return res.status(400).json({ message: 'Provide only one of credentialId or integrationId' });
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
- const credExists = project.credentials?.some((c) => c.id === credentialId);
139
- if (!credExists)
140
- return res.status(400).json({ message: 'Credential not found in project' });
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, credentialId, config, description } = req.body;
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
- credentialId,
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.credentialId) {
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 credential = project.credentials?.find((cred) => cred.id === exportMetadata.credentialId);
128
- if (credential) {
129
- const decryptedValue = (0, encryption_1.decrypt)(credential.encryptedValue);
130
- const authPrefix = exportMetadata.prefix ? `${exportMetadata.prefix} ` : '';
131
- headers = {
132
- 'Authorization': `${authPrefix}${decryptedValue}`,
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
- credentialId: exportMetadata.credentialId,
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.credentialId) {
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 credential = project.credentials?.find((cred) => cred.id === exportMetadata.credentialId);
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, apiConnections: project.apiConnections || [], exports });
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) {