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.
Files changed (43) hide show
  1. package/README.md +139 -114
  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
package/add-mongo-read.js CHANGED
@@ -1,235 +1,119 @@
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 [];
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
+ } 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-read", "Add data read route");
21
+
22
+ const routeFiles = listRoutes();
23
+ if (routeFiles.length === 0) {
24
+ showError("No route files found in src/routes.");
25
+ process.exit(1);
19
26
  }
20
- return fs.readdirSync(routesDir).filter((file) => file.endsWith(".js"));
21
- }
22
27
 
23
- function listModels() {
24
- const projectRoot = process.cwd();
25
- const modelsDir = path.join(projectRoot, "src", "models");
26
- if (!fs.existsSync(modelsDir)) {
27
- console.error(chalk.red("No models directory found in src/models"));
28
- return [];
28
+ const modelFiles = listModels();
29
+ if (modelFiles.length === 0) {
30
+ showError("No model files found in src/models.");
31
+ process.exit(1);
29
32
  }
30
- return fs.readdirSync(modelsDir).filter((file) => file.endsWith(".js"));
31
- }
32
33
 
33
- function getModelSchema(modelName) {
34
- const projectRoot = process.cwd();
35
- const modelFilePath = path.join(
36
- projectRoot,
37
- "src",
38
- "models",
39
- modelName + ".js"
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 fields = getModelSchema(modelName, { includeId: true });
47
+
48
+ const readType = await select({
49
+ message: "How to fetch data?",
50
+ choices: [
51
+ { name: "Fetch all data", value: "all" },
52
+ { name: "Fetch by specific field", value: "field" },
53
+ ],
54
+ });
55
+
56
+ console.log();
57
+
58
+ const routeFilePath = getRouteFilePath(selectedRoute);
59
+ addImportToRoute(
60
+ routeFilePath,
61
+ `import ${pascalName} from '../models/${modelName}.js';`
40
62
  );
41
63
 
42
- if (!fs.existsSync(modelFilePath)) {
43
- console.error(chalk.red(`Model file not found: ${modelFilePath}`));
44
- return [];
45
- }
46
-
47
- const modelFileContent = fs.readFileSync(modelFilePath, "utf8");
48
- const schemaRegex = /new mongoose\.Schema\s*\(\s*\{([\s\S]*?)\}\s*\)/m;
49
- const match = modelFileContent.match(schemaRegex);
50
-
51
- if (match) {
52
- try {
53
- const schemaContent = match[1];
54
- const fieldRegex = /(\w+):\s*\{[\s\S]*?\}/g;
55
- const schemaKeys = ["_id"]; // Always include `_id`
56
- let fieldMatch;
57
- while ((fieldMatch = fieldRegex.exec(schemaContent)) !== null) {
58
- schemaKeys.push(fieldMatch[1]);
59
- }
60
- return schemaKeys;
61
- } catch (error) {
62
- console.error(chalk.red("Error parsing schema: "), error);
63
- return ["_id"];
64
- }
65
- } else {
66
- console.error(chalk.red("Schema not found in model file."));
67
- return ["_id"];
64
+ let readCode;
65
+ let routeDetail;
66
+
67
+ if (readType === "all") {
68
+ readCode = `
69
+ router.get('/', async (req, res) => {
70
+ try {
71
+ const documents = await ${pascalName}.find();
72
+ res.json({ data: documents });
73
+ } catch (err) {
74
+ console.error(err);
75
+ res.status(500).json({ message: 'Server error' });
68
76
  }
69
- }
70
-
71
- function addReadTemplateToRoute(routeFile, modelName, selectedField = null) {
72
- const projectRoot = process.cwd();
73
- const routeFilePath = path.join(projectRoot, "src", "routes", routeFile);
74
- const pascalModelName =
75
- modelName.charAt(0).toUpperCase() + modelName.slice(1);
76
- const readTemplateImport = `import ${pascalModelName} from '../models/${modelName}.js';\n`;
77
-
78
- let readTemplateFunction;
79
-
80
- if (selectedField) {
81
- readTemplateFunction = `
82
- // Fetch data based on ${selectedField}
83
- try {
84
- const documents = await ${pascalModelName}.find({ ${selectedField}: req.query.${selectedField} });
85
- if (documents.length === 0) {
86
- return res.status(404).json({ message: "No matching documents found" });
87
- }
88
- res.json({ data: documents });
89
- } catch (err) {
90
- console.error(err);
91
- res.status(500).json({ message: "Server error" });
92
- }
93
- `;
77
+ });`;
78
+ routeDetail = "GET / (fetch all)";
94
79
  } else {
95
- readTemplateFunction = `
96
- // Fetch all data
97
- try {
98
- const documents = await ${pascalModelName}.find();
99
- res.json({ data: documents });
100
- } catch (err) {
101
- console.error(err);
102
- res.status(500).json({ message: "Server error" });
103
- }
104
- `;
105
- }
106
-
107
- if (!fs.existsSync(routeFilePath)) {
108
- console.error(
109
- chalk.red(`The specified route file does not exist: ${routeFilePath}`)
110
- );
111
- return;
112
- }
113
-
114
- let routeContent = fs.readFileSync(routeFilePath, "utf8");
80
+ if (fields.length === 0) {
81
+ showError("No fields found in the selected model.");
82
+ process.exit(1);
83
+ }
115
84
 
116
- if (!routeContent.includes(`import ${pascalModelName}`)) {
117
- routeContent = readTemplateImport + routeContent;
85
+ const selectedField = await select({
86
+ message: "Select field to filter by:",
87
+ choices: fields.map((f) => ({ name: f, value: f })),
88
+ });
89
+
90
+ readCode = `
91
+ router.get('/', async (req, res) => {
92
+ try {
93
+ const documents = await ${pascalName}.find({ ${selectedField}: req.query.${selectedField} });
94
+ if (documents.length === 0) {
95
+ return res.status(404).json({ message: 'No matching documents found' });
96
+ }
97
+ res.json({ data: documents });
98
+ } catch (err) {
99
+ console.error(err);
100
+ res.status(500).json({ message: 'Server error' });
118
101
  }
119
-
120
- const lastCloseIndex = routeContent.lastIndexOf("});");
121
- if (lastCloseIndex !== -1) {
122
- const updatedContent = [
123
- routeContent.slice(0, lastCloseIndex),
124
- readTemplateFunction,
125
- routeContent.slice(lastCloseIndex),
126
- ].join("\n");
127
-
128
- routeContent = updatedContent;
129
- } else {
130
- routeContent += `\n${readTemplateFunction}\n`;
102
+ });`;
103
+ routeDetail = `GET /?${selectedField}=... (filter by ${selectedField})`;
131
104
  }
132
105
 
133
- fs.writeFileSync(routeFilePath, routeContent, "utf8");
134
- console.log(chalk.green(`Read template added to ${routeFile}`));
135
- }
136
-
137
- // Main Logic
138
- const routeFiles = listRoutes();
139
- const modelFiles = listModels();
106
+ insertCodeIntoRoute(routeFilePath, readCode);
107
+ showFileAction("updated", `src/routes/${selectedRoute}`);
140
108
 
141
- if (routeFiles.length === 0 || modelFiles.length === 0) {
142
- rl.close();
143
- process.exit(0);
109
+ showTaskDone("Read route added", [
110
+ `Route: ${routeDetail} in ${selectedRoute}`,
111
+ `Model: ${modelName}`,
112
+ ]);
144
113
  }
145
114
 
146
- console.log(chalk.blue("Select the route file to modify:"));
147
- routeFiles.forEach((file, index) => {
148
- console.log(chalk.yellow(`${index + 1}. ${file}`));
115
+ main().catch((err) => {
116
+ if (err.name === "ExitPromptError") process.exit(0);
117
+ showError(err.message);
118
+ process.exit(1);
149
119
  });
150
-
151
- rl.question(
152
- chalk.cyan("Enter the number of the route file: "),
153
- (routeAnswer) => {
154
- const routeIndex = parseInt(routeAnswer, 10) - 1;
155
- if (routeIndex >= 0 && routeIndex < routeFiles.length) {
156
- const selectedRoute = routeFiles[routeIndex];
157
-
158
- console.log(chalk.blue("Select the model file to use:"));
159
- modelFiles.forEach((file, index) => {
160
- console.log(chalk.yellow(`${index + 1}. ${file}`));
161
- });
162
-
163
- rl.question(
164
- chalk.cyan("Enter the number of the model file: "),
165
- (modelAnswer) => {
166
- const modelIndex = parseInt(modelAnswer, 10) - 1;
167
- if (modelIndex >= 0 && modelIndex < modelFiles.length) {
168
- const selectedModel = path.basename(modelFiles[modelIndex], ".js");
169
- const schemaFields = getModelSchema(selectedModel);
170
-
171
- console.log(chalk.green("Do you want to:"));
172
- console.log(chalk.green("1. Fetch all data"));
173
- console.log(chalk.green("2. Fetch data based on a specific field"));
174
-
175
- rl.question(
176
- chalk.cyan("Enter your choice (1 or 2): "),
177
- (choiceAnswer) => {
178
- if (choiceAnswer === "1") {
179
- addReadTemplateToRoute(selectedRoute, selectedModel);
180
- rl.close();
181
- } else if (choiceAnswer === "2" && schemaFields.length > 0) {
182
- console.log(
183
- chalk.green(
184
- "Select a field to filter data by (_id is always available):"
185
- )
186
- );
187
- schemaFields.forEach((field, index) => {
188
- console.log(chalk.yellow(`${index + 1}. ${field}`));
189
- });
190
-
191
- rl.question(
192
- chalk.cyan("Enter the number of the field: "),
193
- (fieldAnswer) => {
194
- const fieldIndex = parseInt(fieldAnswer, 10) - 1;
195
- if (fieldIndex >= 0 && fieldIndex < schemaFields.length) {
196
- const selectedField = schemaFields[fieldIndex];
197
- addReadTemplateToRoute(
198
- selectedRoute,
199
- selectedModel,
200
- selectedField
201
- );
202
- } else {
203
- console.error(
204
- chalk.red(
205
- "Invalid selection. Please choose a valid field number."
206
- )
207
- );
208
- }
209
- rl.close();
210
- }
211
- );
212
- } else {
213
- console.error(
214
- chalk.red("Invalid choice. Please enter 1 or 2.")
215
- );
216
- rl.close();
217
- }
218
- }
219
- );
220
- } else {
221
- console.error(
222
- chalk.red("Invalid selection. Please choose a valid number.")
223
- );
224
- rl.close();
225
- }
226
- }
227
- );
228
- } else {
229
- console.error(
230
- chalk.red("Invalid selection. Please choose a valid number.")
231
- );
232
- rl.close();
233
- }
234
- }
235
- );