4bnode 3.5.3 → 4.0.2

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