4bnode
Version:
4bnode is a CLI-powered backend development platform with a built-in visual dashboard to generate, manage, and test Node.js/Express APIs faster.
117 lines (97 loc) • 3.15 kB
JavaScript
import path from "path";
import { select } from "@inquirer/prompts";
import { toPascalCase } from "./lib/names.js";
import { listRoutes, listModels, getModelSchema } from "./lib/project.js";
import {
getRouteFilePath,
addImportToRoute,
insertCodeIntoRoute,
detectHttpMethod,
getRequestDataSource,
} from "./lib/routeFile.js";
import {
showHeader,
showTaskDone,
showError,
showFileAction,
installDeps,
} from "./lib/ui.js";
async function main() {
showHeader("add-mongo-update", "Add data update route");
const routeFiles = listRoutes();
if (routeFiles.length === 0) {
showError("No route files found in src/routes.");
process.exit(1);
}
const modelFiles = listModels();
if (modelFiles.length === 0) {
showError("No model files found in src/models.");
process.exit(1);
}
const selectedRoute = await select({
message: "Select route file to modify:",
choices: routeFiles.map((f) => ({ name: f, value: f })),
});
const selectedModelFile = await select({
message: "Select model file to use:",
choices: modelFiles.map((f) => ({ name: f, value: f })),
});
const modelName = path.basename(selectedModelFile, ".js");
const pascalName = toPascalCase(modelName);
const fields = getModelSchema(modelName);
if (fields.length === 0) {
showError("No fields found in the selected model.");
process.exit(1);
}
console.log();
const routeFilePath = getRouteFilePath(selectedRoute);
const method = detectHttpMethod(routeFilePath);
const dataSource = getRequestDataSource(method);
let importBlock = `import ${pascalName} from '../models/${modelName}.js';`;
let passwordLogic = "";
if (fields.includes("password")) {
importBlock = `import bcrypt from 'bcrypt';\n${importBlock}`;
passwordLogic = `
if (updatedData.password) {
const salt = await bcrypt.genSalt(10);
updatedData.password = await bcrypt.hash(updatedData.password, salt);
}`;
}
addImportToRoute(routeFilePath, importBlock);
const fieldAssignments = fields
.map((f) => ` ${f}: ${dataSource}.${f}`)
.join(",\n");
const updateCode = `
router.put('/:id', async (req, res) => {
const updatedData = {
${fieldAssignments}
};
${passwordLogic}
try {
const updatedDocument = await ${pascalName}.findByIdAndUpdate(req.params.id, updatedData, { returnDocument: 'after' });
if (!updatedDocument) {
return res.status(404).json({ message: 'Document not found' });
}
res.json({ message: 'Data updated successfully', data: updatedDocument });
} catch (err) {
console.error(err);
res.status(500).json({ message: 'Server error' });
}
});`;
insertCodeIntoRoute(routeFilePath, updateCode);
showFileAction("updated", `src/routes/${selectedRoute}`);
if (fields.includes("password")) {
await installDeps("bcrypt");
}
showTaskDone("Update route added", [
`Route: PUT /:id in ${selectedRoute}`,
`Model: ${modelName}`,
`Fields: ${fields.join(", ")}`,
]);
}
main().catch((err) => {
if (err.name === "ExitPromptError") process.exit(0);
showError(err.message);
process.exit(1);
});