4bnode 3.5.3 → 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.
Files changed (43) hide show
  1. package/README.md +138 -113
  2. package/add-api.js +288 -156
  3. package/add-crud.js +226 -0
  4. package/add-login.js +152 -319
  5. package/add-mongo-delete.js +67 -138
  6. package/add-mongo-insert.js +87 -204
  7. package/add-mongo-read.js +100 -216
  8. package/add-mongo-schema.js +431 -199
  9. package/add-mongo-update.js +89 -202
  10. package/add-mongo.js +125 -218
  11. package/add-serialport.js +53 -98
  12. package/add-socket.js +43 -66
  13. package/add-websocket.js +68 -140
  14. package/index.js +288 -155
  15. package/lib/env.js +18 -0
  16. package/lib/indexFile.js +87 -0
  17. package/lib/names.js +21 -0
  18. package/lib/project.js +122 -0
  19. package/lib/routeFile.js +56 -0
  20. package/lib/ui.js +161 -0
  21. package/package.json +9 -16
  22. package/skeleton/dev-api.js +2785 -0
  23. package/skeleton/index.js +10 -3
  24. package/skeleton/package.json +9 -5
  25. package/skeleton/public/fonts/NotoSans-Bold.woff2 +0 -0
  26. package/skeleton/public/fonts/NotoSans-Light.woff2 +0 -0
  27. package/skeleton/public/fonts/NotoSans-Medium.woff2 +0 -0
  28. package/skeleton/public/fonts/NotoSans-Regular.woff2 +0 -0
  29. package/skeleton/public/fonts/NotoSans-SemiBold.woff2 +0 -0
  30. package/skeleton/public/images/mongo.png +0 -0
  31. package/skeleton/public/images/mysql.png +0 -0
  32. package/skeleton/public/images/postgresql.png +0 -0
  33. package/skeleton/public/images/sqlite.png +0 -0
  34. package/skeleton/public/index.html +7562 -64
  35. package/README_4bnode.md +0 -197
  36. package/src/auth/passport.js +0 -21
  37. package/src/db.js +0 -13
  38. package/test-server/.env.development +0 -1
  39. package/test-server/.env.production +0 -1
  40. package/test-server/index.js +0 -82
  41. package/test-server/package-lock.json +0 -1267
  42. package/test-server/package.json +0 -20
  43. package/test-server/public/index.html +0 -90
@@ -1,229 +1,116 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import fs from "fs";
4
3
  import path from "path";
5
- import readline from "readline";
6
- import chalk from "chalk";
7
- import { execSync } from "child_process";
8
-
9
- const rl = readline.createInterface({
10
- input: process.stdin,
11
- output: process.stdout,
12
- });
13
-
14
- function listRoutes() {
15
- const projectRoot = process.cwd();
16
- const routesDir = path.join(projectRoot, "src", "routes");
17
- if (!fs.existsSync(routesDir)) {
18
- console.error(chalk.red("No routes directory found in src/routes"));
19
- return null;
20
- }
21
- const routeFiles = fs
22
- .readdirSync(routesDir)
23
- .filter((file) => file.endsWith(".js"));
4
+ import { select } from "@inquirer/prompts";
5
+ import { toPascalCase } from "./lib/names.js";
6
+ import { listRoutes, listModels, getModelSchema } from "./lib/project.js";
7
+ import {
8
+ getRouteFilePath,
9
+ addImportToRoute,
10
+ insertCodeIntoRoute,
11
+ detectHttpMethod,
12
+ getRequestDataSource,
13
+ } from "./lib/routeFile.js";
14
+ import {
15
+ showHeader,
16
+ showTaskDone,
17
+ showError,
18
+ showFileAction,
19
+ installDeps,
20
+ } from "./lib/ui.js";
21
+
22
+ async function main() {
23
+ showHeader("add-mongo-update", "Add data update route");
24
+
25
+ const routeFiles = listRoutes();
24
26
  if (routeFiles.length === 0) {
25
- console.error(chalk.red("No route files found in src/routes"));
26
- return null;
27
+ showError("No route files found in src/routes.");
28
+ process.exit(1);
27
29
  }
28
- return routeFiles;
29
- }
30
30
 
31
- function listModels() {
32
- const projectRoot = process.cwd();
33
- const modelsDir = path.join(projectRoot, "src", "models");
34
- if (!fs.existsSync(modelsDir)) {
35
- console.error(chalk.red("No models directory found in src/models"));
36
- return null;
37
- }
38
- const modelFiles = fs
39
- .readdirSync(modelsDir)
40
- .filter((file) => file.endsWith(".js"));
31
+ const modelFiles = listModels();
41
32
  if (modelFiles.length === 0) {
42
- console.error(chalk.red("No model files found in src/models"));
43
- return null;
33
+ showError("No model files found in src/models.");
34
+ process.exit(1);
44
35
  }
45
- return modelFiles;
46
- }
47
36
 
48
- function getModelSchema(modelName) {
49
- const projectRoot = process.cwd();
50
- const modelFilePath = path.join(
51
- projectRoot,
52
- "src",
53
- "models",
54
- modelName + ".js"
55
- );
56
- const modelFileContent = fs.readFileSync(modelFilePath, "utf8");
57
-
58
- // Improved regex to handle beautified code
59
- const schemaRegex = /new\s+mongoose\.Schema\s*\(\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*\{/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
- return null;
75
- }
76
- } else {
77
- console.error(chalk.red("Schema not found in model file."));
78
- return null;
79
- }
80
- }
37
+ const selectedRoute = await select({
38
+ message: "Select route file to modify:",
39
+ choices: routeFiles.map((f) => ({ name: f, value: f })),
40
+ });
81
41
 
82
- function addUpdateTemplateToRoute(routeFile, fields, modelName) {
83
- const projectRoot = process.cwd();
84
- const routeFilePath = path.join(projectRoot, "src", "routes", routeFile);
85
- const pascalModelName =
86
- modelName.charAt(0).toUpperCase() + modelName.slice(1);
42
+ const selectedModelFile = await select({
43
+ message: "Select model file to use:",
44
+ choices: modelFiles.map((f) => ({ name: f, value: f })),
45
+ });
87
46
 
88
- let updateTemplateImport = `import ${pascalModelName} from '../models/${modelName}.js';\n`;
47
+ const modelName = path.basename(selectedModelFile, ".js");
48
+ const pascalName = toPascalCase(modelName);
49
+ const fields = getModelSchema(modelName);
89
50
 
90
- let passwordHashingLogic = "";
91
- if (fields.includes("password")) {
92
- updateTemplateImport =
93
- `import bcrypt from 'bcrypt';\n` + updateTemplateImport;
94
- passwordHashingLogic = `
95
- if (updatedData.password) {
96
- const salt = await bcrypt.genSalt(10);
97
- updatedData.password = await bcrypt.hash(updatedData.password, salt);
98
- }
99
- `;
51
+ if (fields.length === 0) {
52
+ showError("No fields found in the selected model.");
53
+ process.exit(1);
100
54
  }
101
55
 
102
- if (!fs.existsSync(routeFilePath)) {
103
- console.error(
104
- chalk.red(`The specified route file does not exist: ${routeFilePath}`)
105
- );
106
- return;
107
- }
56
+ console.log();
108
57
 
109
- let routeContent = fs.readFileSync(routeFilePath, "utf8");
58
+ const routeFilePath = getRouteFilePath(selectedRoute);
59
+ const method = detectHttpMethod(routeFilePath);
60
+ const dataSource = getRequestDataSource(method);
110
61
 
111
- if (!routeContent.includes(`import ${pascalModelName}`)) {
112
- routeContent = updateTemplateImport + routeContent;
113
- }
62
+ let importBlock = `import ${pascalName} from '../models/${modelName}.js';`;
63
+ let passwordLogic = "";
114
64
 
115
- // Detect HTTP method type
116
- const methodTypeRegex =
117
- /router\.(get|post|put|patch|delete)\((['"`])\/.*?\2,\s*async\s*\(req,\s*res/;
118
- const methodMatch = routeContent.match(methodTypeRegex);
119
- const methodType = methodMatch ? methodMatch[1].toLowerCase() : "put";
120
-
121
- // Determine whether to use req.body or req.query based on method type
122
- const requestDataSource =
123
- methodType === "get" || methodType === "delete" ? "req.query" : "req.body";
124
-
125
- // Generate update logic
126
- const updateTemplateFunction = `
127
- // Data update logic
128
- const updatedData = {
129
- ${fields
130
- .map((field) => `${field}: ${requestDataSource}.${field}`)
131
- .join(",\n ")}
132
- };
133
- ${passwordHashingLogic}
134
- try {
135
- const updatedDocument = await ${pascalModelName}.findByIdAndUpdate(req.params.id, updatedData, { new: true });
136
- if (!updatedDocument) {
137
- return res.status(404).json({ message: "Document not found" });
65
+ if (fields.includes("password")) {
66
+ importBlock = `import bcrypt from 'bcrypt';\n${importBlock}`;
67
+ passwordLogic = `
68
+ if (updatedData.password) {
69
+ const salt = await bcrypt.genSalt(10);
70
+ updatedData.password = await bcrypt.hash(updatedData.password, salt);
71
+ }`;
138
72
  }
139
- res.json({ message: "Data updated successfully", data: updatedDocument });
140
- } catch (err) {
141
- console.error(err);
142
- res.status(500).json({ message: "Server error" });
143
- }
144
- `;
145
-
146
- // Insert the logic before the last closing `});`
147
- const lastCloseIndex = routeContent.lastIndexOf("});");
148
- if (lastCloseIndex !== -1) {
149
- const updatedContent = [
150
- routeContent.slice(0, lastCloseIndex),
151
- updateTemplateFunction,
152
- routeContent.slice(lastCloseIndex),
153
- ].join("\n");
154
-
155
- routeContent = updatedContent;
156
- } else {
157
- routeContent += `\n${updateTemplateFunction}\n`;
73
+
74
+ addImportToRoute(routeFilePath, importBlock);
75
+
76
+ const fieldAssignments = fields
77
+ .map((f) => ` ${f}: ${dataSource}.${f}`)
78
+ .join(",\n");
79
+
80
+ const updateCode = `
81
+ router.put('/:id', async (req, res) => {
82
+ const updatedData = {
83
+ ${fieldAssignments}
84
+ };
85
+ ${passwordLogic}
86
+ try {
87
+ const updatedDocument = await ${pascalName}.findByIdAndUpdate(req.params.id, updatedData, { new: true });
88
+ if (!updatedDocument) {
89
+ return res.status(404).json({ message: 'Document not found' });
90
+ }
91
+ res.json({ message: 'Data updated successfully', data: updatedDocument });
92
+ } catch (err) {
93
+ console.error(err);
94
+ res.status(500).json({ message: 'Server error' });
158
95
  }
96
+ });`;
159
97
 
160
- fs.writeFileSync(routeFilePath, routeContent, "utf8");
161
- console.log(chalk.green(`Update template added to ${routeFile}`));
98
+ insertCodeIntoRoute(routeFilePath, updateCode);
99
+ showFileAction("updated", `src/routes/${selectedRoute}`);
162
100
 
163
- // Install bcrypt if needed
164
101
  if (fields.includes("password")) {
165
- console.log(chalk.green("Installing bcrypt..."));
166
- execSync("npm install bcrypt", { stdio: "inherit" });
102
+ await installDeps("bcrypt");
167
103
  }
168
- }
169
-
170
- const routeFiles = listRoutes();
171
- const modelFiles = listModels();
172
104
 
173
- if (!routeFiles || !modelFiles) {
174
- rl.close();
175
- process.exit(0);
105
+ showTaskDone("Update route added", [
106
+ `Route: PUT /:id in ${selectedRoute}`,
107
+ `Model: ${modelName}`,
108
+ `Fields: ${fields.join(", ")}`,
109
+ ]);
176
110
  }
177
111
 
178
- console.log(chalk.blue("Select the route file to modify:"));
179
- routeFiles.forEach((file, index) => {
180
- console.log(chalk.yellow(`${index + 1}. ${file}`));
112
+ main().catch((err) => {
113
+ if (err.name === "ExitPromptError") process.exit(0);
114
+ showError(err.message);
115
+ process.exit(1);
181
116
  });
182
-
183
- rl.question(
184
- chalk.cyan("Enter the number of the route file: "),
185
- (routeAnswer) => {
186
- const routeIndex = parseInt(routeAnswer, 10) - 1;
187
- if (routeIndex >= 0 && routeIndex < routeFiles.length) {
188
- const selectedRoute = routeFiles[routeIndex];
189
-
190
- console.log(chalk.blue("Select the model file to use:"));
191
- modelFiles.forEach((file, index) => {
192
- console.log(chalk.yellow(`${index + 1}. ${file}`));
193
- });
194
-
195
- rl.question(
196
- chalk.cyan("Enter the number of the model file: "),
197
- (modelAnswer) => {
198
- const modelIndex = parseInt(modelAnswer, 10) - 1;
199
- if (modelIndex >= 0 && modelIndex < modelFiles.length) {
200
- const selectedModel = path.basename(modelFiles[modelIndex], ".js");
201
- const schemaFields = getModelSchema(selectedModel);
202
-
203
- if (!schemaFields) {
204
- rl.close();
205
- process.exit(0);
206
- }
207
-
208
- addUpdateTemplateToRoute(
209
- selectedRoute,
210
- schemaFields,
211
- selectedModel
212
- );
213
- rl.close();
214
- } else {
215
- console.error(
216
- chalk.red("Invalid selection. Please choose a valid number.")
217
- );
218
- rl.close();
219
- }
220
- }
221
- );
222
- } else {
223
- console.error(
224
- chalk.red("Invalid selection. Please choose a valid number.")
225
- );
226
- rl.close();
227
- }
228
- }
229
- );