@friggframework/devtools 2.0.0--canary.454.e2a280d.0 → 2.0.0--canary.458.c150d9a.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/frigg-cli/__tests__/unit/commands/db-setup.test.js +1 -1
- package/frigg-cli/__tests__/unit/utils/prisma-runner.test.js +486 -0
- package/frigg-cli/build-command/index.js +1 -12
- package/frigg-cli/db-setup-command/index.js +1 -8
- package/frigg-cli/index.js +0 -1
- package/frigg-cli/utils/database-validator.js +1 -4
- package/frigg-cli/utils/prisma-runner.js +280 -0
- package/infrastructure/README.md +0 -51
- package/infrastructure/aws-discovery.js +2 -504
- package/infrastructure/aws-discovery.test.js +1 -447
- package/infrastructure/serverless-template.js +21 -459
- package/infrastructure/serverless-template.test.js +0 -91
- package/management-ui/src/App.jsx +1 -85
- package/management-ui/src/hooks/useFrigg.jsx +1 -215
- package/package.json +6 -8
- package/infrastructure/POSTGRES-CONFIGURATION.md +0 -645
- package/infrastructure/__tests__/postgres-config.test.js +0 -914
- package/infrastructure/scripts/build-prisma-layer.js +0 -394
|
@@ -0,0 +1,280 @@
|
|
|
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. Local node_modules (where @friggframework/core is installed - production scenario)
|
|
22
|
+
// 2. Parent node_modules (workspace/monorepo setup)
|
|
23
|
+
const possiblePaths = [
|
|
24
|
+
// Check where Frigg is installed via npm (production scenario)
|
|
25
|
+
path.join(projectRoot, 'node_modules', '@friggframework', 'core', `prisma-${dbType}`, 'schema.prisma'),
|
|
26
|
+
path.join(projectRoot, '..', 'node_modules', '@friggframework', 'core', `prisma-${dbType}`, 'schema.prisma')
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
for (const schemaPath of possiblePaths) {
|
|
30
|
+
if (fs.existsSync(schemaPath)) {
|
|
31
|
+
return schemaPath;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// If not found in any location, throw error
|
|
36
|
+
throw new Error(
|
|
37
|
+
`Prisma schema not found at:\n${possiblePaths.join('\n')}\n\n` +
|
|
38
|
+
'Ensure @friggframework/core is installed.'
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Runs prisma generate for the specified database type
|
|
44
|
+
* @param {'mongodb'|'postgresql'} dbType - Database type
|
|
45
|
+
* @param {boolean} verbose - Enable verbose output
|
|
46
|
+
* @returns {Promise<Object>} { success: boolean, output?: string, error?: string }
|
|
47
|
+
*/
|
|
48
|
+
async function runPrismaGenerate(dbType, verbose = false) {
|
|
49
|
+
try {
|
|
50
|
+
const schemaPath = getPrismaSchemaPath(dbType);
|
|
51
|
+
|
|
52
|
+
if (verbose) {
|
|
53
|
+
console.log(chalk.gray(`Running: npx prisma generate --schema=${schemaPath}`));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const output = execSync(
|
|
57
|
+
`npx prisma generate --schema=${schemaPath}`,
|
|
58
|
+
{
|
|
59
|
+
encoding: 'utf8',
|
|
60
|
+
stdio: verbose ? 'inherit' : 'pipe',
|
|
61
|
+
env: {
|
|
62
|
+
...process.env,
|
|
63
|
+
// Suppress Prisma telemetry prompts
|
|
64
|
+
PRISMA_HIDE_UPDATE_MESSAGE: '1'
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
success: true,
|
|
71
|
+
output: verbose ? 'Generated successfully' : output
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
} catch (error) {
|
|
75
|
+
return {
|
|
76
|
+
success: false,
|
|
77
|
+
error: error.message,
|
|
78
|
+
output: error.stdout?.toString() || error.stderr?.toString()
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Checks database migration status
|
|
85
|
+
* @param {'mongodb'|'postgresql'} dbType - Database type
|
|
86
|
+
* @returns {Promise<Object>} { upToDate: boolean, pendingMigrations?: number, error?: string }
|
|
87
|
+
*/
|
|
88
|
+
async function checkDatabaseState(dbType) {
|
|
89
|
+
try {
|
|
90
|
+
// Only applicable for PostgreSQL (MongoDB uses db push)
|
|
91
|
+
if (dbType !== 'postgresql') {
|
|
92
|
+
return { upToDate: true };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const schemaPath = getPrismaSchemaPath(dbType);
|
|
96
|
+
|
|
97
|
+
const output = execSync(
|
|
98
|
+
`npx prisma migrate status --schema=${schemaPath}`,
|
|
99
|
+
{
|
|
100
|
+
encoding: 'utf8',
|
|
101
|
+
stdio: 'pipe',
|
|
102
|
+
env: {
|
|
103
|
+
...process.env,
|
|
104
|
+
PRISMA_HIDE_UPDATE_MESSAGE: '1'
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
if (output.includes('Database schema is up to date')) {
|
|
110
|
+
return { upToDate: true };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Parse pending migrations count
|
|
114
|
+
const pendingMatch = output.match(/(\d+) migration/);
|
|
115
|
+
const pendingMigrations = pendingMatch ? parseInt(pendingMatch[1]) : 0;
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
upToDate: false,
|
|
119
|
+
pendingMigrations
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
} catch (error) {
|
|
123
|
+
// If migrate status fails, database might not be initialized
|
|
124
|
+
return {
|
|
125
|
+
upToDate: false,
|
|
126
|
+
error: error.message
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Runs Prisma migrate for PostgreSQL
|
|
133
|
+
* @param {'dev'|'deploy'} command - Migration command (dev or deploy)
|
|
134
|
+
* @param {boolean} verbose - Enable verbose output
|
|
135
|
+
* @returns {Promise<Object>} { success: boolean, output?: string, error?: string }
|
|
136
|
+
*/
|
|
137
|
+
async function runPrismaMigrate(command = 'dev', verbose = false) {
|
|
138
|
+
return new Promise((resolve) => {
|
|
139
|
+
try {
|
|
140
|
+
const schemaPath = getPrismaSchemaPath('postgresql');
|
|
141
|
+
|
|
142
|
+
const args = [
|
|
143
|
+
'prisma',
|
|
144
|
+
'migrate',
|
|
145
|
+
command,
|
|
146
|
+
'--schema',
|
|
147
|
+
schemaPath
|
|
148
|
+
];
|
|
149
|
+
|
|
150
|
+
if (verbose) {
|
|
151
|
+
console.log(chalk.gray(`Running: npx ${args.join(' ')}`));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const proc = spawn('npx', args, {
|
|
155
|
+
stdio: 'inherit',
|
|
156
|
+
env: {
|
|
157
|
+
...process.env,
|
|
158
|
+
PRISMA_HIDE_UPDATE_MESSAGE: '1'
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
proc.on('error', (error) => {
|
|
163
|
+
resolve({
|
|
164
|
+
success: false,
|
|
165
|
+
error: error.message
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
proc.on('close', (code) => {
|
|
170
|
+
if (code === 0) {
|
|
171
|
+
resolve({
|
|
172
|
+
success: true,
|
|
173
|
+
output: 'Migration completed successfully'
|
|
174
|
+
});
|
|
175
|
+
} else {
|
|
176
|
+
resolve({
|
|
177
|
+
success: false,
|
|
178
|
+
error: `Migration process exited with code ${code}`
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
} catch (error) {
|
|
184
|
+
resolve({
|
|
185
|
+
success: false,
|
|
186
|
+
error: error.message
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Runs Prisma db push for MongoDB
|
|
194
|
+
* Interactive - will prompt user if data loss detected
|
|
195
|
+
* @param {boolean} verbose - Enable verbose output
|
|
196
|
+
* @returns {Promise<Object>} { success: boolean, output?: string, error?: string }
|
|
197
|
+
*/
|
|
198
|
+
async function runPrismaDbPush(verbose = false) {
|
|
199
|
+
return new Promise((resolve) => {
|
|
200
|
+
try {
|
|
201
|
+
const schemaPath = getPrismaSchemaPath('mongodb');
|
|
202
|
+
|
|
203
|
+
const args = [
|
|
204
|
+
'prisma',
|
|
205
|
+
'db',
|
|
206
|
+
'push',
|
|
207
|
+
'--schema',
|
|
208
|
+
schemaPath,
|
|
209
|
+
'--skip-generate' // We generate separately
|
|
210
|
+
];
|
|
211
|
+
|
|
212
|
+
if (verbose) {
|
|
213
|
+
console.log(chalk.gray(`Running: npx ${args.join(' ')}`));
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
console.log(chalk.yellow('⚠️ Interactive mode: You may be prompted if schema changes cause data loss'));
|
|
217
|
+
|
|
218
|
+
const proc = spawn('npx', args, {
|
|
219
|
+
stdio: 'inherit', // Interactive mode - user can respond to prompts
|
|
220
|
+
env: {
|
|
221
|
+
...process.env,
|
|
222
|
+
PRISMA_HIDE_UPDATE_MESSAGE: '1'
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
proc.on('error', (error) => {
|
|
227
|
+
resolve({
|
|
228
|
+
success: false,
|
|
229
|
+
error: error.message
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
proc.on('close', (code) => {
|
|
234
|
+
if (code === 0) {
|
|
235
|
+
resolve({
|
|
236
|
+
success: true,
|
|
237
|
+
output: 'Database push completed successfully'
|
|
238
|
+
});
|
|
239
|
+
} else {
|
|
240
|
+
resolve({
|
|
241
|
+
success: false,
|
|
242
|
+
error: `Database push process exited with code ${code}`
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
} catch (error) {
|
|
248
|
+
resolve({
|
|
249
|
+
success: false,
|
|
250
|
+
error: error.message
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Determines migration command based on STAGE environment variable
|
|
258
|
+
* @param {string} stage - Stage from CLI option or environment
|
|
259
|
+
* @returns {'dev'|'deploy'}
|
|
260
|
+
*/
|
|
261
|
+
function getMigrationCommand(stage) {
|
|
262
|
+
const normalizedStage = (stage || process.env.STAGE || 'development').toLowerCase();
|
|
263
|
+
|
|
264
|
+
const developmentStages = ['dev', 'local', 'test', 'development'];
|
|
265
|
+
|
|
266
|
+
if (developmentStages.includes(normalizedStage)) {
|
|
267
|
+
return 'dev';
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return 'deploy';
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
module.exports = {
|
|
274
|
+
getPrismaSchemaPath,
|
|
275
|
+
runPrismaGenerate,
|
|
276
|
+
checkDatabaseState,
|
|
277
|
+
runPrismaMigrate,
|
|
278
|
+
runPrismaDbPush,
|
|
279
|
+
getMigrationCommand
|
|
280
|
+
};
|
package/infrastructure/README.md
CHANGED
|
@@ -207,56 +207,6 @@ STAGE=production
|
|
|
207
207
|
SERVICE_NAME=my-frigg-app
|
|
208
208
|
```
|
|
209
209
|
|
|
210
|
-
## Lambda Layers
|
|
211
|
-
|
|
212
|
-
### Prisma Layer
|
|
213
|
-
|
|
214
|
-
The Frigg infrastructure uses a Lambda Layer to optimize Prisma deployment, reducing function sizes by ~60%.
|
|
215
|
-
|
|
216
|
-
**What's included:**
|
|
217
|
-
|
|
218
|
-
- `@prisma/client` - Prisma Client runtime
|
|
219
|
-
- `@prisma-mongodb/client` - MongoDB Prisma Client
|
|
220
|
-
- `@prisma-postgresql/client` - PostgreSQL Prisma Client
|
|
221
|
-
- `prisma` - Prisma CLI (for migrations)
|
|
222
|
-
|
|
223
|
-
**Benefits:**
|
|
224
|
-
|
|
225
|
-
- ✅ **Reduces function sizes**: From ~120MB → ~45MB per function (60% reduction)
|
|
226
|
-
- ✅ **Faster deployments**: Layer cached between deployments
|
|
227
|
-
- ✅ **Shared resources**: Prisma uploaded once (~70MB layer), shared by all functions
|
|
228
|
-
- ✅ **Improved cold starts**: Smaller packages = faster initialization
|
|
229
|
-
|
|
230
|
-
**Building the layer:**
|
|
231
|
-
|
|
232
|
-
```bash
|
|
233
|
-
cd packages/devtools
|
|
234
|
-
npm run build:prisma-layer
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
**Expected output:**
|
|
238
|
-
|
|
239
|
-
```
|
|
240
|
-
Building Prisma Lambda Layer...
|
|
241
|
-
✓ Layer built successfully (70MB)
|
|
242
|
-
Layer location: infrastructure/layers/prisma
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
**Automatic deployment:**
|
|
246
|
-
|
|
247
|
-
The layer is automatically deployed when you run `frigg deploy`. All Lambda functions reference the layer via CloudFormation.
|
|
248
|
-
|
|
249
|
-
**Troubleshooting:**
|
|
250
|
-
|
|
251
|
-
If you encounter "Module not found" errors after deployment:
|
|
252
|
-
|
|
253
|
-
```bash
|
|
254
|
-
# Verify layer is attached to function
|
|
255
|
-
aws lambda get-function-configuration \
|
|
256
|
-
--function-name your-app-dev-auth \
|
|
257
|
-
--query 'Layers[*].Arn'
|
|
258
|
-
```
|
|
259
|
-
|
|
260
210
|
## Usage Examples
|
|
261
211
|
|
|
262
212
|
### Basic Deployment
|
|
@@ -485,7 +435,6 @@ npm run test:debug
|
|
|
485
435
|
|
|
486
436
|
## Related Documentation
|
|
487
437
|
|
|
488
|
-
- [Lambda Layer for Prisma](./LAMBDA-LAYER-PRISMA.md) - Complete guide to Prisma Lambda Layer optimization
|
|
489
438
|
- [Phase 3 Deployment Guide](./PHASE3-DEPLOYMENT-GUIDE.md)
|
|
490
439
|
- [Testing Strategy](./README-TESTING.md)
|
|
491
440
|
- [AWS Discovery Troubleshooting](./AWS-DISCOVERY-TROUBLESHOOTING.md)
|