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-update.js
CHANGED
|
@@ -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
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
+
showError("No route files found in src/routes.");
|
|
28
|
+
process.exit(1);
|
|
27
29
|
}
|
|
28
|
-
return routeFiles;
|
|
29
|
-
}
|
|
30
30
|
|
|
31
|
-
|
|
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
|
-
|
|
43
|
-
|
|
33
|
+
showError("No model files found in src/models.");
|
|
34
|
+
process.exit(1);
|
|
44
35
|
}
|
|
45
|
-
return modelFiles;
|
|
46
|
-
}
|
|
47
36
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
47
|
+
const modelName = path.basename(selectedModelFile, ".js");
|
|
48
|
+
const pascalName = toPascalCase(modelName);
|
|
49
|
+
const fields = getModelSchema(modelName);
|
|
89
50
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
103
|
-
console.error(
|
|
104
|
-
chalk.red(`The specified route file does not exist: ${routeFilePath}`)
|
|
105
|
-
);
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
56
|
+
console.log();
|
|
108
57
|
|
|
109
|
-
|
|
58
|
+
const routeFilePath = getRouteFilePath(selectedRoute);
|
|
59
|
+
const method = detectHttpMethod(routeFilePath);
|
|
60
|
+
const dataSource = getRequestDataSource(method);
|
|
110
61
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
62
|
+
let importBlock = `import ${pascalName} from '../models/${modelName}.js';`;
|
|
63
|
+
let passwordLogic = "";
|
|
114
64
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
|
|
161
|
-
|
|
98
|
+
insertCodeIntoRoute(routeFilePath, updateCode);
|
|
99
|
+
showFileAction("updated", `src/routes/${selectedRoute}`);
|
|
162
100
|
|
|
163
|
-
// Install bcrypt if needed
|
|
164
101
|
if (fields.includes("password")) {
|
|
165
|
-
|
|
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
|
-
|
|
174
|
-
|
|
175
|
-
|
|
105
|
+
showTaskDone("Update route added", [
|
|
106
|
+
`Route: PUT /:id in ${selectedRoute}`,
|
|
107
|
+
`Model: ${modelName}`,
|
|
108
|
+
`Fields: ${fields.join(", ")}`,
|
|
109
|
+
]);
|
|
176
110
|
}
|
|
177
111
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
);
|