@rapidd/build 1.0.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/LICENSE +21 -0
- package/README.md +86 -0
- package/bin/cli.js +30 -0
- package/index.js +11 -0
- package/package.json +63 -0
- package/src/commands/build.js +448 -0
- package/src/generators/modelGenerator.js +168 -0
- package/src/generators/relationshipsGenerator.js +186 -0
- package/src/generators/rlsGenerator.js +334 -0
- package/src/generators/rlsGeneratorV2.js +381 -0
- package/src/generators/routeGenerator.js +127 -0
- package/src/parsers/advancedRLSConverter.js +305 -0
- package/src/parsers/autoRLSConverter.js +322 -0
- package/src/parsers/datasourceParser.js +73 -0
- package/src/parsers/deepSQLAnalyzer.js +540 -0
- package/src/parsers/dynamicRLSConverter.js +353 -0
- package/src/parsers/enhancedRLSConverter.js +181 -0
- package/src/parsers/functionAnalyzer.js +302 -0
- package/src/parsers/postgresRLSConverter.js +192 -0
- package/src/parsers/prismaFilterBuilder.js +422 -0
- package/src/parsers/prismaParser.js +245 -0
- package/src/parsers/sqlToJsConverter.js +611 -0
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { Client } = require('pg');
|
|
4
|
+
const { createConverter } = require('../parsers/autoRLSConverter');
|
|
5
|
+
const { createEnhancedConverter } = require('../parsers/enhancedRLSConverter');
|
|
6
|
+
const { analyzeFunctions, generateMappingConfig } = require('../parsers/functionAnalyzer');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Auto-detect user table name (case-insensitive search for user/users)
|
|
10
|
+
*/
|
|
11
|
+
function detectUserTable(models, userTableOption) {
|
|
12
|
+
if (userTableOption) {
|
|
13
|
+
return userTableOption;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const modelNames = Object.keys(models);
|
|
17
|
+
const userTables = modelNames.filter(name =>
|
|
18
|
+
name.toLowerCase() === 'user' || name.toLowerCase() === 'users'
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
if (userTables.length === 0) {
|
|
22
|
+
throw new Error('No user table found (user/users). Please specify --user-table option.');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (userTables.length > 1) {
|
|
26
|
+
throw new Error(`Multiple user tables found: ${userTables.join(', ')}. Please specify --user-table option.`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return userTables[0];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Extract RLS policies from PostgreSQL
|
|
34
|
+
*/
|
|
35
|
+
async function extractPostgreSQLPolicies(databaseUrl, modelNames) {
|
|
36
|
+
const client = new Client({ connectionString: databaseUrl });
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
await client.connect();
|
|
40
|
+
|
|
41
|
+
const policies = {};
|
|
42
|
+
|
|
43
|
+
// Initialize all models with empty policies
|
|
44
|
+
for (const modelName of modelNames) {
|
|
45
|
+
policies[modelName] = [];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Query all RLS policies from pg_policies
|
|
49
|
+
const result = await client.query(`
|
|
50
|
+
SELECT
|
|
51
|
+
tablename,
|
|
52
|
+
policyname,
|
|
53
|
+
permissive,
|
|
54
|
+
roles,
|
|
55
|
+
cmd,
|
|
56
|
+
qual,
|
|
57
|
+
with_check
|
|
58
|
+
FROM pg_policies
|
|
59
|
+
WHERE schemaname = 'public'
|
|
60
|
+
ORDER BY tablename, policyname
|
|
61
|
+
`);
|
|
62
|
+
|
|
63
|
+
// Group policies by table
|
|
64
|
+
for (const row of result.rows) {
|
|
65
|
+
const tableName = row.tablename;
|
|
66
|
+
if (policies[tableName] !== undefined) {
|
|
67
|
+
policies[tableName].push({
|
|
68
|
+
name: row.policyname,
|
|
69
|
+
permissive: row.permissive === 'PERMISSIVE',
|
|
70
|
+
roles: row.roles,
|
|
71
|
+
command: row.cmd,
|
|
72
|
+
using: row.qual,
|
|
73
|
+
withCheck: row.with_check
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
await client.end();
|
|
79
|
+
return policies;
|
|
80
|
+
|
|
81
|
+
} catch (error) {
|
|
82
|
+
try {
|
|
83
|
+
await client.end();
|
|
84
|
+
} catch (e) {}
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Generate RLS functions for a single model from PostgreSQL policies
|
|
91
|
+
*/
|
|
92
|
+
function generateModelRLS(modelName, policies, converter) {
|
|
93
|
+
const hasPolicies = policies && policies.length > 0;
|
|
94
|
+
|
|
95
|
+
if (!hasPolicies) {
|
|
96
|
+
// No RLS policies - generate permissive access
|
|
97
|
+
return ` ${modelName}: {
|
|
98
|
+
canCreate: (user) => true,
|
|
99
|
+
hasAccess: (data, user) => true,
|
|
100
|
+
getAccessFilter: (user) => ({}),
|
|
101
|
+
getUpdateFilter: (user) => ({}),
|
|
102
|
+
getDeleteFilter: (user) => ({}),
|
|
103
|
+
getOmitFields: (user) => []
|
|
104
|
+
}`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Find policies by command type
|
|
108
|
+
const selectPolicies = policies.filter(p => p.command === 'SELECT' || p.command === 'ALL');
|
|
109
|
+
const insertPolicies = policies.filter(p => p.command === 'INSERT' || p.command === 'ALL');
|
|
110
|
+
const updatePolicies = policies.filter(p => p.command === 'UPDATE' || p.command === 'ALL');
|
|
111
|
+
const deletePolicies = policies.filter(p => p.command === 'DELETE' || p.command === 'ALL');
|
|
112
|
+
|
|
113
|
+
// Generate each function
|
|
114
|
+
const canCreateCode = generateFunction(insertPolicies, 'withCheck', converter, modelName);
|
|
115
|
+
const hasAccessCode = generateFunction(selectPolicies, 'using', converter, modelName);
|
|
116
|
+
const accessFilterCode = generateFilter(selectPolicies, 'using', converter, modelName);
|
|
117
|
+
let updateFilterCode = generateFilter(updatePolicies, 'using', converter, modelName);
|
|
118
|
+
let deleteFilterCode = generateFilter(deletePolicies, 'using', converter, modelName);
|
|
119
|
+
|
|
120
|
+
// If update/delete filters are empty/false but access filter is not, copy access filter
|
|
121
|
+
if ((updateFilterCode === 'return false;' || updateFilterCode === 'return {};') &&
|
|
122
|
+
accessFilterCode !== 'return false;' && accessFilterCode !== 'return {};') {
|
|
123
|
+
updateFilterCode = accessFilterCode;
|
|
124
|
+
}
|
|
125
|
+
if ((deleteFilterCode === 'return false;' || deleteFilterCode === 'return {};') &&
|
|
126
|
+
accessFilterCode !== 'return false;' && accessFilterCode !== 'return {};') {
|
|
127
|
+
deleteFilterCode = accessFilterCode;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return ` ${modelName}: {
|
|
131
|
+
canCreate: (user) => {
|
|
132
|
+
${canCreateCode}
|
|
133
|
+
},
|
|
134
|
+
hasAccess: (data, user) => {
|
|
135
|
+
${hasAccessCode}
|
|
136
|
+
},
|
|
137
|
+
getAccessFilter: (user) => {
|
|
138
|
+
${accessFilterCode}
|
|
139
|
+
},
|
|
140
|
+
getUpdateFilter: (user) => {
|
|
141
|
+
${updateFilterCode}
|
|
142
|
+
},
|
|
143
|
+
getDeleteFilter: (user) => {
|
|
144
|
+
${deleteFilterCode}
|
|
145
|
+
},
|
|
146
|
+
getOmitFields: (user) => []
|
|
147
|
+
}`;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Generate JavaScript function from policies
|
|
152
|
+
*/
|
|
153
|
+
function generateFunction(policies, expressionField, converter, modelName) {
|
|
154
|
+
if (policies.length === 0) {
|
|
155
|
+
return 'return true;';
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const conditions = [];
|
|
159
|
+
|
|
160
|
+
for (const policy of policies) {
|
|
161
|
+
const expr = expressionField === 'withCheck'
|
|
162
|
+
? (policy.withCheck || policy.using)
|
|
163
|
+
: policy[expressionField];
|
|
164
|
+
|
|
165
|
+
if (expr) {
|
|
166
|
+
try {
|
|
167
|
+
const jsExpr = converter.convertToJavaScript(expr, 'data', 'user', modelName);
|
|
168
|
+
conditions.push(jsExpr);
|
|
169
|
+
} catch (e) {
|
|
170
|
+
conditions.push(`true /* Error: ${e.message} */`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (conditions.length === 0) {
|
|
176
|
+
return 'return true;';
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Policies are OR'd together (any policy allows)
|
|
180
|
+
return `return ${conditions.join(' || ')};`;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Generate Prisma filter function
|
|
185
|
+
*/
|
|
186
|
+
function generateFilter(policies, expressionField, converter, modelName) {
|
|
187
|
+
if (policies.length === 0) {
|
|
188
|
+
return 'return false;';
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const filtersWithRoles = [];
|
|
192
|
+
|
|
193
|
+
for (const policy of policies) {
|
|
194
|
+
const expr = policy[expressionField];
|
|
195
|
+
if (expr) {
|
|
196
|
+
try {
|
|
197
|
+
const prismaFilter = converter.convertToPrismaFilter(expr, 'user', modelName);
|
|
198
|
+
const analysis = converter.analyzer ? converter.analyzer.analyzeSQLForFilters(expr) : null;
|
|
199
|
+
|
|
200
|
+
// Track role conditions and data filters separately
|
|
201
|
+
const roleConditions = analysis?.conditions?.filter(c =>
|
|
202
|
+
c.type === 'role_any' || c.type === 'role_equal'
|
|
203
|
+
) || [];
|
|
204
|
+
|
|
205
|
+
filtersWithRoles.push({
|
|
206
|
+
filter: prismaFilter,
|
|
207
|
+
roleConditions,
|
|
208
|
+
hasDataFilter: prismaFilter !== '{}'
|
|
209
|
+
});
|
|
210
|
+
} catch (e) {
|
|
211
|
+
// On error, skip filter
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (filtersWithRoles.length === 0) {
|
|
217
|
+
return 'return false;';
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Build conditional filter logic
|
|
221
|
+
return buildConditionalFilter(filtersWithRoles);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Build conditional filter with role checks
|
|
226
|
+
*/
|
|
227
|
+
function buildConditionalFilter(filtersWithRoles) {
|
|
228
|
+
const roleOnlyFilters = [];
|
|
229
|
+
const dataFilters = [];
|
|
230
|
+
|
|
231
|
+
for (const item of filtersWithRoles) {
|
|
232
|
+
if (item.roleConditions.length > 0 && !item.hasDataFilter) {
|
|
233
|
+
// Pure role check - return {} if role matches
|
|
234
|
+
roleOnlyFilters.push(...item.roleConditions);
|
|
235
|
+
} else if (item.roleConditions.length > 0 && item.hasDataFilter) {
|
|
236
|
+
// Has both role and data filter - already handled with if statement
|
|
237
|
+
if (item.filter.includes('if (')) {
|
|
238
|
+
return item.filter;
|
|
239
|
+
}
|
|
240
|
+
dataFilters.push(item.filter);
|
|
241
|
+
} else if (item.hasDataFilter) {
|
|
242
|
+
// Data filter only
|
|
243
|
+
dataFilters.push(item.filter);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Generate conditional code
|
|
248
|
+
const conditions = [];
|
|
249
|
+
|
|
250
|
+
// Collect all roles that grant full access
|
|
251
|
+
const rolesWithFullAccess = new Set();
|
|
252
|
+
for (const roleCond of roleOnlyFilters) {
|
|
253
|
+
if (roleCond.type === 'role_any') {
|
|
254
|
+
roleCond.roles.forEach(r => rolesWithFullAccess.add(r));
|
|
255
|
+
} else if (roleCond.type === 'role_equal') {
|
|
256
|
+
rolesWithFullAccess.add(roleCond.role);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Add single consolidated role check if needed
|
|
261
|
+
if (rolesWithFullAccess.size > 0) {
|
|
262
|
+
const roleArray = Array.from(rolesWithFullAccess);
|
|
263
|
+
if (roleArray.length === 1) {
|
|
264
|
+
conditions.push(`if (user?.role === '${roleArray[0]}') { return {}; }`);
|
|
265
|
+
} else {
|
|
266
|
+
conditions.push(`if ([${roleArray.map(r => `'${r}'`).join(', ')}].includes(user?.role)) { return {}; }`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Deduplicate data filters
|
|
271
|
+
const uniqueDataFilters = [...new Set(dataFilters)];
|
|
272
|
+
|
|
273
|
+
// Add final return with data filters
|
|
274
|
+
if (uniqueDataFilters.length === 0) {
|
|
275
|
+
conditions.push('return false;');
|
|
276
|
+
} else if (uniqueDataFilters.length === 1) {
|
|
277
|
+
conditions.push(`return ${uniqueDataFilters[0]};`);
|
|
278
|
+
} else {
|
|
279
|
+
conditions.push(`return { OR: [${uniqueDataFilters.join(', ')}] };`);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return conditions.join(' ');
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Generate complete rls.js file
|
|
287
|
+
*/
|
|
288
|
+
async function generateRLS(models, outputPath, databaseUrl, isPostgreSQL, userTableOption, relationships = {}) {
|
|
289
|
+
const userTable = detectUserTable(models, userTableOption);
|
|
290
|
+
const modelNames = Object.keys(models);
|
|
291
|
+
|
|
292
|
+
let policies = {};
|
|
293
|
+
const timestamp = new Date().toISOString();
|
|
294
|
+
|
|
295
|
+
let rlsCode = `const rls = {\n model: {},\n lastUpdateDate: '${timestamp}'\n};\n\n`;
|
|
296
|
+
|
|
297
|
+
// Create enhanced converter with analyzed functions, models, and relationships
|
|
298
|
+
let converter = createEnhancedConverter({}, {}, models, relationships);
|
|
299
|
+
|
|
300
|
+
if (isPostgreSQL && databaseUrl) {
|
|
301
|
+
console.log('PostgreSQL detected - analyzing database...');
|
|
302
|
+
|
|
303
|
+
// Step 1: Analyze functions
|
|
304
|
+
try {
|
|
305
|
+
const functionAnalysis = await analyzeFunctions(databaseUrl);
|
|
306
|
+
console.log(`✓ Analyzed ${Object.keys(functionAnalysis.functionMappings).length} PostgreSQL functions`);
|
|
307
|
+
|
|
308
|
+
// Create enhanced converter with analyzed mappings, models, and relationships
|
|
309
|
+
converter = createEnhancedConverter(
|
|
310
|
+
functionAnalysis.functionMappings,
|
|
311
|
+
functionAnalysis.sessionVariables,
|
|
312
|
+
models,
|
|
313
|
+
relationships
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
// Save function analysis for debugging
|
|
317
|
+
const configPath = path.join(path.dirname(outputPath), 'rls-mappings.json');
|
|
318
|
+
const mappingConfig = generateMappingConfig(functionAnalysis);
|
|
319
|
+
fs.writeFileSync(configPath, JSON.stringify(mappingConfig, null, 2));
|
|
320
|
+
console.log(`✓ Function mappings saved to ${configPath}`);
|
|
321
|
+
|
|
322
|
+
// Also add user context requirements as a comment in rls.js
|
|
323
|
+
if (Object.keys(functionAnalysis.userContextRequirements).length > 0) {
|
|
324
|
+
rlsCode = `/**
|
|
325
|
+
* User Context Requirements:
|
|
326
|
+
* The user object should contain:
|
|
327
|
+
${Object.entries(functionAnalysis.userContextRequirements)
|
|
328
|
+
.map(([field, req]) => ` * - ${field}: ${typeof req === 'object' ? req.description : 'required'}`)
|
|
329
|
+
.join('\n')}
|
|
330
|
+
*/
|
|
331
|
+
|
|
332
|
+
` + rlsCode;
|
|
333
|
+
}
|
|
334
|
+
} catch (error) {
|
|
335
|
+
console.warn(`⚠ Could not analyze functions: ${error.message}`);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Step 2: Extract policies
|
|
339
|
+
try {
|
|
340
|
+
policies = await extractPostgreSQLPolicies(databaseUrl, modelNames);
|
|
341
|
+
const totalPolicies = Object.values(policies).reduce((sum, p) => sum + p.length, 0);
|
|
342
|
+
console.log(`✓ Extracted ${totalPolicies} RLS policies from PostgreSQL`);
|
|
343
|
+
} catch (error) {
|
|
344
|
+
console.warn(`⚠ Failed to extract PostgreSQL RLS: ${error.message}`);
|
|
345
|
+
console.log('Generating permissive RLS for all models...');
|
|
346
|
+
for (const modelName of modelNames) {
|
|
347
|
+
policies[modelName] = [];
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
} else {
|
|
351
|
+
if (!isPostgreSQL) {
|
|
352
|
+
console.log('Non-PostgreSQL database detected - RLS not supported');
|
|
353
|
+
}
|
|
354
|
+
console.log('Generating permissive RLS for all models...');
|
|
355
|
+
for (const modelName of modelNames) {
|
|
356
|
+
policies[modelName] = [];
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Generate RLS for each model
|
|
361
|
+
rlsCode += 'rls.model = {\n';
|
|
362
|
+
const modelRLSCode = modelNames.map(modelName => {
|
|
363
|
+
return generateModelRLS(modelName, policies[modelName], converter);
|
|
364
|
+
});
|
|
365
|
+
rlsCode += modelRLSCode.join(',\n');
|
|
366
|
+
rlsCode += '\n};\n\n';
|
|
367
|
+
rlsCode += 'module.exports = rls;\n';
|
|
368
|
+
|
|
369
|
+
// Ensure output directory exists
|
|
370
|
+
const outputDir = path.dirname(outputPath);
|
|
371
|
+
if (!fs.existsSync(outputDir)) {
|
|
372
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
fs.writeFileSync(outputPath, rlsCode);
|
|
376
|
+
console.log('✓ Generated rls.js with dynamic function mappings');
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
module.exports = {
|
|
380
|
+
generateRLS
|
|
381
|
+
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Generate Express route for a single model
|
|
6
|
+
* @param {string} modelName - Name of the model
|
|
7
|
+
* @returns {string} - Generated route code
|
|
8
|
+
*/
|
|
9
|
+
function generateRouteFile(modelName) {
|
|
10
|
+
const modelNameLower = modelName.toLowerCase();
|
|
11
|
+
const className = modelName.charAt(0).toUpperCase() + modelName.slice(1);
|
|
12
|
+
|
|
13
|
+
return `const router = require('express').Router();
|
|
14
|
+
const {Api, ErrorResponse} = require('../../../src/Api');
|
|
15
|
+
const {${className}, QueryBuilder, prisma} = require('../../../src/Model/${className}');
|
|
16
|
+
|
|
17
|
+
router.all('*', async (req, res, next) => {
|
|
18
|
+
if(req.session && req.user){
|
|
19
|
+
req.${className} = new ${className}({'user': req.user});
|
|
20
|
+
next();
|
|
21
|
+
}
|
|
22
|
+
else{
|
|
23
|
+
res.status(401).send({'status_code': res.statusCode, 'message': "No valid session"});
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// GET ALL
|
|
28
|
+
router.get('/', async function(req, res) {
|
|
29
|
+
let response, status_code = 200;
|
|
30
|
+
try {
|
|
31
|
+
const { q = {}, include = "", limit = 25, offset = 0, sortBy = "id", sortOrder = "asc" } = req.query;
|
|
32
|
+
|
|
33
|
+
const _data = req.${className}.getMany(q, include, limit, offset, sortBy, sortOrder);
|
|
34
|
+
const _count = req.${className}.count(q);
|
|
35
|
+
const [data, count] = await Promise.all([_data, _count]);
|
|
36
|
+
|
|
37
|
+
response = Api.getListResponseBody(data, {'take': req.${className}.take(Number(limit)), 'skip': req.${className}.skip(Number(offset)), 'total': count});
|
|
38
|
+
}
|
|
39
|
+
catch(error){
|
|
40
|
+
response = QueryBuilder.errorHandler(error);
|
|
41
|
+
status_code = response.status_code;
|
|
42
|
+
}
|
|
43
|
+
res.status(status_code).send(response);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// GET BY ID
|
|
47
|
+
router.get('/:id', async function(req, res) {
|
|
48
|
+
let response, status_code = 200;
|
|
49
|
+
try{
|
|
50
|
+
const { include = ""} = req.query;
|
|
51
|
+
response = await req.${className}.get(req.params.id, include);
|
|
52
|
+
}
|
|
53
|
+
catch(error){
|
|
54
|
+
response = QueryBuilder.errorHandler(error);
|
|
55
|
+
status_code = response.status_code;
|
|
56
|
+
}
|
|
57
|
+
res.status(status_code).send(response);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// CREATE
|
|
61
|
+
router.post('/', async function(req, res) {
|
|
62
|
+
let response, status_code = 201, payload = req.body;
|
|
63
|
+
try{
|
|
64
|
+
response = await req.${className}.create(payload);
|
|
65
|
+
}
|
|
66
|
+
catch(error){
|
|
67
|
+
response = QueryBuilder.errorHandler(error, payload);
|
|
68
|
+
status_code = response.status_code;
|
|
69
|
+
}
|
|
70
|
+
res.status(status_code).send(response);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// UPDATE
|
|
74
|
+
router.patch('/:id', async function(req, res) {
|
|
75
|
+
let response, status_code = 200, payload = req.body;
|
|
76
|
+
try{
|
|
77
|
+
response = await req.${className}.update(req.params.id, payload);
|
|
78
|
+
}
|
|
79
|
+
catch(error){
|
|
80
|
+
response = QueryBuilder.errorHandler(error, payload);
|
|
81
|
+
status_code = response.status_code;
|
|
82
|
+
}
|
|
83
|
+
res.status(status_code).send(response);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// DELETE
|
|
87
|
+
router.delete('/:id', async (req, res)=>{
|
|
88
|
+
let response, status_code = 200;
|
|
89
|
+
try{
|
|
90
|
+
await req.${className}.delete(req.params.id);
|
|
91
|
+
response = {'status_code': status_code, 'message': "${className} successfully deleted"}
|
|
92
|
+
}
|
|
93
|
+
catch(error){
|
|
94
|
+
response = QueryBuilder.errorHandler(error);
|
|
95
|
+
status_code = response.status_code;
|
|
96
|
+
}
|
|
97
|
+
res.status(status_code).send(response);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
module.exports = router;
|
|
101
|
+
`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Generate all route files
|
|
106
|
+
* @param {Object} models - Models object from parser
|
|
107
|
+
* @param {string} routesDir - Directory to output route files
|
|
108
|
+
*/
|
|
109
|
+
function generateAllRoutes(models, routesDir) {
|
|
110
|
+
// Create routes directory if it doesn't exist
|
|
111
|
+
if (!fs.existsSync(routesDir)) {
|
|
112
|
+
fs.mkdirSync(routesDir, { recursive: true });
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Generate individual route files
|
|
116
|
+
for (const modelName of Object.keys(models)) {
|
|
117
|
+
const routeCode = generateRouteFile(modelName);
|
|
118
|
+
const routePath = path.join(routesDir, `${modelName.toLowerCase()}.js`);
|
|
119
|
+
fs.writeFileSync(routePath, routeCode);
|
|
120
|
+
console.log(`Generated route: ${modelName.toLowerCase()}.js`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
module.exports = {
|
|
125
|
+
generateAllRoutes,
|
|
126
|
+
generateRouteFile
|
|
127
|
+
};
|