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-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
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
117
|
-
|
|
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
|
-
|
|
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
|
-
|
|
134
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
109
|
+
showTaskDone("Read route added", [
|
|
110
|
+
`Route: ${routeDetail} in ${selectedRoute}`,
|
|
111
|
+
`Model: ${modelName}`,
|
|
112
|
+
]);
|
|
144
113
|
}
|
|
145
114
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
);
|