@friggframework/core 2.0.0-next.45 → 2.0.0-next.46
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 +28 -0
- package/application/commands/integration-commands.js +19 -0
- package/core/Worker.js +8 -21
- package/credential/repositories/credential-repository-mongo.js +14 -8
- package/credential/repositories/credential-repository-postgres.js +14 -8
- package/credential/repositories/credential-repository.js +3 -8
- package/database/MONGODB_TRANSACTION_FIX.md +198 -0
- package/database/adapters/lambda-invoker.js +97 -0
- package/database/config.js +11 -2
- package/database/models/WebsocketConnection.js +11 -10
- package/database/prisma.js +63 -3
- package/database/repositories/health-check-repository-mongodb.js +3 -0
- package/database/repositories/migration-status-repository-s3.js +137 -0
- package/database/use-cases/check-database-state-use-case.js +81 -0
- package/database/use-cases/check-encryption-health-use-case.js +3 -2
- package/database/use-cases/get-database-state-via-worker-use-case.js +61 -0
- package/database/use-cases/get-migration-status-use-case.js +93 -0
- package/database/use-cases/run-database-migration-use-case.js +137 -0
- package/database/use-cases/trigger-database-migration-use-case.js +157 -0
- package/database/utils/mongodb-collection-utils.js +91 -0
- package/database/utils/mongodb-schema-init.js +106 -0
- package/database/utils/prisma-runner.js +400 -0
- package/database/utils/prisma-schema-parser.js +182 -0
- package/encrypt/Cryptor.js +14 -16
- package/generated/prisma-mongodb/client.d.ts +1 -0
- package/generated/prisma-mongodb/client.js +4 -0
- package/generated/prisma-mongodb/default.d.ts +1 -0
- package/generated/prisma-mongodb/default.js +4 -0
- package/generated/prisma-mongodb/edge.d.ts +1 -0
- package/generated/prisma-mongodb/edge.js +334 -0
- package/generated/prisma-mongodb/index-browser.js +316 -0
- package/generated/prisma-mongodb/index.d.ts +22897 -0
- package/generated/prisma-mongodb/index.js +359 -0
- package/generated/prisma-mongodb/package.json +183 -0
- package/generated/prisma-mongodb/query-engine-debian-openssl-3.0.x +0 -0
- package/generated/prisma-mongodb/query-engine-rhel-openssl-3.0.x +0 -0
- package/generated/prisma-mongodb/runtime/binary.d.ts +1 -0
- package/generated/prisma-mongodb/runtime/binary.js +289 -0
- package/generated/prisma-mongodb/runtime/edge-esm.js +34 -0
- package/generated/prisma-mongodb/runtime/edge.js +34 -0
- package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
- package/generated/prisma-mongodb/runtime/index-browser.js +16 -0
- package/generated/prisma-mongodb/runtime/library.d.ts +3977 -0
- package/generated/prisma-mongodb/runtime/react-native.js +83 -0
- package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +84 -0
- package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +36 -0
- package/generated/prisma-mongodb/schema.prisma +362 -0
- package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +4 -0
- package/generated/prisma-mongodb/wasm-worker-loader.mjs +4 -0
- package/generated/prisma-mongodb/wasm.d.ts +1 -0
- package/generated/prisma-mongodb/wasm.js +341 -0
- package/generated/prisma-postgresql/client.d.ts +1 -0
- package/generated/prisma-postgresql/client.js +4 -0
- package/generated/prisma-postgresql/default.d.ts +1 -0
- package/generated/prisma-postgresql/default.js +4 -0
- package/generated/prisma-postgresql/edge.d.ts +1 -0
- package/generated/prisma-postgresql/edge.js +356 -0
- package/generated/prisma-postgresql/index-browser.js +338 -0
- package/generated/prisma-postgresql/index.d.ts +25071 -0
- package/generated/prisma-postgresql/index.js +381 -0
- package/generated/prisma-postgresql/package.json +183 -0
- package/generated/prisma-postgresql/query-engine-debian-openssl-3.0.x +0 -0
- package/generated/prisma-postgresql/query-engine-rhel-openssl-3.0.x +0 -0
- package/generated/prisma-postgresql/query_engine_bg.js +2 -0
- package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
- package/generated/prisma-postgresql/runtime/binary.d.ts +1 -0
- package/generated/prisma-postgresql/runtime/binary.js +289 -0
- package/generated/prisma-postgresql/runtime/edge-esm.js +34 -0
- package/generated/prisma-postgresql/runtime/edge.js +34 -0
- package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
- package/generated/prisma-postgresql/runtime/index-browser.js +16 -0
- package/generated/prisma-postgresql/runtime/library.d.ts +3977 -0
- package/generated/prisma-postgresql/runtime/react-native.js +83 -0
- package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +84 -0
- package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +36 -0
- package/generated/prisma-postgresql/schema.prisma +345 -0
- package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +4 -0
- package/generated/prisma-postgresql/wasm-worker-loader.mjs +4 -0
- package/generated/prisma-postgresql/wasm.d.ts +1 -0
- package/generated/prisma-postgresql/wasm.js +363 -0
- package/handlers/database-migration-handler.js +227 -0
- package/handlers/routers/auth.js +1 -1
- package/handlers/routers/db-migration.handler.js +29 -0
- package/handlers/routers/db-migration.js +256 -0
- package/handlers/routers/health.js +41 -6
- package/handlers/routers/integration-webhook-routers.js +2 -2
- package/handlers/use-cases/check-integrations-health-use-case.js +22 -10
- package/handlers/workers/db-migration.js +352 -0
- package/index.js +12 -0
- package/integrations/integration-router.js +60 -70
- package/integrations/repositories/integration-repository-interface.js +12 -0
- package/integrations/repositories/integration-repository-mongo.js +32 -0
- package/integrations/repositories/integration-repository-postgres.js +33 -0
- package/integrations/repositories/process-repository-postgres.js +2 -2
- package/integrations/tests/doubles/test-integration-repository.js +2 -2
- package/logs/logger.js +0 -4
- package/modules/entity.js +0 -1
- package/modules/repositories/module-repository-mongo.js +3 -12
- package/modules/repositories/module-repository-postgres.js +0 -11
- package/modules/repositories/module-repository.js +1 -12
- package/modules/use-cases/get-entity-options-by-id.js +1 -1
- package/modules/use-cases/get-module.js +1 -2
- package/modules/use-cases/refresh-entity-options.js +1 -1
- package/modules/use-cases/test-module-auth.js +1 -1
- package/package.json +82 -66
- package/prisma-mongodb/schema.prisma +21 -21
- package/prisma-postgresql/schema.prisma +15 -15
- package/queues/queuer-util.js +24 -21
- package/types/core/index.d.ts +2 -2
- package/types/module-plugin/index.d.ts +0 -2
- package/user/use-cases/authenticate-user.js +127 -0
- package/user/use-cases/authenticate-with-shared-secret.js +48 -0
- package/user/use-cases/get-user-from-adopter-jwt.js +149 -0
- package/user/use-cases/get-user-from-x-frigg-headers.js +106 -0
- package/user/user.js +16 -0
- package/websocket/repositories/websocket-connection-repository-mongo.js +11 -10
- package/websocket/repositories/websocket-connection-repository-postgres.js +11 -10
- package/websocket/repositories/websocket-connection-repository.js +11 -10
- package/application/commands/integration-commands.test.js +0 -123
- package/database/encryption/encryption-integration.test.js +0 -553
- package/database/encryption/encryption-schema-registry.test.js +0 -392
- package/database/encryption/field-encryption-service.test.js +0 -525
- package/database/encryption/mongo-decryption-fix-verification.test.js +0 -348
- package/database/encryption/postgres-decryption-fix-verification.test.js +0 -371
- package/database/encryption/postgres-relation-decryption.test.js +0 -245
- package/database/encryption/prisma-encryption-extension.test.js +0 -439
- package/errors/base-error.test.js +0 -32
- package/errors/fetch-error.test.js +0 -79
- package/errors/halt-error.test.js +0 -11
- package/errors/validation-errors.test.js +0 -120
- package/handlers/auth-flow.integration.test.js +0 -147
- package/handlers/integration-event-dispatcher.test.js +0 -209
- package/handlers/routers/health.test.js +0 -210
- package/handlers/routers/integration-webhook-routers.test.js +0 -126
- package/handlers/webhook-flow.integration.test.js +0 -356
- package/handlers/workers/integration-defined-workers.test.js +0 -184
- package/integrations/tests/use-cases/create-integration.test.js +0 -131
- package/integrations/tests/use-cases/delete-integration-for-user.test.js +0 -150
- package/integrations/tests/use-cases/find-integration-context-by-external-entity-id.test.js +0 -92
- package/integrations/tests/use-cases/get-integration-for-user.test.js +0 -150
- package/integrations/tests/use-cases/get-integration-instance.test.js +0 -176
- package/integrations/tests/use-cases/get-integrations-for-user.test.js +0 -176
- package/integrations/tests/use-cases/get-possible-integrations.test.js +0 -188
- package/integrations/tests/use-cases/update-integration-messages.test.js +0 -142
- package/integrations/tests/use-cases/update-integration-status.test.js +0 -103
- package/integrations/tests/use-cases/update-integration.test.js +0 -141
- package/integrations/use-cases/create-process.test.js +0 -178
- package/integrations/use-cases/get-process.test.js +0 -190
- package/integrations/use-cases/load-integration-context-full.test.js +0 -329
- package/integrations/use-cases/load-integration-context.test.js +0 -114
- package/integrations/use-cases/update-process-metrics.test.js +0 -308
- package/integrations/use-cases/update-process-state.test.js +0 -256
- package/lambda/TimeoutCatcher.test.js +0 -68
- package/logs/logger.test.js +0 -76
- package/modules/module-hydration.test.js +0 -205
- package/modules/requester/requester.test.js +0 -28
- package/user/tests/use-cases/create-individual-user.test.js +0 -24
- package/user/tests/use-cases/create-organization-user.test.js +0 -28
- package/user/tests/use-cases/create-token-for-user-id.test.js +0 -19
- package/user/tests/use-cases/get-user-from-bearer-token.test.js +0 -64
- package/user/tests/use-cases/login-user.test.js +0 -220
- package/user/tests/user-password-encryption-isolation.test.js +0 -237
- package/user/tests/user-password-hashing.test.js +0 -235
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
const { execSync, spawn } = require('child_process');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Prisma Command Runner Utility
|
|
8
|
+
* Handles execution of Prisma CLI commands for database setup
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Gets the path to the Prisma schema file for the database type
|
|
13
|
+
* @param {'mongodb'|'postgresql'} dbType - Database type
|
|
14
|
+
* @param {string} projectRoot - Project root directory
|
|
15
|
+
* @returns {string} Absolute path to schema file
|
|
16
|
+
* @throws {Error} If schema file doesn't exist
|
|
17
|
+
*/
|
|
18
|
+
function getPrismaSchemaPath(dbType, projectRoot = process.cwd()) {
|
|
19
|
+
// Try multiple locations for the schema file
|
|
20
|
+
// Priority order:
|
|
21
|
+
// 1. Lambda layer path (where the schema actually exists in deployed Lambda)
|
|
22
|
+
// 2. Local node_modules (where @friggframework/core is installed - production scenario)
|
|
23
|
+
// 3. Parent node_modules (workspace/monorepo setup)
|
|
24
|
+
const possiblePaths = [
|
|
25
|
+
// Lambda layer path - this is where the schema actually exists in deployed Lambda
|
|
26
|
+
`/opt/nodejs/node_modules/generated/prisma-${dbType}/schema.prisma`,
|
|
27
|
+
// Check where Frigg is installed via npm (production scenario)
|
|
28
|
+
path.join(projectRoot, 'node_modules', '@friggframework', 'core', `prisma-${dbType}`, 'schema.prisma'),
|
|
29
|
+
path.join(projectRoot, '..', 'node_modules', '@friggframework', 'core', `prisma-${dbType}`, 'schema.prisma')
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
for (const schemaPath of possiblePaths) {
|
|
33
|
+
if (fs.existsSync(schemaPath)) {
|
|
34
|
+
return schemaPath;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// If not found in any location, throw error
|
|
39
|
+
throw new Error(
|
|
40
|
+
`Prisma schema not found at:\n${possiblePaths.join('\n')}\n\n` +
|
|
41
|
+
'Ensure @friggframework/core is installed.'
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Runs prisma generate for the specified database type
|
|
47
|
+
* @param {'mongodb'|'postgresql'} dbType - Database type
|
|
48
|
+
* @param {boolean} verbose - Enable verbose output
|
|
49
|
+
* @returns {Promise<Object>} { success: boolean, output?: string, error?: string }
|
|
50
|
+
*/
|
|
51
|
+
async function runPrismaGenerate(dbType, verbose = false) {
|
|
52
|
+
try {
|
|
53
|
+
const schemaPath = getPrismaSchemaPath(dbType);
|
|
54
|
+
|
|
55
|
+
// Check if Prisma client already exists (e.g., in Lambda or pre-generated)
|
|
56
|
+
const generatedClientPath = path.join(path.dirname(path.dirname(schemaPath)), 'generated', `prisma-${dbType}`, 'client.js');
|
|
57
|
+
const isLambdaEnvironment = !!process.env.AWS_LAMBDA_FUNCTION_NAME || !!process.env.LAMBDA_TASK_ROOT;
|
|
58
|
+
|
|
59
|
+
// In Lambda, also check the layer path (/opt/nodejs/node_modules)
|
|
60
|
+
const lambdaLayerClientPath = `/opt/nodejs/node_modules/generated/prisma-${dbType}/client.js`;
|
|
61
|
+
|
|
62
|
+
const clientExists = fs.existsSync(generatedClientPath) || (isLambdaEnvironment && fs.existsSync(lambdaLayerClientPath));
|
|
63
|
+
|
|
64
|
+
if (clientExists) {
|
|
65
|
+
const foundPath = fs.existsSync(generatedClientPath) ? generatedClientPath : lambdaLayerClientPath;
|
|
66
|
+
if (verbose) {
|
|
67
|
+
console.log(chalk.gray(`✓ Prisma client already generated at: ${foundPath}`));
|
|
68
|
+
}
|
|
69
|
+
if (isLambdaEnvironment) {
|
|
70
|
+
if (verbose) {
|
|
71
|
+
console.log(chalk.gray('Skipping generation in Lambda environment (using pre-generated client)'));
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
success: true,
|
|
75
|
+
output: 'Using pre-generated Prisma client (Lambda environment)'
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (verbose) {
|
|
81
|
+
console.log(chalk.gray(`Running: npx prisma generate --schema=${schemaPath}`));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const output = execSync(
|
|
85
|
+
`npx prisma generate --schema=${schemaPath}`,
|
|
86
|
+
{
|
|
87
|
+
encoding: 'utf8',
|
|
88
|
+
stdio: verbose ? 'inherit' : 'pipe',
|
|
89
|
+
env: {
|
|
90
|
+
...process.env,
|
|
91
|
+
// Suppress Prisma telemetry prompts
|
|
92
|
+
PRISMA_HIDE_UPDATE_MESSAGE: '1'
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
success: true,
|
|
99
|
+
output: verbose ? 'Generated successfully' : output
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
} catch (error) {
|
|
103
|
+
return {
|
|
104
|
+
success: false,
|
|
105
|
+
error: error.message,
|
|
106
|
+
output: error.stdout?.toString() || error.stderr?.toString()
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Checks database migration status
|
|
113
|
+
* @param {'mongodb'|'postgresql'} dbType - Database type
|
|
114
|
+
* @returns {Promise<Object>} { upToDate: boolean, pendingMigrations?: number, error?: string }
|
|
115
|
+
*/
|
|
116
|
+
async function checkDatabaseState(dbType) {
|
|
117
|
+
try {
|
|
118
|
+
// Only applicable for PostgreSQL (MongoDB uses db push)
|
|
119
|
+
if (dbType !== 'postgresql') {
|
|
120
|
+
return { upToDate: true };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const schemaPath = getPrismaSchemaPath(dbType);
|
|
124
|
+
const prismaBin = getPrismaBinaryPath();
|
|
125
|
+
|
|
126
|
+
// Use direct path instead of npx to avoid WASM file resolution issues
|
|
127
|
+
const isDirectBinary = prismaBin !== 'npx prisma';
|
|
128
|
+
const command = isDirectBinary
|
|
129
|
+
? `${prismaBin} migrate status --schema=${schemaPath}`
|
|
130
|
+
: `npx prisma migrate status --schema=${schemaPath}`;
|
|
131
|
+
|
|
132
|
+
const output = execSync(
|
|
133
|
+
command,
|
|
134
|
+
{
|
|
135
|
+
encoding: 'utf8',
|
|
136
|
+
stdio: 'pipe',
|
|
137
|
+
env: {
|
|
138
|
+
...process.env,
|
|
139
|
+
PRISMA_HIDE_UPDATE_MESSAGE: '1'
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
if (output.includes('Database schema is up to date')) {
|
|
145
|
+
return { upToDate: true };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Parse pending migrations count
|
|
149
|
+
const pendingMatch = output.match(/(\d+) migration/);
|
|
150
|
+
const pendingMigrations = pendingMatch ? parseInt(pendingMatch[1]) : 0;
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
upToDate: false,
|
|
154
|
+
pendingMigrations
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
} catch (error) {
|
|
158
|
+
// If migrate status fails, database might not be initialized
|
|
159
|
+
return {
|
|
160
|
+
upToDate: false,
|
|
161
|
+
error: error.message
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Gets the path to the Prisma CLI entry point
|
|
168
|
+
*
|
|
169
|
+
* IMPORTANT: We invoke prisma/build/index.js directly instead of .bin/prisma
|
|
170
|
+
* because .bin/prisma uses __dirname to find WASM files, and when the symlink
|
|
171
|
+
* is resolved during Lambda packaging, __dirname points to .bin/ instead of
|
|
172
|
+
* prisma/build/, causing WASM files to not be found.
|
|
173
|
+
*
|
|
174
|
+
* @returns {string} Command to run Prisma CLI (e.g., 'node /path/to/index.js' or 'npx prisma')
|
|
175
|
+
*/
|
|
176
|
+
function getPrismaBinaryPath() {
|
|
177
|
+
const fs = require('fs');
|
|
178
|
+
|
|
179
|
+
// Check function's bundled Prisma (Lambda) - use actual CLI location
|
|
180
|
+
const functionPrisma = '/var/task/node_modules/prisma/build/index.js';
|
|
181
|
+
if (fs.existsSync(functionPrisma)) {
|
|
182
|
+
return `node ${functionPrisma}`;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Check Lambda layer path - use actual CLI location
|
|
186
|
+
const layerPrisma = '/opt/nodejs/node_modules/prisma/build/index.js';
|
|
187
|
+
if (fs.existsSync(layerPrisma)) {
|
|
188
|
+
return `node ${layerPrisma}`;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Check local node_modules - use actual CLI location
|
|
192
|
+
const localPrisma = path.join(process.cwd(), 'node_modules', 'prisma', 'build', 'index.js');
|
|
193
|
+
if (fs.existsSync(localPrisma)) {
|
|
194
|
+
return `node ${localPrisma}`;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Fallback to npx (local dev)
|
|
198
|
+
return 'npx prisma';
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Runs Prisma migrate for PostgreSQL
|
|
203
|
+
* @param {'dev'|'deploy'} command - Migration command (dev or deploy)
|
|
204
|
+
* @param {boolean} verbose - Enable verbose output
|
|
205
|
+
* @returns {Promise<Object>} { success: boolean, output?: string, error?: string }
|
|
206
|
+
*/
|
|
207
|
+
async function runPrismaMigrate(command = 'dev', verbose = false) {
|
|
208
|
+
return new Promise((resolve) => {
|
|
209
|
+
try {
|
|
210
|
+
const schemaPath = getPrismaSchemaPath('postgresql');
|
|
211
|
+
|
|
212
|
+
// Get Prisma binary path (checks multiple locations)
|
|
213
|
+
const isLambdaEnvironment = !!process.env.AWS_LAMBDA_FUNCTION_NAME || !!process.env.LAMBDA_TASK_ROOT;
|
|
214
|
+
const prismaBin = getPrismaBinaryPath();
|
|
215
|
+
|
|
216
|
+
// Determine args based on whether we're using direct binary or npx
|
|
217
|
+
// Direct binary (e.g., /var/task/node_modules/.bin/prisma): ['migrate', command, ...]
|
|
218
|
+
// npx (local dev or fallback): ['prisma', 'migrate', command, ...]
|
|
219
|
+
const isDirectBinary = prismaBin !== 'npx';
|
|
220
|
+
const args = isDirectBinary
|
|
221
|
+
? ['migrate', command, '--schema', schemaPath]
|
|
222
|
+
: ['prisma', 'migrate', command, '--schema', schemaPath];
|
|
223
|
+
|
|
224
|
+
if (verbose) {
|
|
225
|
+
const displayCmd = isDirectBinary
|
|
226
|
+
? `${prismaBin} ${args.join(' ')}`
|
|
227
|
+
: `npx ${args.join(' ')}`;
|
|
228
|
+
console.log(chalk.gray(`Running: ${displayCmd}`));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Execute the command (prismaBin might be 'node /path/to/index.js' or 'npx prisma')
|
|
232
|
+
const [executable, ...executableArgs] = prismaBin.split(' ');
|
|
233
|
+
const fullArgs = [...executableArgs, ...args];
|
|
234
|
+
|
|
235
|
+
const proc = spawn(executable, fullArgs, {
|
|
236
|
+
stdio: 'inherit',
|
|
237
|
+
env: {
|
|
238
|
+
...process.env,
|
|
239
|
+
PRISMA_HIDE_UPDATE_MESSAGE: '1'
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
proc.on('error', (error) => {
|
|
244
|
+
resolve({
|
|
245
|
+
success: false,
|
|
246
|
+
error: error.message
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
proc.on('close', (code) => {
|
|
251
|
+
if (code === 0) {
|
|
252
|
+
resolve({
|
|
253
|
+
success: true,
|
|
254
|
+
output: 'Migration completed successfully'
|
|
255
|
+
});
|
|
256
|
+
} else {
|
|
257
|
+
resolve({
|
|
258
|
+
success: false,
|
|
259
|
+
error: `Migration process exited with code ${code}`
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
} catch (error) {
|
|
265
|
+
resolve({
|
|
266
|
+
success: false,
|
|
267
|
+
error: error.message
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Runs Prisma db push for MongoDB
|
|
275
|
+
* @param {boolean} verbose - Enable verbose output
|
|
276
|
+
* @param {boolean} nonInteractive - Run in non-interactive mode (accepts data loss, for Lambda/CI)
|
|
277
|
+
* @returns {Promise<Object>} { success: boolean, output?: string, error?: string }
|
|
278
|
+
*/
|
|
279
|
+
async function runPrismaDbPush(verbose = false, nonInteractive = false) {
|
|
280
|
+
return new Promise((resolve) => {
|
|
281
|
+
try {
|
|
282
|
+
const schemaPath = getPrismaSchemaPath('mongodb');
|
|
283
|
+
|
|
284
|
+
const args = [
|
|
285
|
+
'prisma',
|
|
286
|
+
'db',
|
|
287
|
+
'push',
|
|
288
|
+
'--schema',
|
|
289
|
+
schemaPath,
|
|
290
|
+
'--skip-generate' // We generate separately
|
|
291
|
+
];
|
|
292
|
+
|
|
293
|
+
// Add non-interactive flag for Lambda/CI environments
|
|
294
|
+
if (nonInteractive) {
|
|
295
|
+
args.push('--accept-data-loss');
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (verbose) {
|
|
299
|
+
console.log(chalk.gray(`Running: npx ${args.join(' ')}`));
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (nonInteractive) {
|
|
303
|
+
console.log(chalk.yellow('⚠️ Non-interactive mode: Data loss will be automatically accepted'));
|
|
304
|
+
} else {
|
|
305
|
+
console.log(chalk.yellow('⚠️ Interactive mode: You may be prompted if schema changes cause data loss'));
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const proc = spawn('npx', args, {
|
|
309
|
+
stdio: nonInteractive ? 'pipe' : 'inherit', // Use pipe for non-interactive to capture output
|
|
310
|
+
env: {
|
|
311
|
+
...process.env,
|
|
312
|
+
PRISMA_HIDE_UPDATE_MESSAGE: '1'
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
let stdout = '';
|
|
317
|
+
let stderr = '';
|
|
318
|
+
|
|
319
|
+
// Capture output in non-interactive mode
|
|
320
|
+
if (nonInteractive) {
|
|
321
|
+
if (proc.stdout) {
|
|
322
|
+
proc.stdout.on('data', (data) => {
|
|
323
|
+
stdout += data.toString();
|
|
324
|
+
if (verbose) {
|
|
325
|
+
process.stdout.write(data);
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
if (proc.stderr) {
|
|
330
|
+
proc.stderr.on('data', (data) => {
|
|
331
|
+
stderr += data.toString();
|
|
332
|
+
if (verbose) {
|
|
333
|
+
process.stderr.write(data);
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
proc.on('error', (error) => {
|
|
340
|
+
resolve({
|
|
341
|
+
success: false,
|
|
342
|
+
error: error.message
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
proc.on('close', (code) => {
|
|
347
|
+
if (code === 0) {
|
|
348
|
+
resolve({
|
|
349
|
+
success: true,
|
|
350
|
+
output: nonInteractive ? stdout || 'Database push completed successfully' : 'Database push completed successfully'
|
|
351
|
+
});
|
|
352
|
+
} else {
|
|
353
|
+
resolve({
|
|
354
|
+
success: false,
|
|
355
|
+
error: `Database push process exited with code ${code}`,
|
|
356
|
+
output: stderr || stdout
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
} catch (error) {
|
|
362
|
+
resolve({
|
|
363
|
+
success: false,
|
|
364
|
+
error: error.message
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Determines migration command based on STAGE environment variable
|
|
372
|
+
* @param {string} stage - Stage from CLI option or environment
|
|
373
|
+
* @returns {'dev'|'deploy'}
|
|
374
|
+
*/
|
|
375
|
+
function getMigrationCommand(stage) {
|
|
376
|
+
// Always use 'deploy' in Lambda environment (it's non-interactive and doesn't create migrations)
|
|
377
|
+
const isLambdaEnvironment = !!process.env.AWS_LAMBDA_FUNCTION_NAME || !!process.env.LAMBDA_TASK_ROOT;
|
|
378
|
+
if (isLambdaEnvironment) {
|
|
379
|
+
return 'deploy';
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const normalizedStage = (stage || process.env.STAGE || 'development').toLowerCase();
|
|
383
|
+
|
|
384
|
+
const developmentStages = ['dev', 'local', 'test', 'development'];
|
|
385
|
+
|
|
386
|
+
if (developmentStages.includes(normalizedStage)) {
|
|
387
|
+
return 'dev';
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return 'deploy';
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
module.exports = {
|
|
394
|
+
getPrismaSchemaPath,
|
|
395
|
+
runPrismaGenerate,
|
|
396
|
+
checkDatabaseState,
|
|
397
|
+
runPrismaMigrate,
|
|
398
|
+
runPrismaDbPush,
|
|
399
|
+
getMigrationCommand
|
|
400
|
+
};
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prisma Schema Parser for MongoDB Collections
|
|
3
|
+
*
|
|
4
|
+
* Dynamically parses the Prisma schema file to extract MongoDB collection names.
|
|
5
|
+
* This ensures collection names stay in sync with the schema without hardcoding.
|
|
6
|
+
*
|
|
7
|
+
* Handles:
|
|
8
|
+
* - @@map() directives (custom collection names)
|
|
9
|
+
* - Models without @@map() (uses model name)
|
|
10
|
+
* - Comments and whitespace
|
|
11
|
+
* - Multiple schema file locations
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Parse Prisma schema file to extract collection names
|
|
19
|
+
*
|
|
20
|
+
* Reads the schema.prisma file and extracts all model definitions,
|
|
21
|
+
* returning the actual MongoDB collection names (from @@map directives).
|
|
22
|
+
*
|
|
23
|
+
* @param {string} schemaPath - Path to schema.prisma file
|
|
24
|
+
* @returns {Promise<string[]>} Array of collection names
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```js
|
|
28
|
+
* const collections = await parseCollectionsFromSchema('./prisma/schema.prisma');
|
|
29
|
+
* // Returns: ['User', 'Token', 'Credential', ...]
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
async function parseCollectionsFromSchema(schemaPath) {
|
|
33
|
+
try {
|
|
34
|
+
const schemaContent = await fs.promises.readFile(schemaPath, 'utf-8');
|
|
35
|
+
return extractCollectionNames(schemaContent);
|
|
36
|
+
} catch (error) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
`Failed to parse Prisma schema at ${schemaPath}: ${error.message}`
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Synchronous version of parseCollectionsFromSchema
|
|
45
|
+
*
|
|
46
|
+
* @param {string} schemaPath - Path to schema.prisma file
|
|
47
|
+
* @returns {string[]} Array of collection names
|
|
48
|
+
*/
|
|
49
|
+
function parseCollectionsFromSchemaSync(schemaPath) {
|
|
50
|
+
try {
|
|
51
|
+
const schemaContent = fs.readFileSync(schemaPath, 'utf-8');
|
|
52
|
+
return extractCollectionNames(schemaContent);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
throw new Error(
|
|
55
|
+
`Failed to parse Prisma schema at ${schemaPath}: ${error.message}`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Extract collection names from Prisma schema content
|
|
62
|
+
*
|
|
63
|
+
* Parses the schema content to find:
|
|
64
|
+
* 1. All model definitions
|
|
65
|
+
* 2. Their @@map() directives (if present)
|
|
66
|
+
* 3. Falls back to model name if no @@map()
|
|
67
|
+
*
|
|
68
|
+
* @param {string} schemaContent - Content of schema.prisma file
|
|
69
|
+
* @returns {string[]} Array of collection names
|
|
70
|
+
* @private
|
|
71
|
+
*/
|
|
72
|
+
function extractCollectionNames(schemaContent) {
|
|
73
|
+
const collections = [];
|
|
74
|
+
|
|
75
|
+
// Match model blocks: "model ModelName { ... }"
|
|
76
|
+
// Using non-greedy match to handle multiple models
|
|
77
|
+
const modelRegex = /model\s+(\w+)\s*\{([^}]+)\}/g;
|
|
78
|
+
|
|
79
|
+
let match;
|
|
80
|
+
while ((match = modelRegex.exec(schemaContent)) !== null) {
|
|
81
|
+
const modelName = match[1];
|
|
82
|
+
const modelBody = match[2];
|
|
83
|
+
|
|
84
|
+
// Look for @@map("CollectionName") directive
|
|
85
|
+
const mapMatch = modelBody.match(/@@map\s*\(\s*["'](\w+)["']\s*\)/);
|
|
86
|
+
|
|
87
|
+
if (mapMatch) {
|
|
88
|
+
// Use mapped collection name
|
|
89
|
+
collections.push(mapMatch[1]);
|
|
90
|
+
} else {
|
|
91
|
+
// Use model name as collection name (Prisma default)
|
|
92
|
+
collections.push(modelName);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return collections;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Find Prisma MongoDB schema file
|
|
101
|
+
*
|
|
102
|
+
* Searches for the schema.prisma file in common locations:
|
|
103
|
+
* 1. prisma-mongodb/schema.prisma (Frigg convention)
|
|
104
|
+
* 2. prisma/schema.prisma (Prisma default)
|
|
105
|
+
* 3. schema.prisma (root)
|
|
106
|
+
*
|
|
107
|
+
* @param {string} startDir - Directory to start searching from
|
|
108
|
+
* @returns {string|null} Path to schema file, or null if not found
|
|
109
|
+
*/
|
|
110
|
+
function findMongoDBSchemaFile(startDir = __dirname) {
|
|
111
|
+
// Start from database directory and work up
|
|
112
|
+
const baseDir = path.resolve(startDir, '../..');
|
|
113
|
+
|
|
114
|
+
const searchPaths = [
|
|
115
|
+
path.join(baseDir, 'prisma-mongodb', 'schema.prisma'),
|
|
116
|
+
path.join(baseDir, 'prisma', 'schema.prisma'),
|
|
117
|
+
path.join(baseDir, 'schema.prisma'),
|
|
118
|
+
];
|
|
119
|
+
|
|
120
|
+
for (const schemaPath of searchPaths) {
|
|
121
|
+
if (fs.existsSync(schemaPath)) {
|
|
122
|
+
return schemaPath;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Get MongoDB collection names from Prisma schema
|
|
131
|
+
*
|
|
132
|
+
* Convenience function that finds and parses the schema automatically.
|
|
133
|
+
*
|
|
134
|
+
* @returns {Promise<string[]>} Array of collection names
|
|
135
|
+
* @throws {Error} If schema file not found or parsing fails
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* ```js
|
|
139
|
+
* const collections = await getCollectionsFromSchema();
|
|
140
|
+
* await ensureCollectionsExist(collections);
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
async function getCollectionsFromSchema() {
|
|
144
|
+
const schemaPath = findMongoDBSchemaFile();
|
|
145
|
+
|
|
146
|
+
if (!schemaPath) {
|
|
147
|
+
throw new Error(
|
|
148
|
+
'Could not find Prisma MongoDB schema file. ' +
|
|
149
|
+
'Searched: prisma-mongodb/schema.prisma, prisma/schema.prisma, schema.prisma'
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return await parseCollectionsFromSchema(schemaPath);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Synchronous version of getCollectionsFromSchema
|
|
158
|
+
*
|
|
159
|
+
* @returns {string[]} Array of collection names
|
|
160
|
+
* @throws {Error} If schema file not found or parsing fails
|
|
161
|
+
*/
|
|
162
|
+
function getCollectionsFromSchemaSync() {
|
|
163
|
+
const schemaPath = findMongoDBSchemaFile();
|
|
164
|
+
|
|
165
|
+
if (!schemaPath) {
|
|
166
|
+
throw new Error(
|
|
167
|
+
'Could not find Prisma MongoDB schema file. ' +
|
|
168
|
+
'Searched: prisma-mongodb/schema.prisma, prisma/schema.prisma, schema.prisma'
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return parseCollectionsFromSchemaSync(schemaPath);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
module.exports = {
|
|
176
|
+
parseCollectionsFromSchema,
|
|
177
|
+
parseCollectionsFromSchemaSync,
|
|
178
|
+
extractCollectionNames,
|
|
179
|
+
findMongoDBSchemaFile,
|
|
180
|
+
getCollectionsFromSchema,
|
|
181
|
+
getCollectionsFromSchemaSync,
|
|
182
|
+
};
|
package/encrypt/Cryptor.js
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
const crypto = require('crypto');
|
|
20
|
-
const
|
|
20
|
+
const { KMSClient, GenerateDataKeyCommand, DecryptCommand } = require('@aws-sdk/client-kms');
|
|
21
21
|
const aes = require('./aes');
|
|
22
22
|
|
|
23
23
|
class Cryptor {
|
|
@@ -27,16 +27,15 @@ class Cryptor {
|
|
|
27
27
|
|
|
28
28
|
async generateDataKey() {
|
|
29
29
|
if (this.shouldUseAws) {
|
|
30
|
-
const kmsClient = new
|
|
31
|
-
const
|
|
32
|
-
.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
.promise();
|
|
30
|
+
const kmsClient = new KMSClient({});
|
|
31
|
+
const command = new GenerateDataKeyCommand({
|
|
32
|
+
KeyId: process.env.KMS_KEY_ARN,
|
|
33
|
+
KeySpec: 'AES_256',
|
|
34
|
+
});
|
|
35
|
+
const dataKey = await kmsClient.send(command);
|
|
37
36
|
|
|
38
37
|
const keyId = Buffer.from(dataKey.KeyId).toString('base64');
|
|
39
|
-
const encryptedKey = dataKey.CiphertextBlob.toString('base64');
|
|
38
|
+
const encryptedKey = Buffer.from(dataKey.CiphertextBlob).toString('base64');
|
|
40
39
|
const plaintext = dataKey.Plaintext;
|
|
41
40
|
return { keyId, encryptedKey, plaintext };
|
|
42
41
|
}
|
|
@@ -70,13 +69,12 @@ class Cryptor {
|
|
|
70
69
|
|
|
71
70
|
async decryptDataKey(keyId, encryptedKey) {
|
|
72
71
|
if (this.shouldUseAws) {
|
|
73
|
-
const kmsClient = new
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
.promise();
|
|
72
|
+
const kmsClient = new KMSClient({});
|
|
73
|
+
const command = new DecryptCommand({
|
|
74
|
+
KeyId: keyId,
|
|
75
|
+
CiphertextBlob: encryptedKey,
|
|
76
|
+
});
|
|
77
|
+
const dataKey = await kmsClient.send(command);
|
|
80
78
|
|
|
81
79
|
return dataKey.Plaintext;
|
|
82
80
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./index"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./index"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./default"
|