4bnode 3.5.2 → 4.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/README.md +139 -114
- package/add-api.js +288 -156
- package/add-crud.js +226 -0
- package/add-login.js +152 -319
- package/add-mongo-delete.js +67 -138
- package/add-mongo-insert.js +87 -204
- package/add-mongo-read.js +100 -216
- package/add-mongo-schema.js +431 -199
- package/add-mongo-update.js +89 -202
- package/add-mongo.js +125 -218
- package/add-serialport.js +53 -98
- package/add-socket.js +43 -66
- package/add-websocket.js +68 -140
- package/index.js +288 -155
- package/lib/env.js +18 -0
- package/lib/indexFile.js +87 -0
- package/lib/names.js +21 -0
- package/lib/project.js +122 -0
- package/lib/routeFile.js +56 -0
- package/lib/ui.js +161 -0
- package/package.json +9 -16
- package/skeleton/dev-api.js +2785 -0
- package/skeleton/index.js +10 -3
- package/skeleton/package.json +9 -5
- package/skeleton/public/fonts/NotoSans-Bold.woff2 +0 -0
- package/skeleton/public/fonts/NotoSans-Light.woff2 +0 -0
- package/skeleton/public/fonts/NotoSans-Medium.woff2 +0 -0
- package/skeleton/public/fonts/NotoSans-Regular.woff2 +0 -0
- package/skeleton/public/fonts/NotoSans-SemiBold.woff2 +0 -0
- package/skeleton/public/images/mongo.png +0 -0
- package/skeleton/public/images/mysql.png +0 -0
- package/skeleton/public/images/postgresql.png +0 -0
- package/skeleton/public/images/sqlite.png +0 -0
- package/skeleton/public/index.html +7562 -64
- package/README_4bnode.md +0 -197
- package/src/auth/passport.js +0 -21
- package/src/db.js +0 -13
- package/test-server/.env.development +0 -1
- package/test-server/.env.production +0 -1
- package/test-server/index.js +0 -82
- package/test-server/package-lock.json +0 -1267
- package/test-server/package.json +0 -20
- package/test-server/public/index.html +0 -90
package/add-login.js
CHANGED
|
@@ -2,219 +2,42 @@
|
|
|
2
2
|
|
|
3
3
|
import fs from "fs";
|
|
4
4
|
import path from "path";
|
|
5
|
-
import readline from "readline";
|
|
6
5
|
import crypto from "crypto";
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function listModels() {
|
|
33
|
-
const projectRoot = process.cwd();
|
|
34
|
-
const modelsDir = path.join(projectRoot, "src", "models");
|
|
35
|
-
if (!fs.existsSync(modelsDir)) {
|
|
36
|
-
console.error(chalk.red("No models directory found in src/models"));
|
|
37
|
-
process.exit(0);
|
|
38
|
-
}
|
|
39
|
-
const modelFiles = fs
|
|
40
|
-
.readdirSync(modelsDir)
|
|
41
|
-
.filter((file) => file.endsWith(".js"));
|
|
42
|
-
if (modelFiles.length === 0) {
|
|
43
|
-
console.error(chalk.red("No model files found in src/models"));
|
|
44
|
-
process.exit(0);
|
|
45
|
-
}
|
|
46
|
-
return modelFiles;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function getModelSchema(modelName) {
|
|
50
|
-
const projectRoot = process.cwd();
|
|
51
|
-
const modelFilePath = path.join(
|
|
52
|
-
projectRoot,
|
|
53
|
-
"src",
|
|
54
|
-
"models",
|
|
55
|
-
modelName + ".js"
|
|
56
|
-
);
|
|
57
|
-
const modelFileContent = fs.readFileSync(modelFilePath, "utf8");
|
|
58
|
-
|
|
59
|
-
const schemaRegex = /new mongoose\.Schema\(\s*\{([\s\S]*?)\}\s*\)/m;
|
|
60
|
-
const match = modelFileContent.match(schemaRegex);
|
|
61
|
-
|
|
62
|
-
if (match) {
|
|
63
|
-
try {
|
|
64
|
-
const schemaContent = match[1];
|
|
65
|
-
const fieldRegex = /(\w+):\s*\{[\s\S]*?\}/g;
|
|
66
|
-
const schemaKeys = [];
|
|
67
|
-
let fieldMatch;
|
|
68
|
-
while ((fieldMatch = fieldRegex.exec(schemaContent)) !== null) {
|
|
69
|
-
schemaKeys.push(fieldMatch[1]);
|
|
70
|
-
}
|
|
71
|
-
return schemaKeys;
|
|
72
|
-
} catch (error) {
|
|
73
|
-
console.error(chalk.red("Error parsing schema: "), error);
|
|
74
|
-
process.exit(0);
|
|
75
|
-
}
|
|
76
|
-
} else {
|
|
77
|
-
console.error(chalk.red("Schema not found in model file."));
|
|
78
|
-
process.exit(0);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function promptSelectFields(fields, callback) {
|
|
83
|
-
console.log(
|
|
84
|
-
chalk.yellow(
|
|
85
|
-
"Select the fields to include in the data insertion (space-separated list of numbers):"
|
|
86
|
-
)
|
|
87
|
-
);
|
|
88
|
-
fields.forEach((field, index) => {
|
|
89
|
-
console.log(chalk.cyan(`${index + 1}. ${field}`));
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
rl.question(
|
|
93
|
-
chalk.yellow("Enter the numbers of the fields: "),
|
|
94
|
-
(fieldAnswer) => {
|
|
95
|
-
const selectedIndexes = fieldAnswer
|
|
96
|
-
.split(" ")
|
|
97
|
-
.map((index) => parseInt(index.trim(), 10) - 1);
|
|
98
|
-
const selectedFields = selectedIndexes
|
|
99
|
-
.map((index) => fields[index])
|
|
100
|
-
.filter((field) => field !== undefined);
|
|
101
|
-
callback(selectedFields);
|
|
102
|
-
rl.close();
|
|
103
|
-
}
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function generateJwtSecret() {
|
|
108
|
-
return crypto.randomBytes(32).toString("hex");
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function updateEnvFile(filePath, jwtSecret) {
|
|
112
|
-
const jwtSecretLine = `JWT_SECRET=${jwtSecret}\n`;
|
|
113
|
-
|
|
114
|
-
if (fs.existsSync(filePath)) {
|
|
115
|
-
let envContent = fs.readFileSync(filePath, "utf8");
|
|
116
|
-
if (envContent.includes("JWT_SECRET=")) {
|
|
117
|
-
envContent = envContent.replace(/JWT_SECRET=.*/, jwtSecretLine.trim());
|
|
118
|
-
} else {
|
|
119
|
-
envContent = envContent.trim() + "\n" + jwtSecretLine;
|
|
120
|
-
}
|
|
121
|
-
fs.writeFileSync(filePath, envContent, "utf8");
|
|
122
|
-
} else {
|
|
123
|
-
fs.writeFileSync(filePath, jwtSecretLine, "utf8");
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function installDependencies() {
|
|
128
|
-
console.log(chalk.green("Installing dependencies: jsonwebtoken, bcrypt..."));
|
|
129
|
-
execSync("npm install jsonwebtoken bcrypt", { stdio: "inherit" });
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function addLoginTemplateToRoute(routeFile, fields, modelName, useExpiry) {
|
|
133
|
-
const projectRoot = process.cwd();
|
|
134
|
-
const routeFilePath = path.join(projectRoot, "src", "routes", routeFile);
|
|
135
|
-
const pascalModelName =
|
|
136
|
-
modelName.charAt(0).toUpperCase() + modelName.slice(1);
|
|
137
|
-
const loginTemplateImport = `
|
|
138
|
-
import bcrypt from 'bcrypt';
|
|
139
|
-
import jwt from 'jsonwebtoken';
|
|
140
|
-
import ${pascalModelName} from '../models/${modelName}.js';
|
|
141
|
-
`;
|
|
142
|
-
|
|
143
|
-
const loginTemplateFunction = `
|
|
144
|
-
// Login logic
|
|
145
|
-
const { ${fields.join(", ")} } = req.body;
|
|
146
|
-
try {
|
|
147
|
-
const user = await ${pascalModelName}.findOne({ ${fields[0]}: ${fields[0]} });
|
|
148
|
-
if (!user) {
|
|
149
|
-
return res.status(404).json({ message: 'User not found' });
|
|
150
|
-
}
|
|
151
|
-
${
|
|
152
|
-
fields.includes("password")
|
|
153
|
-
? `
|
|
154
|
-
const isMatch = await bcrypt.compare(password, user.password);
|
|
155
|
-
if (!isMatch) {
|
|
156
|
-
return res.status(400).json({ message: 'Invalid credentials' });
|
|
157
|
-
}`
|
|
158
|
-
: ""
|
|
159
|
-
}
|
|
160
|
-
const payload = { id: user._id };
|
|
161
|
-
${
|
|
162
|
-
useExpiry
|
|
163
|
-
? `
|
|
164
|
-
const token = jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: '15m' });
|
|
165
|
-
const refreshToken = jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: '7d' });
|
|
166
|
-
res.json({ token, refreshToken });`
|
|
167
|
-
: `
|
|
168
|
-
const token = jwt.sign(payload, process.env.JWT_SECRET);
|
|
169
|
-
res.json({ token });`
|
|
170
|
-
}
|
|
171
|
-
} catch (err) {
|
|
172
|
-
console.error(err);
|
|
173
|
-
res.status(500).json({ message: 'Server error' });
|
|
174
|
-
}
|
|
175
|
-
`;
|
|
176
|
-
|
|
177
|
-
if (!fs.existsSync(routeFilePath)) {
|
|
178
|
-
console.error(
|
|
179
|
-
chalk.red(`The specified route file does not exist: ${routeFilePath}`)
|
|
180
|
-
);
|
|
181
|
-
process.exit(0);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
let routeContent = fs.readFileSync(routeFilePath, "utf8");
|
|
185
|
-
|
|
186
|
-
if (!routeContent.includes("import bcrypt")) {
|
|
187
|
-
routeContent = loginTemplateImport + routeContent;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const lastCloseIndex = routeContent.lastIndexOf("});");
|
|
191
|
-
if (lastCloseIndex !== -1) {
|
|
192
|
-
const updatedContent = [
|
|
193
|
-
routeContent.slice(0, lastCloseIndex),
|
|
194
|
-
loginTemplateFunction,
|
|
195
|
-
routeContent.slice(lastCloseIndex),
|
|
196
|
-
].join("\n");
|
|
197
|
-
|
|
198
|
-
routeContent = updatedContent;
|
|
199
|
-
} else {
|
|
200
|
-
routeContent += `\n${loginTemplateFunction}\n`;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
fs.writeFileSync(routeFilePath, routeContent, "utf8");
|
|
204
|
-
console.log(chalk.green(`Login template added to ${routeFile}`));
|
|
205
|
-
}
|
|
6
|
+
import { select, confirm, checkbox } from "@inquirer/prompts";
|
|
7
|
+
import { toPascalCase } from "./lib/names.js";
|
|
8
|
+
import {
|
|
9
|
+
getProjectRoot,
|
|
10
|
+
listRoutes,
|
|
11
|
+
listModels,
|
|
12
|
+
getModelSchema,
|
|
13
|
+
} from "./lib/project.js";
|
|
14
|
+
import { updateEnvFile } from "./lib/env.js";
|
|
15
|
+
import { addRouteRegistration } from "./lib/indexFile.js";
|
|
16
|
+
import {
|
|
17
|
+
getRouteFilePath,
|
|
18
|
+
addImportToRoute,
|
|
19
|
+
insertCodeIntoRoute,
|
|
20
|
+
} from "./lib/routeFile.js";
|
|
21
|
+
import {
|
|
22
|
+
showHeader,
|
|
23
|
+
showTaskDone,
|
|
24
|
+
showError,
|
|
25
|
+
showInfo,
|
|
26
|
+
showFileAction,
|
|
27
|
+
installDeps,
|
|
28
|
+
} from "./lib/ui.js";
|
|
206
29
|
|
|
207
30
|
function addAuthMiddleware() {
|
|
208
|
-
const projectRoot =
|
|
209
|
-
const
|
|
31
|
+
const projectRoot = getProjectRoot();
|
|
32
|
+
const authDir = path.join(projectRoot, "src", "middleware");
|
|
33
|
+
const authFilePath = path.join(authDir, "auth.js");
|
|
210
34
|
|
|
211
35
|
if (fs.existsSync(authFilePath)) {
|
|
212
|
-
|
|
36
|
+
showInfo("Auth middleware already exists.");
|
|
213
37
|
return;
|
|
214
38
|
}
|
|
215
39
|
|
|
216
|
-
const
|
|
217
|
-
import jwt from 'jsonwebtoken';
|
|
40
|
+
const content = `import jwt from 'jsonwebtoken';
|
|
218
41
|
|
|
219
42
|
const auth = (req, res, next) => {
|
|
220
43
|
const token = req.header('Authorization')?.replace('Bearer ', '');
|
|
@@ -234,25 +57,22 @@ const auth = (req, res, next) => {
|
|
|
234
57
|
export default auth;
|
|
235
58
|
`;
|
|
236
59
|
|
|
237
|
-
fs.mkdirSync(
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
fs.writeFileSync(authFilePath, authMiddlewareContent.trim(), "utf8");
|
|
241
|
-
console.log(chalk.green("Auth middleware created successfully."));
|
|
60
|
+
fs.mkdirSync(authDir, { recursive: true });
|
|
61
|
+
fs.writeFileSync(authFilePath, content, "utf8");
|
|
62
|
+
showFileAction("created", "src/middleware/auth.js");
|
|
242
63
|
}
|
|
243
64
|
|
|
244
65
|
function addRefreshTokenRoute() {
|
|
245
|
-
const projectRoot =
|
|
66
|
+
const projectRoot = getProjectRoot();
|
|
246
67
|
const routesDir = path.join(projectRoot, "src", "routes");
|
|
247
|
-
const
|
|
68
|
+
const filePath = path.join(routesDir, "refresh-token.js");
|
|
248
69
|
|
|
249
|
-
if (fs.existsSync(
|
|
250
|
-
|
|
70
|
+
if (fs.existsSync(filePath)) {
|
|
71
|
+
showInfo("Refresh token route already exists.");
|
|
251
72
|
return;
|
|
252
73
|
}
|
|
253
74
|
|
|
254
|
-
const
|
|
255
|
-
import express from 'express';
|
|
75
|
+
const content = `import express from 'express';
|
|
256
76
|
import jwt from 'jsonwebtoken';
|
|
257
77
|
|
|
258
78
|
const router = express.Router();
|
|
@@ -278,118 +98,131 @@ export default router;
|
|
|
278
98
|
`;
|
|
279
99
|
|
|
280
100
|
fs.mkdirSync(routesDir, { recursive: true });
|
|
281
|
-
fs.writeFileSync(
|
|
101
|
+
fs.writeFileSync(filePath, content, "utf8");
|
|
102
|
+
showFileAction("created", "src/routes/refresh-token.js");
|
|
103
|
+
|
|
104
|
+
addRouteRegistration(
|
|
105
|
+
"import refreshToken from './src/routes/refresh-token.js';",
|
|
106
|
+
"app.use('/refresh-token', refreshToken);"
|
|
107
|
+
);
|
|
108
|
+
showFileAction("updated", "index.js");
|
|
109
|
+
}
|
|
282
110
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
const routeImport = `import refreshToken from './src/routes/refresh-token.js';\n`;
|
|
286
|
-
const routeUse = `app.use('/refresh-token', refreshToken);\n`;
|
|
111
|
+
async function main() {
|
|
112
|
+
showHeader("add-login", "Setup authentication & login");
|
|
287
113
|
|
|
288
|
-
|
|
289
|
-
if (
|
|
290
|
-
|
|
114
|
+
const routeFiles = listRoutes();
|
|
115
|
+
if (routeFiles.length === 0) {
|
|
116
|
+
showError("No route files found in src/routes.");
|
|
117
|
+
process.exit(1);
|
|
291
118
|
}
|
|
292
119
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
routeUse + "app.listen(port"
|
|
298
|
-
);
|
|
299
|
-
} else if (indexContent.includes("server.listen(port")) {
|
|
300
|
-
indexContent = indexContent.replace(
|
|
301
|
-
"server.listen(port",
|
|
302
|
-
routeUse + "server.listen(port"
|
|
303
|
-
);
|
|
304
|
-
}
|
|
120
|
+
const modelFiles = listModels();
|
|
121
|
+
if (modelFiles.length === 0) {
|
|
122
|
+
showError("No model files found in src/models.");
|
|
123
|
+
process.exit(1);
|
|
305
124
|
}
|
|
306
125
|
|
|
307
|
-
|
|
126
|
+
const selectedRoute = await select({
|
|
127
|
+
message: "Select route file to modify:",
|
|
128
|
+
choices: routeFiles.map((f) => ({ name: f, value: f })),
|
|
129
|
+
});
|
|
308
130
|
|
|
309
|
-
|
|
310
|
-
|
|
131
|
+
const selectedModelFile = await select({
|
|
132
|
+
message: "Select model file to use:",
|
|
133
|
+
choices: modelFiles.map((f) => ({ name: f, value: f })),
|
|
134
|
+
});
|
|
311
135
|
|
|
312
|
-
const
|
|
313
|
-
const
|
|
136
|
+
const modelName = path.basename(selectedModelFile, ".js");
|
|
137
|
+
const pascalName = toPascalCase(modelName);
|
|
138
|
+
const schemaFields = getModelSchema(modelName);
|
|
314
139
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
}
|
|
140
|
+
if (schemaFields.length === 0) {
|
|
141
|
+
showError("No fields found in the selected model.");
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const useExpiry = await confirm({
|
|
146
|
+
message: "Use token expiry? (15m access + 7d refresh)",
|
|
147
|
+
default: false,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const selectedFields = await checkbox({
|
|
151
|
+
message: "Select login fields:",
|
|
152
|
+
choices: schemaFields.map((f) => ({ name: f, value: f })),
|
|
153
|
+
validate: (v) => (v.length > 0 ? true : "Select at least one field."),
|
|
154
|
+
});
|
|
319
155
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
(
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
(expiryAnswer) => {
|
|
345
|
-
const useExpiry = expiryAnswer.trim().toLowerCase() === "y";
|
|
346
|
-
promptSelectFields(schemaFields, (fields) => {
|
|
347
|
-
addLoginTemplateToRoute(
|
|
348
|
-
selectedRoute,
|
|
349
|
-
fields,
|
|
350
|
-
selectedModel,
|
|
351
|
-
useExpiry
|
|
352
|
-
);
|
|
353
|
-
|
|
354
|
-
const jwtSecret = generateJwtSecret();
|
|
355
|
-
const projectRoot = process.cwd();
|
|
356
|
-
updateEnvFile(
|
|
357
|
-
path.join(projectRoot, ".env.development"),
|
|
358
|
-
jwtSecret
|
|
359
|
-
);
|
|
360
|
-
updateEnvFile(
|
|
361
|
-
path.join(projectRoot, ".env.production"),
|
|
362
|
-
jwtSecret
|
|
363
|
-
);
|
|
364
|
-
console.log(
|
|
365
|
-
chalk.green(
|
|
366
|
-
"JWT_SECRET has been added to .env.development and .env.production files."
|
|
367
|
-
)
|
|
368
|
-
);
|
|
369
|
-
|
|
370
|
-
installDependencies();
|
|
371
|
-
addAuthMiddleware();
|
|
372
|
-
if (useExpiry) {
|
|
373
|
-
addRefreshTokenRoute();
|
|
374
|
-
}
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
);
|
|
378
|
-
} else {
|
|
379
|
-
console.error(
|
|
380
|
-
chalk.red("Invalid selection. Please choose a valid number.")
|
|
381
|
-
);
|
|
382
|
-
rl.close();
|
|
383
|
-
process.exit(0);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
);
|
|
387
|
-
} else {
|
|
388
|
-
console.error(
|
|
389
|
-
chalk.red("Invalid selection. Please choose a valid number.")
|
|
390
|
-
);
|
|
391
|
-
rl.close();
|
|
392
|
-
process.exit(0);
|
|
156
|
+
console.log();
|
|
157
|
+
|
|
158
|
+
const routeFilePath = getRouteFilePath(selectedRoute);
|
|
159
|
+
|
|
160
|
+
addImportToRoute(
|
|
161
|
+
routeFilePath,
|
|
162
|
+
`import bcrypt from 'bcrypt';\nimport jwt from 'jsonwebtoken';\nimport ${pascalName} from '../models/${modelName}.js';`
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
const loginCode = `
|
|
166
|
+
router.post('/login', async (req, res) => {
|
|
167
|
+
const { ${selectedFields.join(", ")} } = req.body;
|
|
168
|
+
try {
|
|
169
|
+
const user = await ${pascalName}.findOne({ ${selectedFields[0]}: ${selectedFields[0]} });
|
|
170
|
+
if (!user) {
|
|
171
|
+
return res.status(404).json({ message: 'User not found' });
|
|
172
|
+
}
|
|
173
|
+
${
|
|
174
|
+
selectedFields.includes("password")
|
|
175
|
+
? `const isMatch = await bcrypt.compare(password, user.password);
|
|
176
|
+
if (!isMatch) {
|
|
177
|
+
return res.status(400).json({ message: 'Invalid credentials' });
|
|
178
|
+
}`
|
|
179
|
+
: ""
|
|
393
180
|
}
|
|
181
|
+
const payload = { id: user._id };
|
|
182
|
+
${
|
|
183
|
+
useExpiry
|
|
184
|
+
? `const token = jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: '15m' });
|
|
185
|
+
const refreshToken = jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: '7d' });
|
|
186
|
+
res.json({ token, refreshToken });`
|
|
187
|
+
: `const token = jwt.sign(payload, process.env.JWT_SECRET);
|
|
188
|
+
res.json({ token });`
|
|
189
|
+
}
|
|
190
|
+
} catch (err) {
|
|
191
|
+
console.error(err);
|
|
192
|
+
res.status(500).json({ message: 'Server error' });
|
|
394
193
|
}
|
|
395
|
-
)
|
|
194
|
+
});`;
|
|
195
|
+
|
|
196
|
+
insertCodeIntoRoute(routeFilePath, loginCode);
|
|
197
|
+
showFileAction("updated", `src/routes/${selectedRoute}`);
|
|
198
|
+
|
|
199
|
+
const projectRoot = getProjectRoot();
|
|
200
|
+
const devSecret = crypto.randomBytes(32).toString("hex");
|
|
201
|
+
const prodSecret = crypto.randomBytes(32).toString("hex");
|
|
202
|
+
updateEnvFile(path.join(projectRoot, ".env.development"), "JWT_SECRET", devSecret);
|
|
203
|
+
showFileAction("updated", ".env.development");
|
|
204
|
+
updateEnvFile(path.join(projectRoot, ".env.production"), "JWT_SECRET", prodSecret);
|
|
205
|
+
showFileAction("updated", ".env.production");
|
|
206
|
+
|
|
207
|
+
await installDeps(["jsonwebtoken", "bcrypt"]);
|
|
208
|
+
|
|
209
|
+
addAuthMiddleware();
|
|
210
|
+
|
|
211
|
+
if (useExpiry) {
|
|
212
|
+
addRefreshTokenRoute();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const details = [
|
|
216
|
+
`Route: ${selectedRoute}`,
|
|
217
|
+
`Model: ${modelName}`,
|
|
218
|
+
`Fields: ${selectedFields.join(", ")}`,
|
|
219
|
+
useExpiry ? "JWT with 15m expiry + refresh token" : "JWT without expiry",
|
|
220
|
+
];
|
|
221
|
+
showTaskDone("Login & auth configured", details);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
main().catch((err) => {
|
|
225
|
+
if (err.name === "ExitPromptError") process.exit(0);
|
|
226
|
+
showError(err.message);
|
|
227
|
+
process.exit(1);
|
|
228
|
+
});
|