├── .gitignore
├── .DS_Store
├── servitectLogo.jpeg
├── bin
├── postinstall.js
├── readlineInterface.js
├── fileFormatter.js
├── modules
│ ├── dockerModule.js
│ ├── whatsappModule.js
│ ├── nodemailerModule.js
│ ├── chatModule.js
│ ├── ollamaModule.js
│ ├── uploadModule.js
│ ├── fcmModule.js
│ ├── nonActorModule.js
│ └── actorModule.js
├── helper
│ ├── initProjectDir.js
│ └── initialize.js
├── codeInserter.js
├── dependencies.js
├── header
│ └── header.js
├── dependencyInstaller.js
├── messages
│ └── message.js
├── mvcInitializers.js
├── prompts
│ └── menuPrompt.js
├── MVCBuilder.js
└── fileContents.js
├── LICENSE
├── package.json
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
--------------------------------------------------------------------------------
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kushkapadia/servitect/HEAD/.DS_Store
--------------------------------------------------------------------------------
/servitectLogo.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kushkapadia/servitect/HEAD/servitectLogo.jpeg
--------------------------------------------------------------------------------
/bin/postinstall.js:
--------------------------------------------------------------------------------
1 | import isGlobal from 'is-installed-globally';
2 |
3 | if (!isGlobal) {
4 | console.log('\x1b[33m%s\x1b[0m', '⚠️ It is recommended to install this package globally: npm install -g servitect');
5 | }
--------------------------------------------------------------------------------
/bin/readlineInterface.js:
--------------------------------------------------------------------------------
1 | import readline from 'readline';
2 |
3 | let rl;
4 |
5 | function initializeReadline() {
6 | if (!rl) {
7 | rl = readline.createInterface({
8 | input: process.stdin,
9 | output: process.stdout,
10 | });
11 | }
12 | return rl;
13 | }
14 |
15 | export { initializeReadline, rl };
16 |
--------------------------------------------------------------------------------
/bin/fileFormatter.js:
--------------------------------------------------------------------------------
1 | // Utility Function to remove unnecessary indentation and tab spaces
2 |
3 | const removeIndentation = (str) => {
4 | const lines = str.split("\n");
5 | const minIndent = Math.min(
6 | ...lines.filter(line => line.trim()).map(line => line.match(/^(\s*)/)[1].length)
7 | );
8 | return lines.map(line => line.slice(minIndent)).join("\n").trim();
9 | };
10 | export default removeIndentation;
--------------------------------------------------------------------------------
/bin/modules/dockerModule.js:
--------------------------------------------------------------------------------
1 | import { showProgressMessages } from "../dependencyInstaller.js";
2 | import { dockerMessages } from "../messages/message.js";
3 | import menu from "../MVCBuilder.js";
4 | import mvcInitializers from "../mvcInitializers.js";
5 |
6 | async function addDockerModule(projectDirPath) {
7 | await mvcInitializers.initDocker(projectDirPath);
8 |
9 | await showProgressMessages(dockerMessages);
10 |
11 | menu();
12 | }
13 |
14 | export default addDockerModule;
15 |
--------------------------------------------------------------------------------
/bin/helper/initProjectDir.js:
--------------------------------------------------------------------------------
1 | import path from "path";
2 | import * as fs from "fs/promises";
3 | import fileSelector from "inquirer-file-selector";
4 | import { input } from "@inquirer/prompts";
5 |
6 | async function selectAndCreateProjectDir(projectDirPath) {
7 | if (projectDirPath == null || projectDirPath == undefined) {
8 | projectDirPath = await fileSelector({
9 | message: "Select a directory to create project in:",
10 | type: "directory",
11 | filter: (file) => {
12 | return file.isDirectory();
13 | },
14 | });
15 | const projectName = await input({
16 | message: "Enter the project name:",
17 | default: "myNodeProject",
18 | });
19 | projectDirPath = path.join(projectDirPath, projectName);
20 | await fs.mkdir(projectDirPath, { recursive: true });
21 |
22 | return projectDirPath;
23 | }
24 | }
25 |
26 | export default selectAndCreateProjectDir;
27 |
--------------------------------------------------------------------------------
/bin/codeInserter.js:
--------------------------------------------------------------------------------
1 | import * as fs from "fs/promises";
2 | //code inserter
3 | async function insertCode(
4 | importMarker,
5 | routeMarker,
6 | filePath,
7 | importContent,
8 | routeContent,
9 | data
10 | ) {
11 | // Insert import content
12 | let importIndex = data.indexOf(importMarker);
13 | if (importIndex !== -1) {
14 | data =
15 | data.slice(0, importIndex + importMarker.length) +
16 | "\n" +
17 | importContent +
18 | data.slice(importIndex + importMarker.length);
19 | } else {
20 | console.error(`Marker "${importMarker}" not found in file.`);
21 | }
22 |
23 | // Insert route content
24 | let routeIndex = data.indexOf(routeMarker);
25 | if (routeIndex !== -1) {
26 | data =
27 | data.slice(0, routeIndex + routeMarker.length) +
28 | "\n" +
29 | routeContent +
30 | data.slice(routeIndex + routeMarker.length);
31 | } else {
32 | console.error(`Marker "${routeMarker}" not found in file.`);
33 | }
34 |
35 | // Write the modified content back to the file
36 | await fs.writeFile(filePath, data, "utf8");
37 | }
38 |
39 | export default insertCode;
40 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Elite Coders
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/bin/helper/initialize.js:
--------------------------------------------------------------------------------
1 | import mvcInitializers from "../mvcInitializers.js";
2 | import ansiColors from "ansi-colors";
3 | import menu from "../MVCBuilder.js";
4 | import {
5 | showProgressAnimation,
6 | installWithAnimation,
7 | } from "../dependencyInstaller.js";
8 | import figures from "figures";
9 | import { dependencies } from "../dependencies.js";
10 |
11 | async function initialize(projectDirPath) {
12 | try {
13 | mvcInitializers.initPackageFile(projectDirPath);
14 | await installWithAnimation(dependencies, projectDirPath);
15 | await mvcInitializers.initMainAppFile(projectDirPath);
16 | await mvcInitializers.initDbConnection(projectDirPath);
17 | await mvcInitializers.initEnv(projectDirPath);
18 | await mvcInitializers.initGitIgnore(projectDirPath);
19 | await mvcInitializers.initConstants(projectDirPath);
20 | await mvcInitializers.initHelpers(projectDirPath);
21 | await mvcInitializers.initMVC(projectDirPath);
22 |
23 | await showProgressAnimation();
24 | menu();
25 | } catch (err) {
26 | console.error(
27 | `${ansiColors.red(figures.cross)} Error during initialization2:", ${
28 | err.message
29 | }`
30 | );
31 | }
32 | }
33 |
34 | export default initialize;
35 |
--------------------------------------------------------------------------------
/bin/dependencies.js:
--------------------------------------------------------------------------------
1 | // MVC Essential Dependencies
2 |
3 | const dependencies = [
4 | { name: "bcryptjs", isDev: false },
5 | { name: "express", isDev: false },
6 | { name: "jsonwebtoken", isDev: false },
7 | { name: "connect-mongo", isDev: false },
8 | { name: "dotenv", isDev: false },
9 | { name: "express-async-handler", isDev: false },
10 | { name: "cors", isDev: false },
11 | { name: "http-status-codes", isDev: false },
12 | { name: "morgan", isDev: true },
13 | { name: "nodemon", isDev: true },
14 | ];
15 |
16 | const fileUploadDependencies = [
17 | { name: "multer", isDev: false },
18 | { name: "cloudinary", isDev: false },
19 | ];
20 |
21 | const firebaseDependencies = [
22 | { name: "firebase-admin@12.7.0", isDev: false },
23 | { name: "google-auth-library", isDev: false },
24 | { name: "googleapis", isDev: false },
25 | ];
26 |
27 | const whatsappDependencies = [{ name: "axios", isDev: false }];
28 |
29 | const nodeMailerDependencies = [{ name: "nodemailer", isDev: false }];
30 |
31 | const llmUsingOllamaDependencies = [
32 | { name: "@langchain/community", isDev: false },
33 | { name: "@langchain/core", isDev: false },
34 | { name: "@langchain/ollama", isDev: false },
35 | { name: "langchain", isDev: false },
36 | ];
37 |
38 | export {
39 | dependencies,
40 | fileUploadDependencies,
41 | firebaseDependencies,
42 | whatsappDependencies,
43 | nodeMailerDependencies,
44 | llmUsingOllamaDependencies,
45 | };
46 |
--------------------------------------------------------------------------------
/bin/modules/whatsappModule.js:
--------------------------------------------------------------------------------
1 | import mvcFileContent from "../fileContents.js";
2 | import * as fs from "fs/promises";
3 | import {
4 | installWithAnimation,
5 | showProgressMessages,
6 | } from "../dependencyInstaller.js";
7 | import { input, confirm } from "@inquirer/prompts";
8 | import { whatsappDependencies } from "../dependencies.js";
9 | import { whatsappMessages } from "../messages/message.js";
10 | import menu from "../MVCBuilder.js";
11 |
12 | async function addWhatsappModule(projectDirPath) {
13 | await installWithAnimation(whatsappDependencies, projectDirPath);
14 |
15 | let WHATSAPP_ACCESS_TOKEN = "WHATSAPP_ACCESS_TOKEN";
16 |
17 | let ans = await confirm({
18 | message: "Would you like to add your Whatsapp credentials now?",
19 | default: false,
20 | });
21 |
22 | ans = ans ? "Yes" : "No";
23 |
24 | if (ans == "Yes") {
25 | WHATSAPP_ACCESS_TOKEN = await input({
26 | message: "Enter the Whatsapp Access Token:",
27 | default: WHATSAPP_ACCESS_TOKEN,
28 | });
29 | }
30 |
31 | await fs.appendFile(
32 | `${projectDirPath}/helper/WhatsappNotification.js`,
33 | mvcFileContent.whatsappFileContent
34 | );
35 | await fs.appendFile(
36 | `${projectDirPath}/.env`,
37 | `\nWHATSAPP_URL="https://graph.facebook.com/v18.0/144528362069356/messages"\nWHATSAPP_ACCESS_TOKEN=${WHATSAPP_ACCESS_TOKEN.trim()}`
38 | );
39 |
40 | await showProgressMessages(whatsappMessages);
41 |
42 | menu();
43 | }
44 |
45 | export default addWhatsappModule;
46 |
--------------------------------------------------------------------------------
/bin/modules/nodemailerModule.js:
--------------------------------------------------------------------------------
1 | import mvcFileContent from "../fileContents.js";
2 | import * as fs from "fs/promises";
3 | import {
4 | installWithAnimation,
5 | showProgressMessages,
6 | } from "../dependencyInstaller.js";
7 | import { input, confirm } from "@inquirer/prompts";
8 | import { nodeMailerDependencies } from "../dependencies.js";
9 | import { nodeMailerMessages } from "../messages/message.js";
10 | import menu from "../MVCBuilder.js";
11 |
12 | async function addNodemailerModule(projectDirPath) {
13 | await installWithAnimation(nodeMailerDependencies, projectDirPath);
14 |
15 | let NODEMAILER_ADMIN_EMAIL = "email";
16 | let NODEMAILER_ADMIN_PASSWORD = "password";
17 |
18 | let ans = await confirm({
19 | message: "Would you like to add your Nodemailer credentials now?",
20 | default: false,
21 | });
22 |
23 | ans = ans ? "Yes" : "No";
24 |
25 | if (ans == "Yes") {
26 | NODEMAILER_ADMIN_EMAIL = await input({
27 | message: "Enter the Email:",
28 | default: NODEMAILER_ADMIN_EMAIL,
29 | });
30 |
31 | NODEMAILER_ADMIN_PASSWORD = await input({
32 | message: "Enter the Password:",
33 | default: NODEMAILER_ADMIN_PASSWORD,
34 | });
35 | }
36 |
37 | await fs.appendFile(
38 | `${projectDirPath}/helper/Nodemailer.js`,
39 | mvcFileContent.nodemailerFileContent
40 | );
41 | await fs.appendFile(
42 | `${projectDirPath}/.env`,
43 | `\nNODEMAILER_ADMIN_EMAIL=${NODEMAILER_ADMIN_EMAIL.trim()}\nNODEMAILER_ADMIN_PASSWORD=${NODEMAILER_ADMIN_PASSWORD.trim()}`
44 | );
45 |
46 | await showProgressMessages(nodeMailerMessages);
47 |
48 | menu();
49 | }
50 |
51 | export default addNodemailerModule;
52 |
--------------------------------------------------------------------------------
/bin/modules/chatModule.js:
--------------------------------------------------------------------------------
1 | import path from "path";
2 | import mvcFileContent from "../fileContents.js";
3 | import * as fs from "fs/promises";
4 | import { showProgressMessages } from "../dependencyInstaller.js";
5 | import codeInserter from "../codeInserter.js";
6 | import figures from "figures";
7 | import ansiColors from "ansi-colors";
8 | import { chatMessages } from "../messages/message.js";
9 | import menu from "../MVCBuilder.js";
10 |
11 | async function addChatModule(projectDirPath) {
12 | await fs.appendFile(
13 | `${projectDirPath}/models/Chat.js`,
14 | mvcFileContent.chatModelFileContent
15 | );
16 |
17 | await fs.appendFile(
18 | `${projectDirPath}/controllers/chatController.js`,
19 | mvcFileContent.chatControllerFileContent
20 | );
21 |
22 | //add chat routes
23 | try {
24 | await fs.writeFile(
25 | path.join(`${projectDirPath}/routes`, `chatRoutes.js`),
26 | mvcFileContent.chatRouterFileContent("Chat")
27 | );
28 |
29 | await showProgressMessages(chatMessages);
30 |
31 | // Read the file content
32 | let data = await fs.readFile(`${projectDirPath}/routes/router.js`, "utf8");
33 |
34 | const importContent = `const chatRoutes = require("./chatRoutes");`;
35 |
36 | const routeContent = `router.use("/chat", chatRoutes);`;
37 |
38 | const importMarker = "//imports here";
39 | const routeMarker = "//code here";
40 |
41 | await codeInserter(
42 | importMarker,
43 | routeMarker,
44 | `${projectDirPath}/routes/router.js`,
45 | importContent,
46 | routeContent,
47 | data
48 | );
49 | menu();
50 | } catch (err) {
51 | console.error(`${ansiColors.red(figures.cross)} Error: ${err.message}`);
52 | }
53 | }
54 |
55 | export default addChatModule;
56 |
--------------------------------------------------------------------------------
/bin/header/header.js:
--------------------------------------------------------------------------------
1 | // const chalk = require('chalk');
2 | // const figlet = require('figlet');
3 | // const boxen = require('boxen');
4 | // const gradient = require('gradient-string');
5 | import chalk from 'chalk';
6 | import figlet from 'figlet';
7 | import boxen from 'boxen';
8 | import gradient from 'gradient-string';
9 |
10 |
11 | // Helper function to create terminal hyperlinks
12 | const terminalLink = (text, url) => {
13 | return `\u001B]8;;${url}\u0007${text}\u001B]8;;\u0007`;
14 | };
15 |
16 | // Create the main title using figlet
17 | const title = figlet.textSync('SERVITECT', {
18 | font: 'Standard',
19 | horizontalLayout: 'default',
20 | verticalLayout: 'default'
21 | });
22 |
23 | // Package information with link
24 | const packageInfo = chalk.blue(
25 | terminalLink('📦 servitect', 'https://www.npmjs.com/package/servitect')
26 | );
27 | const installCmd = chalk.gray('npm install -g servitect');
28 |
29 | // Create the developer credits with GitHub links
30 | const devInfo = `Developers: ${chalk.cyan(terminalLink('Kush Kapadia', 'https://github.com/kushkapadia'))} | ${chalk.cyan(terminalLink('Mit Shah', 'https://github.com/mitshah2406'))} | ${chalk.cyan(terminalLink('Atharva Jadhav', 'https://github.com/atharva884'))}`;
31 |
32 | // Create the version and other info
33 | const version = chalk.gray('v1.2.4');
34 | const subtitle = chalk.yellow('Server + Architect = Servitect');
35 |
36 | // Combine all elements
37 | const header = `
38 | ${gradient.pastel.multiline(title)}
39 |
40 | ${subtitle}
41 |
42 | ${packageInfo}
43 | ${installCmd}
44 |
45 | ${devInfo}
46 | ${version}
47 | `;
48 |
49 | // Create a box around everything
50 | const boxedHeader = boxen(header, {
51 | padding: 1,
52 | margin: 1,
53 | borderStyle: 'double',
54 | borderColor: 'cyan',
55 | backgroundColor: '#1B1B1B'
56 | });
57 |
58 |
59 | export default boxedHeader;
--------------------------------------------------------------------------------
/bin/modules/ollamaModule.js:
--------------------------------------------------------------------------------
1 | import { llmUsingOllamaDependencies } from "../dependencies.js";
2 | import {
3 | installWithAnimation,
4 | showProgressMessages,
5 | } from "../dependencyInstaller.js";
6 | import mvcFileContent from "../fileContents.js";
7 | import menu from "../MVCBuilder.js";
8 | import codeInserter from "../codeInserter.js";
9 | import figures from "figures";
10 | import { llmUsingOllamaMessages } from "../messages/message.js";
11 | import ansiColors from "ansi-colors";
12 | import * as fs from "fs/promises";
13 | import path from "path";
14 |
15 | async function addLLMUsingOllamaModule(projectDirPath) {
16 | try {
17 | await installWithAnimation(llmUsingOllamaDependencies, projectDirPath);
18 | await fs.writeFile(
19 | path.join(`${projectDirPath}/routes`, `llmRoutes.js`),
20 | mvcFileContent.llmUsingOllamaRouterContent
21 | );
22 |
23 | // Read the file content
24 | let data = await fs.readFile(`${projectDirPath}/routes/router.js`, "utf8");
25 |
26 | const importContent = `const llmUsingOllamaRoutes = require("./llmRoutes");`;
27 |
28 | const routeContent = `router.use("/llm", llmUsingOllamaRoutes);`;
29 |
30 | const importMarker = "//imports here";
31 |
32 | const routeMarker = "//code here";
33 |
34 | await fs.appendFile(
35 | `${projectDirPath}/helper/LlmHelperOllama.js`,
36 | mvcFileContent.llmUsingOllamaHelperFileContent
37 | );
38 |
39 | await codeInserter(
40 | importMarker,
41 | routeMarker,
42 | `${projectDirPath}/routes/router.js`,
43 | importContent,
44 | routeContent,
45 | data
46 | );
47 |
48 | await showProgressMessages(llmUsingOllamaMessages);
49 |
50 | menu();
51 | } catch (err) {
52 | console.error(`${ansiColors.red(figures.cross)} Error: ${err.message}`);
53 | }
54 | }
55 |
56 | export default addLLMUsingOllamaModule;
57 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "servitect",
3 | "version": "1.2.7",
4 | "description": "servitect stands for Server-Architect. A one-click Node.js MVC Server generator",
5 | "main": "./bin/MVCBuilder.js",
6 | "type": "module",
7 | "scripts": {
8 | "test": "echo \"Error: no test specified\" && exit 1",
9 | "build": "node ./bin/MVCBuilder.js",
10 | "postinstall": "node ./bin/postinstall.js"
11 | },
12 | "bin": {
13 | "servitect": "./bin/MVCBuilder.js"
14 | },
15 | "repository": {
16 | "type": "git",
17 | "url": "git+https://github.com/kushkapadia/MVCGenerator.git"
18 | },
19 | "keywords": [
20 |
21 | "MVC", "server-generator", "backend-generator", "nodejs-server",
22 | "expressjs-generator", "mongodb-backend", "jwt-authentication",
23 | "firebase-integration", "nodemailer", "whatsapp-cloud-api",
24 | "dockerized-app", "actor-model", "menu-driven-tool",
25 | "llm", "scalable-backend", "modern-backend",
26 | "fullstack-nodejs", "cli-backend-generator", "developer-tool",
27 | "backend-automation", "npm-tool", "open-source"
28 |
29 | ],
30 | "author": "Elite Coders",
31 | "license": "ISC",
32 | "bugs": {
33 | "url": "https://github.com/kushkapadia/MVCGenerator/issues"
34 | },
35 | "homepage": "https://github.com/kushkapadia/MVCGenerator#readme",
36 | "dependencies": {
37 | "@inquirer/prompts": "^7.2.1",
38 | "ansi-colors": "^4.1.3",
39 | "ansi-styles": "^6.2.1",
40 | "boxen": "^8.0.1",
41 | "chalk": "^5.4.1",
42 | "figlet": "^1.8.0",
43 | "figures": "^6.1.0",
44 | "firebase-admin": "^13.0.2",
45 | "google-auth-library": "^9.15.0",
46 | "googleapis": "^144.0.0",
47 | "gradient-string": "^3.0.0",
48 | "inquirer-file-selector": "^0.6.1",
49 | "is-installed-globally": "^1.0.0",
50 | "ora": "^8.1.1",
51 | "progress": "^2.0.3",
52 | "servitect": "file:",
53 | "terminal-link": "^3.0.0"
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/bin/dependencyInstaller.js:
--------------------------------------------------------------------------------
1 | import ora from "ora";
2 | import chalk from "chalk";
3 | import { exec } from "child_process";
4 | import figures from "figures";
5 | import { progressMessages } from "./messages/message.js";
6 |
7 | const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
8 |
9 | // For Initialization
10 | async function showProgressAnimation() {
11 | const spinner = ora("Setting up your project...").start();
12 | for (const message of progressMessages) {
13 | // Clear the line and move cursor to start
14 | spinner.text = chalk.cyan(`${chalk.cyan(figures.tick)} ${message}`);
15 | await sleep(500);
16 | }
17 | // Stop the spinner and show completion
18 | spinner.stop();
19 | console.log(
20 | chalk.greenBright(`${chalk.greenBright(figures.tick)} Configuration completed!`)
21 | );
22 | }
23 |
24 | // For Messages
25 | async function showProgressMessages(messages) {
26 | const spinner = ora("Starting...").start();
27 | for (const message of messages) {
28 | // Clear the line and move cursor to start
29 | spinner.text = chalk.cyan(`${message}`);
30 | await sleep(500);
31 | }
32 | // Stop the spinner and show completion
33 | spinner.stop();
34 | console.log(chalk.green(`${figures.tick} Done`));
35 | }
36 |
37 | async function installWithAnimation(dependencies, dir) {
38 | const spinner = ora("Installing dependencies...").start();
39 | for (const dep of dependencies) {
40 | const { name, isDev } = dep;
41 | // Update spinner text
42 | spinner.text = chalk.cyanBright(`Installing ${name}...`);
43 |
44 | const command = isDev ? `npm install -D ${name}` : `npm install ${name}`;
45 |
46 | try {
47 | await new Promise((resolve, reject) => {
48 | exec(command, { cwd: dir }, (error) => {
49 | if (error) reject(error);
50 | else resolve();
51 | });
52 | });
53 | await sleep(300);
54 | } catch (error) {
55 | spinner.stop();
56 | console.error(chalk.red(`\nFailed to install ${name}`));
57 | throw error;
58 | }
59 | }
60 | // Stop the spinner and show completion
61 | spinner.stop();
62 | console.log(
63 | chalk.greenBright(`${chalk.greenBright(figures.tick)} Package installation completed!`)
64 | );
65 | }
66 |
67 | export { showProgressAnimation, installWithAnimation, showProgressMessages };
68 |
--------------------------------------------------------------------------------
/bin/messages/message.js:
--------------------------------------------------------------------------------
1 | // Initialization Messages
2 | const progressMessages = [
3 | "constants folder created successfully.",
4 | "helper folder created successfully.",
5 | "models folder created successfully.",
6 | "controllers folder created successfully.",
7 | "routes folder created successfully.",
8 | "Env file created successfully.",
9 | "Git Ignore file created successfully.",
10 | "messages.js file created successfully.",
11 | "JsonResponse.js file created successfully.",
12 | "JWTAuthHelper.js file created successfully.",
13 | "TryCatch.js file created successfully.",
14 | ];
15 |
16 | const actorMessages = [
17 | "Actor Contoller File Created Successfully.",
18 | "Actor Router file created successfully.",
19 | "Actor Model file created successfully.",
20 | ];
21 |
22 | const nonActorMessages = [
23 | "Non Actor Contoller File Created Successfully.",
24 | "Non Actor Router file created successfully.",
25 | "Non Actor Model file created successfully.",
26 | ];
27 |
28 | const chatMessages = [
29 | "Chat Router file created successfully.",
30 | "Chat Contoller File Created Successfully.",
31 | "Chat Model file created successfully.",
32 | ];
33 |
34 | const fileUploadMessages = [
35 | "FileUpload Route file created successfully.",
36 | "FileUpload Controller file created successfully.",
37 | "FileUpload Multer middleware created successfully.",
38 | "FileUpload Helper file created successfully.",
39 | "Public & uploads folder created successfully.",
40 | ];
41 |
42 | const firebaseMessages = [
43 | "Firebase Route file created successfully.",
44 | "Firebase Controller file created successfully.",
45 | "Firebase-key.json file created successfully.",
46 | ];
47 |
48 | const whatsappMessages = [
49 | "WhatsApp Helper file created successfully.",
50 | "Added url & Access token in .env file.",
51 | ];
52 |
53 | const nodeMailerMessages = [
54 | "NodeMailer Helper file created successfully.",
55 | "Added email & password in .env file.",
56 | ];
57 |
58 | const dockerMessages = ["Basic Docker file created successfully."];
59 |
60 | const llmUsingOllamaMessages = [
61 | "LLM Using Ollama Routes Added.",
62 | "LLM Using Ollama Helper.js created successfully."
63 | ]
64 | export {
65 | progressMessages,
66 | actorMessages,
67 | nonActorMessages,
68 | chatMessages,
69 | fileUploadMessages,
70 | firebaseMessages,
71 | whatsappMessages,
72 | nodeMailerMessages,
73 | dockerMessages,
74 | llmUsingOllamaMessages
75 | };
76 |
--------------------------------------------------------------------------------
/bin/modules/uploadModule.js:
--------------------------------------------------------------------------------
1 | import path from "path";
2 | import mvcFileContent from "../fileContents.js";
3 | import * as fs from "fs/promises";
4 | import {
5 | installWithAnimation,
6 | showProgressMessages,
7 | } from "../dependencyInstaller.js";
8 | import codeInserter from "../codeInserter.js";
9 | import figures from "figures";
10 | import { input, confirm } from "@inquirer/prompts";
11 | import ansiColors from "ansi-colors";
12 | import { fileUploadDependencies } from "../dependencies.js";
13 | import { fileUploadMessages } from "../messages/message.js";
14 | import menu from "../MVCBuilder.js";
15 |
16 | async function addUploadModule(projectDirPath) {
17 | await installWithAnimation(fileUploadDependencies, projectDirPath);
18 |
19 | let CLOUD_NAME = "myCloudName";
20 | let API_KEY = "myApiKey";
21 | let API_SECRET = "myApiSecret";
22 |
23 | let ans = await confirm({
24 | message: "Would you like to add your cloudinary credentials now?",
25 | default: false,
26 | });
27 |
28 | ans = ans ? "Yes" : "No";
29 |
30 | if (ans == "Yes") {
31 | CLOUD_NAME = await input({
32 | message: "Enter the Cloudinary Cloud Name:",
33 | default: CLOUD_NAME,
34 | });
35 |
36 | API_KEY = await input({
37 | message: "Enter the Cloudinary API Key:",
38 | default: API_KEY,
39 | });
40 |
41 | API_SECRET = await input({
42 | message: "Enter the Cloudinary API Secret:",
43 | default: API_SECRET,
44 | });
45 | }
46 |
47 | await fs.appendFile(
48 | `${projectDirPath}/.env`,
49 | `\nCLOUDINARY_CLOUD_NAME=${CLOUD_NAME.trim()}\nCLOUDINARY_API_KEY=${API_KEY.trim()}\nCLOUDINARY_API_SECRET=${API_SECRET.trim()}`
50 | );
51 |
52 | await createFileUploadRoutes(projectDirPath);
53 |
54 | await fs.appendFile(
55 | `${projectDirPath}/helper/cloudinary.js`,
56 | mvcFileContent.cloudinaryHelperFileContent
57 | );
58 |
59 | const middlewareDir = path.join(projectDirPath, "middleware");
60 | await fs.mkdir(middlewareDir, { recursive: true });
61 | await fs.appendFile(
62 | `${projectDirPath}/middleware/multer.js`,
63 | mvcFileContent.uploadMiddlewareFileContent
64 | );
65 |
66 | await fs.appendFile(
67 | `${projectDirPath}/controllers/uploadController.js`,
68 | mvcFileContent.uploadControllerFile
69 | );
70 |
71 | const publicDir = path.join(projectDirPath, "public");
72 | await fs.mkdir(publicDir, { recursive: true });
73 | const uploadsDir = path.join(publicDir, "uploads");
74 | await fs.mkdir(uploadsDir, { recursive: true });
75 |
76 | await showProgressMessages(fileUploadMessages);
77 |
78 | menu();
79 | }
80 |
81 | async function createFileUploadRoutes(projectDirPath) {
82 | try {
83 | await fs.writeFile(
84 | path.join(`${projectDirPath}/routes`, `fileUploadRoutes.js`),
85 | mvcFileContent.fileUploadRouterFileContent("FileUpload")
86 | );
87 |
88 | // Read the file content
89 | let data = await fs.readFile(`${projectDirPath}/routes/router.js`, "utf8");
90 |
91 | const importContent = `const fileUploadRoutes = require("./fileUploadRoutes");`;
92 |
93 | const routeContent = `router.use("/fileUpload", fileUploadRoutes);`;
94 |
95 | const importMarker = "//imports here";
96 | const routeMarker = "//code here";
97 |
98 | await codeInserter(
99 | importMarker,
100 | routeMarker,
101 | `${projectDirPath}/routes/router.js`,
102 | importContent,
103 | routeContent,
104 | data
105 | );
106 | } catch (err) {
107 | console.error(`${ansiColors.red(figures.cross)} Error: ${err.message}`);
108 | }
109 | }
110 |
111 | export default addUploadModule;
112 |
--------------------------------------------------------------------------------
/bin/modules/fcmModule.js:
--------------------------------------------------------------------------------
1 | import path from "path";
2 | import mvcFileContent from "../fileContents.js";
3 | import * as fs from "fs/promises";
4 | import {
5 | installWithAnimation,
6 | showProgressMessages,
7 | } from "../dependencyInstaller.js";
8 | import codeInserter from "../codeInserter.js";
9 | import figures from "figures";
10 | import { input, confirm } from "@inquirer/prompts";
11 | import ansiColors from "ansi-colors";
12 | import { firebaseDependencies } from "../dependencies.js";
13 | import { firebaseMessages } from "../messages/message.js";
14 | import removeIndentation from "../fileFormatter.js";
15 | import menu from "../MVCBuilder.js";
16 |
17 | async function addFirebaseModule(projectDirPath) {
18 | try {
19 | await installWithAnimation(firebaseDependencies, projectDirPath);
20 |
21 | let PROJECT_ID = "project_id";
22 |
23 | let ans = await confirm({
24 | message: "Would you like to add your firebase credentials now?",
25 | default: false,
26 | });
27 |
28 | ans = ans ? "Yes" : "No";
29 |
30 | if (ans == "Yes") {
31 | PROJECT_ID = await input({
32 | message: "Enter the Project ID of firebase project:",
33 | default: PROJECT_ID,
34 | });
35 | }
36 |
37 | // Read the file content
38 | let data = await fs.readFile(`${projectDirPath}/app.js`, "utf8");
39 |
40 | const importContent = `var admin = require("firebase-admin");`;
41 | const routeContent = `//firebase init\nprocess.env.GOOGLE_APPLICATION_CREDENTIALS;\nadmin.initializeApp({
42 | credential: admin.credential.applicationDefault(),
43 | projectId: "${PROJECT_ID.trim()}",
44 | });`;
45 | const importMarker = "//imports here";
46 | const routeMarker = "//code here";
47 |
48 | await codeInserter(
49 | importMarker,
50 | routeMarker,
51 | `${projectDirPath}/app.js`,
52 | importContent,
53 | routeContent,
54 | data
55 | );
56 | await createFirebaseRoutes(projectDirPath);
57 |
58 | await fs.appendFile(
59 | `${projectDirPath}/firebase-key.json`,
60 | `{
61 | "message": "PASTE YOUR copied contents here"
62 | } `
63 | );
64 | console.log(
65 | removeIndentation(
66 | `
67 | ⭐ ${ansiColors.magenta.bold(
68 | "Firebase Private Key Added to Environment Variables"
69 | )} ⭐
70 |
71 | • Create a private key file.
72 | • Create a Firebase project.
73 | • Go to ${ansiColors.yellow.bold(
74 | "🛠️ Settings"
75 | )} -> ${ansiColors.yellow.bold(
76 | "⛅ Cloud Messaging Tab"
77 | )} and enable it.
78 | • Navigate to ${ansiColors.yellow.bold(
79 | "Service Accounts Tab"
80 | )} and generate a ${ansiColors.yellow.bold("🔐 Private Key")}.
81 | • Copy the content of the generated file into a file named ${ansiColors.yellow.bold(
82 | "📂 firebase-key.json"
83 | )}.
84 | `
85 | )
86 | );
87 | await fs.appendFile(
88 | `${projectDirPath}/.env`,
89 | '\nGOOGLE_APPLICATION_CREDENTIALS="firebase-key.json"'
90 | );
91 | await fs.appendFile(
92 | `${projectDirPath}/controllers/firebaseController.js`,
93 | mvcFileContent.firebaseControllerFile
94 | );
95 |
96 | await showProgressMessages(firebaseMessages);
97 |
98 | menu();
99 | } catch (err) {
100 | console.error(`${ansiColors.red(figures.cross)} Error: ${err.message}`);
101 | }
102 | }
103 |
104 | async function createFirebaseRoutes(projectDirPath) {
105 | try {
106 | await fs.writeFile(
107 | path.join(`${projectDirPath}/routes`, `firebaseRoutes.js`),
108 | mvcFileContent.firebaseRouterFileContent("Firebase")
109 | );
110 | let data = await fs.readFile(`${projectDirPath}/routes/router.js`, "utf8");
111 |
112 | const importContent = `const firebaseRoutes = require("./firebaseRoutes");`;
113 |
114 | const routeContent = `router.use("/firebase", firebaseRoutes);`;
115 |
116 | const importMarker = "//imports here";
117 | const routeMarker = "//code here";
118 |
119 | await codeInserter(
120 | importMarker,
121 | routeMarker,
122 | `${projectDirPath}/routes/router.js`,
123 | importContent,
124 | routeContent,
125 | data
126 | );
127 | } catch (err) {
128 | console.error(`${ansiColors.red(figures.cross)} Error: ${err.message}`);
129 | }
130 | }
131 |
132 | export default addFirebaseModule;
133 |
--------------------------------------------------------------------------------
/bin/mvcInitializers.js:
--------------------------------------------------------------------------------
1 | // const fs = require("fs/promises")
2 | import * as fs from "fs/promises";
3 | import { exit } from "process";
4 | import path from "path";
5 | import readline from "readline";
6 | import { exec } from "child_process";
7 | import mvcFileContent from "./fileContents.js";
8 | import { initializeReadline } from "./readlineInterface.js";
9 | import { input, number } from "@inquirer/prompts";
10 | import ansiColors from "ansi-colors";
11 | import figures, { mainSymbols, fallbackSymbols, replaceSymbols } from "figures";
12 | import chalk from "chalk";
13 |
14 | // initializer functions
15 | let rl = initializeReadline();
16 | const initializers = {
17 | initDbConnection: async function (projectDirPath) {
18 | await fs.appendFile(
19 | `${projectDirPath}/db.js`,
20 | mvcFileContent.dbFileContent
21 | );
22 | },
23 | initMainAppFile: async function (projectDirPath) {
24 | let routePrefix = await input({
25 | message: "Enter the route prefix:",
26 | default: "/",
27 | });
28 | routePrefix = routePrefix.trim();
29 | const isValidRoute = /^\/[a-zA-Z0-9-_]*$/.test(routePrefix);
30 |
31 | if (!isValidRoute || !routePrefix) {
32 | console.log(
33 | ansiColors.cyan(
34 | `${figures.info} Invalid route prefix entered. Using Default Route Prefix.`
35 | )
36 | );
37 | routePrefix = "/";
38 | }
39 |
40 | await fs.appendFile(
41 | `${projectDirPath}/app.js`,
42 | mvcFileContent.appFileContent(routePrefix)
43 | );
44 | },
45 | initEnv: async function (projectDirPath) {
46 | const PORT = await number({
47 | message: "Enter port number you want to use:",
48 | default: 4000,
49 | min: 1024,
50 | max: 65535,
51 | });
52 |
53 | const CONNECTION_STRING = await input({
54 | message: "Enter the mongodb connection string:",
55 | default: "",
56 | });
57 | await fs.appendFile(
58 | `${projectDirPath}/.env`,
59 | mvcFileContent.envFileContent(PORT, CONNECTION_STRING)
60 | );
61 | },
62 |
63 | initGitIgnore: async function (projectDirPath) {
64 | await fs.appendFile(
65 | `${projectDirPath}/.gitignore`,
66 | mvcFileContent.gitIgnoreFileContent
67 | );
68 | },
69 |
70 | initPackageFile: async function (projectDirPath) {
71 | await fs.appendFile(
72 | `${projectDirPath}/package.json`,
73 | mvcFileContent.packageJsonFileContent
74 | );
75 | },
76 |
77 | initConstants: async function (projectDirPath) {
78 | const constantDir = path.join(projectDirPath, `constants`);
79 | await fs.mkdir(constantDir, { recursive: true });
80 | await fs.writeFile(
81 | path.join(constantDir, "Message.js"),
82 | mvcFileContent.messageFileContent
83 | );
84 | },
85 |
86 | initMVC: async function (projectDirPath) {
87 | const modelsDir = path.join(projectDirPath, "models");
88 | await fs.mkdir(modelsDir, { recursive: true });
89 | const controllersDir = path.join(projectDirPath, "controllers");
90 | await fs.mkdir(controllersDir, { recursive: true });
91 |
92 | const routesDir = path.join(projectDirPath, "routes");
93 | await fs.mkdir(routesDir, { recursive: true });
94 | await fs.writeFile(
95 | `${projectDirPath}/routes/router.js`,
96 | mvcFileContent.routerFileContent
97 | );
98 | },
99 | initHelpers: async function (projectDirPath) {
100 | const helperDir = path.join(projectDirPath, "helper");
101 | await fs.mkdir(helperDir, { recursive: true });
102 |
103 | await fs.writeFile(
104 | path.join(helperDir, "JsonResponse.js"),
105 | mvcFileContent.JsonResponseFileContent
106 | );
107 |
108 | await fs.writeFile(
109 | path.join(helperDir, "JWTAuthHelper.js"),
110 | mvcFileContent.JWTAuthHelperFileContent
111 | );
112 |
113 | await fs.writeFile(
114 | path.join(helperDir, "TryCatch.js"),
115 | mvcFileContent.tryCatchFileContent
116 | );
117 | },
118 | initDocker: async function (projectDirPath) {
119 | const dirName = "initialImage";
120 | const tagName = "v1";
121 | await fs.appendFile(
122 | `${projectDirPath}/Dockerfile`,
123 | mvcFileContent.dockerFileContent
124 | );
125 | console.log(`1) To build image : docker build -t ${dirName}:${tagName}.`);
126 | console.log(
127 | `2) To use image (container invocation) : docker run -it -p 4000:4000 --rm ${dirName}:${tagName}`
128 | );
129 | console.log("3) View Images: docker images");
130 | console.log("4) View Containers(all): docker ps -a");
131 | console.log(`5) Delete Image: docker rmi ${dirName}:${tagName}`);
132 | console.log(`6) Remove Container: docker rm "containerName"`);
133 | },
134 | };
135 |
136 | export default initializers;
137 |
--------------------------------------------------------------------------------
/bin/modules/nonActorModule.js:
--------------------------------------------------------------------------------
1 | import path from "path";
2 | import mvcFileContent from "../fileContents.js";
3 | import * as fs from "fs/promises";
4 | import { showProgressMessages } from "../dependencyInstaller.js";
5 | import figures from "figures";
6 | import { input, confirm } from "@inquirer/prompts";
7 | import ansiColors from "ansi-colors";
8 | import { nonActorMessages } from "../messages/message.js";
9 | import menu from "../MVCBuilder.js";
10 | import codeInserter from "../codeInserter.js";
11 |
12 | async function createModel(
13 | content,
14 | ModelFileContent,
15 | nonActorAttributes,
16 | projectDirPath
17 | ) {
18 | content = "";
19 | ModelFileContent = "";
20 | nonActorAttributes = "";
21 | let modelName = await input({
22 | message: "Enter the Name of the Entity MODEL:",
23 | });
24 |
25 | if (modelName.length === 0 || modelName.trim() === "") {
26 | console.log(ansiColors.red(`${figures.cross} Model name cannot be empty.`));
27 | await createModel(
28 | content,
29 | ModelFileContent,
30 | nonActorAttributes,
31 | projectDirPath
32 | );
33 | }
34 |
35 | if (modelName.charAt(0) !== modelName.charAt(0).toUpperCase()) {
36 | console.log(
37 | ansiColors.yellow(
38 | `${figures.cross} Model name must start with a capital letter. Using name as `
39 | ) +
40 | ansiColors.green(
41 | `${modelName.charAt(0).toUpperCase()}${modelName.slice(1)}`
42 | )
43 | );
44 | modelName = modelName.charAt(0).toUpperCase() + modelName.slice(1);
45 | }
46 |
47 | await askForNonActorAttributes(
48 | modelName,
49 | nonActorAttributes,
50 | projectDirPath,
51 | ModelFileContent
52 | );
53 | }
54 |
55 | async function askForNonActorAttributes(
56 | modelName,
57 | nonActorAttributes,
58 | projectDirPath,
59 | ModelFileContent
60 | ) {
61 | let ans = await confirm({
62 | message: "Do you want to Add an attribute?",
63 | default: false,
64 | });
65 |
66 | ans = ans ? "Yes" : "No";
67 |
68 | switch (ans) {
69 | case "Yes":
70 | const attributeName = await input({
71 | message: "Enter the Attribute Name:",
72 | });
73 | nonActorAttributes += `${attributeName}: this.data.${attributeName},\n`;
74 | await askForNonActorAttributes(
75 | modelName,
76 | nonActorAttributes,
77 | projectDirPath,
78 | ModelFileContent
79 | );
80 |
81 | break;
82 | case "No":
83 | ModelFileContent += mvcFileContent.nonActorModelFileContent(
84 | modelName,
85 | nonActorAttributes
86 | );
87 |
88 | await addNonActorRoutes(modelName, projectDirPath);
89 | await createNonActorController(modelName, projectDirPath);
90 | await fs.appendFile(
91 | `${projectDirPath}/models/${modelName}.js`,
92 | `${ModelFileContent}`
93 | );
94 |
95 | await showProgressMessages(nonActorMessages);
96 |
97 | menu();
98 | break;
99 | default:
100 | console.log(
101 | ansiColors.red(
102 | `${figures.cross} Invalid Input. Please Enter Valid Input\n`
103 | )
104 | );
105 | await askForNonActorAttributes(
106 | modelName,
107 | nonActorAttributes,
108 | projectDirPath,
109 | ModelFileContent
110 | ); // recursive call to ask again
111 | break;
112 | }
113 | }
114 |
115 | async function createNonActorController(modelname, projectDirPath) {
116 | await fs.appendFile(
117 | `${projectDirPath}/controllers/${modelname.toLowerCase()}Controller.js`,
118 | mvcFileContent.nonActorControllerFileContent(modelname)
119 | );
120 | }
121 |
122 | async function addNonActorRoutes(modelName, projectDirPath) {
123 | try {
124 | // Create the folder in routes
125 | // Create file
126 | await fs.writeFile(
127 | path.join(
128 | `${projectDirPath}/routes`,
129 | `${modelName.toLowerCase()}Routes.js`
130 | ),
131 | mvcFileContent.nonActorRouterFileContent(modelName)
132 | );
133 | const data = await fs.readFile(
134 | `${projectDirPath}/routes/router.js`,
135 | "utf8"
136 | );
137 |
138 | const importContent = `const ${modelName.toLowerCase()}Routes = require("./${modelName.toLowerCase()}Routes");`;
139 |
140 | const routeContent = `router.use("/${modelName.toLowerCase()}", ${modelName.toLowerCase()}Routes);`;
141 |
142 | const importMarker = "//imports here";
143 | const routeMarker = "//code here";
144 |
145 | await codeInserter(
146 | importMarker,
147 | routeMarker,
148 | `${projectDirPath}/routes/router.js`,
149 | importContent,
150 | routeContent,
151 | data
152 | );
153 | } catch (err) {
154 | console.error(`${ansiColors.red(figures.cross)} Error: ${err.message}`);
155 | }
156 | }
157 |
158 | export default createModel;
159 |
--------------------------------------------------------------------------------
/bin/prompts/menuPrompt.js:
--------------------------------------------------------------------------------
1 | import { select, Separator } from "@inquirer/prompts";
2 | import ansiColors from "ansi-colors";
3 | import process from "process";
4 | import figureSet from "figures";
5 |
6 | async function promptUser() {
7 | try {
8 | const answer = await select({
9 | theme: {
10 | prefix: ansiColors.green(figureSet.nodejs),
11 | style: {
12 | highlight: (text) => ansiColors.cyanBright.underline(text),
13 | message: (text) => ansiColors.yellow.bold(text),
14 | },
15 | },
16 | message: ansiColors.magentaBright.italic("\n\nSelect your choice"),
17 | pageSize: 11,
18 | loop: false,
19 | default: "1",
20 | choices: [
21 | {
22 | name: "Initialize MVC",
23 | value: "1",
24 | description: ansiColors.gray(
25 | "Use this to initialize a new MVC project"
26 | ),
27 | },
28 | {
29 | name: "Create New Actor Model",
30 | value: "2",
31 | description: ansiColors.gray(
32 | "Use this to create a new actor model (First Letter Uppercase) (eg- User, Admin,etc)"
33 | ),
34 | },
35 | {
36 | name: "Create New Entity Model",
37 | value: "3",
38 | description: ansiColors.gray(
39 | "Use this to create a new entity model (First Letter Uppercase) (eg - Book, Product,etc)"
40 | ),
41 | },
42 | {
43 | name: "Add Chat Module",
44 | value: "4",
45 | description: ansiColors.gray("Use this to create a chat module"),
46 | },
47 | {
48 | name: "Add File Upload Module",
49 | value: "5",
50 | description: ansiColors.gray(
51 | "Use this to create a file upload module"
52 | ),
53 | },
54 | {
55 | name: "Add Firebase For Firebase Cloud Messaging",
56 | value: "6",
57 | description: ansiColors.gray(
58 | "Use this to create API routes for Firebase Cloud Messaging (FCM) for mobile push notifications"
59 | ),
60 | },
61 | {
62 | name: "Add Whatsapp Bot Messaging",
63 | value: "7",
64 | description: ansiColors.gray(
65 | "Use this to create API routes for Whatsapp Bot Messaging"
66 | ),
67 | },
68 | {
69 | name: "Add Nodemailer",
70 | value: "8",
71 | description: ansiColors.gray(
72 | "Use this to add Nodemailer functionality for sending emails"
73 | ),
74 | },
75 | {
76 | name: "Add Docker Setup",
77 | value: "9",
78 | description: ansiColors.gray(
79 | "Use this to add Docker setup for containerization"
80 | ),
81 | },
82 | {
83 | name: "Add Large Language Model (LLM) Implementation",
84 | value: "10",
85 | description: ansiColors.gray(
86 | "Use this to add LLM setup for text generation"
87 | ),
88 | },
89 | new Separator(),
90 | {
91 | name: ansiColors.red.bold("Exit"),
92 | value: "11",
93 | description: ansiColors.gray("Use this to exit the CLI"),
94 | },
95 | ],
96 | });
97 |
98 | return answer;
99 | } catch (error) {
100 | if (error instanceof Error && error.name === "ExitPromptError") {
101 | return "10";
102 | } else {
103 | return "10";
104 | }
105 | }
106 | }
107 |
108 | async function llmSubMenuPrompt() {
109 | const llmOption = await select({
110 | theme: {
111 | prefix: ansiColors.green(figureSet.nodejs),
112 | style: {
113 | highlight: (text) => ansiColors.cyanBright.underline(text),
114 | message: (text) => ansiColors.yellow.bold(text),
115 | },
116 | },
117 | message: ansiColors.magentaBright.italic(
118 | "\n\nSelect the LLM implementation"
119 | ),
120 | choices: [
121 | {
122 | name: "Using Ollama",
123 | value: "ollama",
124 | description: ansiColors.gray("Use Ollama for text generation"),
125 | },
126 | {
127 | name: "Using OpenAI API",
128 | value: "openai",
129 | description: ansiColors.gray("Use OpenAI for text generation"),
130 | },
131 | new Separator(),
132 | {
133 | name: ansiColors.red.bold("Go Back"),
134 | value: "back",
135 | description: ansiColors.gray("Return to the main menu"),
136 | },
137 | ],
138 | });
139 | return llmOption;
140 | }
141 | // Handle Ctrl+C signal
142 | process.on("SIGINT", () => {
143 | console.log(ansiColors.red.bold("\n\nExiting... Goodbye!"));
144 | process.exit(0); // Exit the process cleanly
145 | });
146 |
147 | export default promptUser;
148 | export { llmSubMenuPrompt };
149 |
--------------------------------------------------------------------------------
/bin/modules/actorModule.js:
--------------------------------------------------------------------------------
1 | import path from "path";
2 | import mvcFileContent from "../fileContents.js";
3 | import * as fs from "fs/promises";
4 | import { showProgressMessages } from "../dependencyInstaller.js";
5 | import codeInserter from "../codeInserter.js";
6 | import figures from "figures";
7 | import { input, confirm } from "@inquirer/prompts";
8 | import ansiColors from "ansi-colors";
9 | import { actorMessages } from "../messages/message.js";
10 | import menu from "../MVCBuilder.js";
11 |
12 | async function createActorModel(
13 | content,
14 | attributes,
15 | actorModelFileContent,
16 | ModelFileContent,
17 | projectDirPath
18 | ) {
19 | try {
20 | content = "";
21 | attributes = "";
22 | actorModelFileContent = "";
23 | ModelFileContent = "";
24 | let modelName = await input({
25 | message: "Enter the Name of the ACTOR MODEL:",
26 | });
27 |
28 | if (modelName.length === 0 || modelName.trim() === "") {
29 | console.log(
30 | ansiColors.red(`${figures.cross} Model name cannot be empty.`)
31 | );
32 | await createActorModel(
33 | content,
34 | attributes,
35 | actorModelFileContent,
36 | ModelFileContent
37 | );
38 | }
39 |
40 | if (modelName.charAt(0) !== modelName.charAt(0).toUpperCase()) {
41 | console.log(
42 | ansiColors.yellow(
43 | `${figures.warning} Model name must start with a capital letter. Using name as `
44 | ) +
45 | ansiColors.green(
46 | `${modelName.charAt(0).toUpperCase()}${modelName.slice(1)}`
47 | )
48 | );
49 | modelName = modelName.charAt(0).toUpperCase() + modelName.slice(1);
50 | }
51 |
52 | await askForAttributes(
53 | modelName,
54 | projectDirPath,
55 | attributes,
56 | actorModelFileContent
57 | );
58 | } catch (err) {
59 | console.error(`${ansiColors.red(figures.cross)} Error:`, err.message);
60 | }
61 | }
62 |
63 | async function askForAttributes(
64 | modelName,
65 | projectDirPath,
66 | attributes,
67 | actorModelFileContent
68 | ) {
69 | let ans = await confirm({
70 | message: "Do you want to Add an attribute?",
71 | default: false,
72 | });
73 |
74 | ans = ans ? "Yes" : "No";
75 |
76 | switch (ans) {
77 | case "Yes":
78 | const attributeName = await input({
79 | message: "Enter the Attribute Name:",
80 | });
81 | attributes += `${attributeName}: this.data.${attributeName},\n`;
82 | askForAttributes(
83 | modelName,
84 | projectDirPath,
85 | attributes,
86 | actorModelFileContent
87 | );
88 | break;
89 | case "No":
90 | actorModelFileContent += mvcFileContent.actorModelFileContent(
91 | modelName,
92 | attributes
93 | );
94 |
95 | await createActorControllerfile(modelName, projectDirPath);
96 | await addActorRoutes(modelName, projectDirPath);
97 | await fs.appendFile(
98 | `${projectDirPath}/models/${modelName}.js`,
99 | `${actorModelFileContent}`
100 | );
101 |
102 | await showProgressMessages(actorMessages);
103 |
104 | menu();
105 | break;
106 | default:
107 | console.log(
108 | ansiColors.red(
109 | `${figures.cross} Invalid Input. Please Enter Valid Input\n`
110 | )
111 | );
112 | await askForAttributes(
113 | modelName,
114 | projectDirPath,
115 | attributes,
116 | actorModelFileContent
117 | ); // recursive call to ask again
118 | break;
119 | }
120 | }
121 |
122 | async function addActorRoutes(modelName, projectDirPath) {
123 | try {
124 | // Create the folder in routes
125 | // Create file
126 | await fs.writeFile(
127 | path.join(
128 | `${projectDirPath}/routes`,
129 | `${modelName.toLowerCase()}Routes.js`
130 | ),
131 | mvcFileContent.actorRouterFileContent(modelName)
132 | );
133 | const data = await fs.readFile(
134 | `${projectDirPath}/routes/router.js`,
135 | "utf8"
136 | );
137 |
138 | const importContent = `const ${modelName.toLowerCase()}Routes = require("./${modelName.toLowerCase()}Routes");`;
139 |
140 | const routeContent = `router.use("/${modelName.toLowerCase()}", ${modelName.toLowerCase()}Routes);`;
141 |
142 | const importMarker = "//imports here";
143 | const routeMarker = "//code here";
144 |
145 | await codeInserter(
146 | importMarker,
147 | routeMarker,
148 | `${projectDirPath}/routes/router.js`,
149 | importContent,
150 | routeContent,
151 | data
152 | );
153 | } catch (err) {
154 | console.error(`${ansiColors.red(figures.cross)} Error: ${err.message}`);
155 | }
156 | }
157 |
158 | async function createActorControllerfile(modelname, projectDirPath) {
159 | await fs.appendFile(
160 | `${projectDirPath}/controllers/${modelname.toLowerCase()}Controller.js`,
161 | mvcFileContent.actorControllerFileContent(modelname)
162 | );
163 | }
164 |
165 | export default createActorModel;
166 |
--------------------------------------------------------------------------------
/bin/MVCBuilder.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | import { exit } from "process";
3 | import displayHeader from "./header/header.js";
4 | import promptUser, { llmSubMenuPrompt } from "./prompts/menuPrompt.js";
5 | import figures from "figures";
6 | import ansiColors from "ansi-colors";
7 | import createActorModel from "./modules/actorModule.js";
8 | import createModel from "./modules/nonActorModule.js";
9 | import addChatModule from "./modules/chatModule.js";
10 | import addUploadModule from "./modules/uploadModule.js";
11 | import addFirebaseModule from "./modules/fcmModule.js";
12 | import addWhatsappModule from "./modules/whatsappModule.js";
13 | import addNodemailerModule from "./modules/nodemailerModule.js";
14 | import addDockerModule from "./modules/dockerModule.js";
15 | import addLLMUsingOllamaModule from "./modules/ollamaModule.js";
16 | import initialize from "./helper/initialize.js";
17 | import selectAndCreateProjectDir from "./helper/initProjectDir.js";
18 |
19 | // Global Variables
20 | let projectDirPath;
21 | let content = "";
22 | let attributes = "";
23 | let nonActorAttributes = "";
24 | let actorModelFileContent = "";
25 | let ModelFileContent = "";
26 |
27 | try {
28 | projectDirPath = await selectAndCreateProjectDir(projectDirPath);
29 | } catch (error) {
30 | if (error.code === "EPERM") {
31 | console.log(
32 | ansiColors.red(
33 | `${figures.cross} Permission denied. Please try selecting another directory.`
34 | )
35 | );
36 | } else {
37 | console.log(
38 | ansiColors.red(`${figures.cross} Unexpected error: ${error.message}`)
39 | );
40 | }
41 | await selectAndCreateProjectDir(projectDirPath);
42 | }
43 |
44 | async function menu() {
45 | let answer = await promptUser();
46 | switch (answer) {
47 | case "1":
48 | try {
49 | await initialize(projectDirPath);
50 | } catch (err) {
51 | console.error(
52 | `${ansiColors.red(figures.cross)} Error during initialization1:`,
53 | err.message
54 | );
55 | }
56 | break;
57 |
58 | case "2":
59 | try {
60 | await createActorModel(
61 | content,
62 | attributes,
63 | actorModelFileContent,
64 | ModelFileContent,
65 | projectDirPath
66 | );
67 | } catch (err) {
68 | console.error(
69 | `${ansiColors.red(figures.cross)} Error creating actor model:`,
70 | err.message
71 | );
72 | }
73 | break;
74 |
75 | case "3":
76 | try {
77 | await createModel(
78 | content,
79 | ModelFileContent,
80 | nonActorAttributes,
81 | projectDirPath
82 | );
83 | } catch (err) {
84 | console.error(
85 | `${ansiColors.red(figures.cross)} Error creating model:`,
86 | err.message
87 | );
88 | }
89 | break;
90 |
91 | case "4":
92 | try {
93 | await addChatModule(projectDirPath);
94 | } catch (err) {
95 | console.error(
96 | `${ansiColors.red(figures.cross)} Error creating chat module:`,
97 | err.message
98 | );
99 | }
100 | break;
101 |
102 | case "5":
103 | try {
104 | await addUploadModule(projectDirPath);
105 | } catch (err) {
106 | console.error(
107 | `${ansiColors.red(figures.cross)} Error creating upload module:`,
108 | err.message
109 | );
110 | }
111 | break;
112 |
113 | case "6":
114 | try {
115 | console.log(projectDirPath);
116 | await addFirebaseModule(projectDirPath);
117 | } catch (err) {
118 | console.error(
119 | `${ansiColors.red(figures.cross)} Error adding firebase module:`,
120 | err.message
121 | );
122 | }
123 | break;
124 |
125 | case "7":
126 | try {
127 | await addWhatsappModule(projectDirPath);
128 | } catch (err) {
129 | console.error(
130 | `${ansiColors.red(figures.cross)} Error adding whatsapp module:`,
131 | err.message
132 | );
133 | }
134 | break;
135 |
136 | case "8":
137 | try {
138 | await addNodemailerModule(projectDirPath);
139 | } catch (err) {
140 | console.error(
141 | `${ansiColors.red(figures.cross)} Error adding nodemailer:`,
142 | err.message
143 | );
144 | }
145 | break;
146 |
147 | case "9":
148 | try {
149 | await addDockerModule(projectDirPath);
150 | } catch (err) {
151 | console.error(
152 | `${ansiColors.red(figures.cross)} Error adding docker setup:`,
153 | err.message
154 | );
155 | }
156 | break;
157 |
158 | case "10":
159 | const llmOption = await llmSubMenuPrompt();
160 |
161 | switch (llmOption) {
162 | case "ollama":
163 | try {
164 | await addLLMUsingOllamaModule(projectDirPath);
165 | } catch (err) {
166 | console.error(
167 | `${ansiColors.red(figures.cross)} Error adding Ollama LLM setup:`,
168 | err.message
169 | );
170 | }
171 | break;
172 | case "openai":
173 | console.log(
174 | "This feature will be available soon. Thank you for your patience🙏"
175 | );
176 | break;
177 |
178 | case "back":
179 | menu();
180 | break;
181 | }
182 |
183 | break;
184 |
185 | case "11":
186 | console.log(
187 | ansiColors.magenta.italic("✨HAPPY CODING - Thank You For Using✨")
188 | );
189 | exit(0);
190 | default:
191 | console.log(
192 | `${ansiColors.red(
193 | figures.cross
194 | )} Invalid Input. Please enter a valid option.\n`
195 | );
196 | menu();
197 | break;
198 | }
199 | }
200 |
201 | console.log(displayHeader);
202 |
203 | menu();
204 |
205 | export default menu;
206 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Servitect - A one-click Node.js MVC Server generator
2 |
3 | ### **Server + Architect = Servitect**
4 |
5 | Automate the creation of a well-structured Model-View-Controller (MVC) pattern for your Node.js projects with ease. `Servitect` is designed to help developers quickly scaffold a project, follow best practices, and integrate various functionalities with minimal setup.
6 |
7 |
8 |

9 |
10 |
11 | [](https://badge.fury.io/js/servitect)
12 | [](https://www.npmjs.com/package/servitect)
13 | [](https://opensource.org/licenses/MIT)
14 |
15 | ### 🤔 Why Servitect?
16 |
17 | Servitect is designed to simplify and accelerate the server development process by automating the setup of essential components for a modern backend. Here's why you should choose Servitect:
18 |
19 | - 🏎️ **Speedy Development**: Perfect for **hackathons** and **rapid prototyping**, Servitect allows you to set up a fully functional server in minutes, letting you focus on building unique features and meeting tight deadlines rather than getting stuck in setup.
20 |
21 | - 🗄️ **MongoDB Integration**: With built-in support for MongoDB, Servitect is ideal for modern applications that need a flexible and scalable NoSQL database.
22 |
23 | - ⚙️ **Customizable**: Servitect's menu-driven approach allows you to pick and choose the features you need, giving you flexibility while still automating repetitive tasks.
24 |
25 | - 🤖 **AI-Ready**: Built-in support for Large Language Models (LLMs) with LangChain integration for modern AI-powered applications.
26 |
27 | Servitect streamlines the backend creation process, allowing developers to focus on building what matters most. Whether you're a beginner or an experienced developer, Servitect provides the tools you need to jumpstart your server development!
28 |
29 | ## 🚀 **Features**
30 |
31 | `Servitect` comes with a variety of built-in features that streamline the development process:
32 |
33 |
34 |

35 |
36 |
37 |
38 | ### 1. 📁 MVC Folder Structure Generation
39 |
40 | - Automatically generates a standard Model-Controller folder structure.
41 | - Creates the directories and files for:
42 | - **Models**: MongoDB Schema definitions and database models.
43 | - **Controllers**: Handles logic and data flow between models and views.
44 | - **Routes**: Defines CRUD endpoints.
45 | - **Helper**: Utility functions and middleware.
46 | - **Constants**: Application constants and messages.
47 |
48 | ### 2. ✅ Best Practices for Scalability and Error Handling
49 |
50 | - Comes with pre-configured global error-handling mechanisms.
51 | - Implements best practices for middleware and request validation.
52 | - Follows scalable design patterns to ensure maintainability even as your application grows.
53 | - Built-in try-catch wrapper for async route handlers.
54 |
55 | ### 3. 🔒 Authentication APIs
56 |
57 | - Provides complete authentication system with JWT tokens.
58 | - Actor-based authentication system for users, admins, and custom roles.
59 | - Secure password hashing with bcryptjs.
60 | - Token verification middleware for protected routes.
61 | - Session management with configurable token expiration.
62 |
63 | ### 4. 📝 CRUD API Generation
64 |
65 | - Quickly generate Create, Read, Update, Delete (CRUD) APIs for any entity.
66 | - Support for both Actor models (with authentication) and Entity models.
67 | - Consistent API response format with success/error handling.
68 | - Automatic route registration and controller generation.
69 |
70 | ### 5. 📤 File Uploads to Cloudinary
71 |
72 | - Complete file upload system with Cloudinary integration.
73 | - Support for single file, multiple files, and field-specific uploads.
74 | - Automatic local file cleanup after cloud upload.
75 | - Built-in file deletion capabilities from Cloudinary.
76 | - Multer middleware for handling multipart/form-data.
77 |
78 | ### 6. 🟢 WhatsApp Notifications Integration
79 |
80 | - Integrates with WhatsApp Cloud API for sending notifications.
81 | - Template-based message sending system.
82 | - Easy configuration with access tokens.
83 | - Perfect for automated notifications and alerts.
84 |
85 | ### 7. 🔔 Firebase Cloud Messaging API Integration for Mobile Apps
86 |
87 | - Complete Firebase FCM setup for push notifications.
88 | - Support for sending notifications to individual devices.
89 | - Topic-based messaging for broadcasting to multiple users.
90 | - Batch notifications for sending to multiple FCM tokens.
91 | - Multi-topic notification support with condition-based targeting.
92 |
93 | ### 8. 📨 NodeMailer Integration
94 |
95 | - Full email functionality with NodeMailer.
96 | - Gmail SMTP configuration out of the box.
97 | - HTML email support for rich content.
98 | - Environment-based configuration for security.
99 |
100 | ### 9. 💬 Chat Logic with Database Model and API Creation
101 |
102 | - Ready-to-use real-time chat system.
103 | - MongoDB-based chat storage with message history.
104 | - APIs for sending messages and retrieving conversations.
105 | - Support for one-on-one conversations with message threading.
106 | - Timestamp tracking and sender identification.
107 |
108 | ### 10. 🐳 Docker Setup
109 | - Pre-configured Docker setup for containerization.
110 | - Production-ready Dockerfile with Node.js best practices.
111 | - Easy deployment with container orchestration support.
112 | - Development and production environment configurations.
113 |
114 | ### 11. 🤖 Large Language Model (LLM) Integration
115 | - **Ollama Integration**: Local LLM deployment with conversation memory.
116 | - **LangChain Framework**: Advanced prompt engineering and chain operations.
117 | - Session-based conversation history for contextual responses.
118 | - Customizable system prompts and model configurations.
119 | - Support for multiple LLM models through Ollama.
120 |
121 | ## 📦 **Installation**
122 |
123 | Install Servitect globally using npm:
124 |
125 | ```bash
126 | npm install -g servitect
127 | ```
128 |
129 | For Linux and Mac users:
130 |
131 | ```bash
132 | sudo npm install -g servitect
133 | ```
134 |
135 | ## 🏁 **Quick Start**
136 |
137 | After installation, you can create a new MVC project by running:
138 |
139 | ```bash
140 | servitect
141 | ```
142 |
143 | Follow the interactive **Menu** to configure your project with the features you need.
144 |
145 | ## 📚 **Usage**
146 |
147 | ### Creating a New Project
148 |
149 | 1. **Install Servitect globally:**
150 | ```bash
151 | npm install -g servitect
152 | ```
153 |
154 | 2. **Run the CLI tool:**
155 | ```bash
156 | servitect
157 | ```
158 |
159 | 3. **Select your project directory** and provide a project name when prompted.
160 |
161 | 4. **Choose "Initialize MVC"** from the menu to create the basic structure.
162 |
163 | 5. **Add additional modules** as needed:
164 | - Actor Models (Users, Admins, etc.)
165 | - Entity Models (Products, Orders, etc.)
166 | - Chat Module
167 | - File Upload Module
168 | - Firebase FCM
169 | - WhatsApp Integration
170 | - NodeMailer
171 | - Docker Setup
172 | - LLM Integration
173 |
174 | 6. **Navigate to your project and start the server:**
175 | ```bash
176 | cd your-project-name
177 | npm run server
178 | ```
179 |
180 | 7. **Test your APIs** - if you see `Connected` in the console, your APIs are ready!
181 |
182 | ## 🗂️ **Project Structure**
183 |
184 | Here's an overview of the generated project structure:
185 |
186 | ```
187 | my-project/
188 | │
189 | ├── models/ # Database models
190 | │ ├── User.js # Example actor model
191 | │ └── Product.js # Example entity model
192 | │
193 | ├── controllers/ # Business logic
194 | │ ├── userController.js
195 | │ ├── productController.js
196 | │ ├── chatController.js
197 | │ ├── uploadController.js
198 | │ └── firebaseController.js
199 | │
200 | ├── routes/ # API routes
201 | │ ├── router.js # Main router
202 | │ ├── userRoutes.js
203 | │ ├── productRoutes.js
204 | │ ├── chatRoutes.js
205 | │ ├── fileUploadRoutes.js
206 | │ └── firebaseRoutes.js
207 | │
208 | ├── helper/ # Utility functions
209 | │ ├── JsonResponse.js # Standardized API responses
210 | │ ├── JWTAuthHelper.js # JWT authentication
211 | │ ├── TryCatch.js # Error handling wrapper
212 | │ ├── cloudinary.js # File upload utilities
213 | │ ├── LlmHelperOllama.js # LLM integration
214 | │ ├── Nodemailer.js # Email utilities
215 | │ └── WhatsappNotification.js # WhatsApp messaging
216 | │
217 | ├── constants/ # Application constants
218 | │ └── Message.js # Response messages
219 | │
220 | ├── middleware/ # Express middleware
221 | │ └── multer.js # File upload middleware
222 | │
223 | ├── public/ # Static files
224 | │ └── uploads/ # Temporary file storage
225 | │
226 | ├── .env # Environment variables
227 | ├── .gitignore # Git ignore rules
228 | ├── package.json # Dependencies and scripts
229 | ├── app.js # Express application setup
230 | ├── db.js # Database connection
231 | ├── Dockerfile # Docker configuration
232 | └── firebase-key.json # Firebase service account key
233 | ```
234 |
235 | ## 🔌 **Integrations**
236 |
237 | ### 1. Cloudinary Setup
238 |
239 | To enable file uploads with [Cloudinary](https://cloudinary.com/):
240 |
241 | 1. Sign up for a Cloudinary account
242 | 2. Get your Cloud Name, API Key, and API Secret from the dashboard
243 | 3. When prompted by Servitect, enter these credentials or add them to `.env` later:
244 | ```env
245 | CLOUDINARY_CLOUD_NAME=your_cloud_name
246 | CLOUDINARY_API_KEY=your_api_key
247 | CLOUDINARY_API_SECRET=your_api_secret
248 | ```
249 |
250 | ### 2. Firebase FCM Setup
251 |
252 | To use Firebase Cloud Messaging:
253 |
254 | 1. Create a Firebase project in the [Firebase Console](https://console.firebase.google.com/)
255 | 2. Go to **Settings** → **Cloud Messaging** and enable it
256 | 3. Go to **Service Accounts** → Generate **Private Key**
257 | 4. Save the downloaded JSON file as `firebase-key.json` in your project root
258 | 5. Add to `.env`:
259 | ```env
260 | GOOGLE_APPLICATION_CREDENTIALS="firebase-key.json"
261 | ```
262 |
263 | ### 3. WhatsApp Setup
264 |
265 | To use WhatsApp Cloud API:
266 |
267 | 1. Follow the [Meta WhatsApp Business API](https://developers.facebook.com/docs/whatsapp/) documentation
268 | 2. Get your access token and phone number ID
269 | 3. Add to `.env`:
270 | ```env
271 | WHATSAPP_URL="https://graph.facebook.com/v18.0/YOUR_PHONE_NUMBER_ID/messages"
272 | WHATSAPP_ACCESS_TOKEN=your_access_token
273 | ```
274 |
275 | ### 4. NodeMailer Setup
276 |
277 | For email functionality:
278 |
279 | 1. Use a Gmail account with App Password (recommended)
280 | 2. Add to `.env`:
281 | ```env
282 | NODEMAILER_ADMIN_EMAIL=your_email@gmail.com
283 | NODEMAILER_ADMIN_PASSWORD=your_app_password
284 | ```
285 |
286 | ### 5. LLM Setup (Ollama)
287 |
288 | For AI integration:
289 |
290 | 1. Install [Ollama](https://ollama.com/) locally
291 | 2. Pull a model (e.g., `ollama pull llama3.1`)
292 | 3. Ensure Ollama is running on `http://localhost:11434`
293 | 4. The LLM helper is pre-configured and ready to use
294 |
295 | ## 📋 **API Documentation**
296 |
297 | ### Authentication Endpoints
298 |
299 | ```
300 | POST /user/register # User registration
301 | POST /user/login # User login
302 | GET /user/get-by-id/:id # Get user by ID (protected)
303 | ```
304 |
305 | ### File Upload Endpoints
306 |
307 | ```
308 | POST /fileUpload/uploadSingleFile # Upload single file
309 | POST /fileUpload/uploadMultipleFiles # Upload multiple files
310 | POST /fileUpload/deleteSingleFile # Delete file from Cloudinary
311 | ```
312 |
313 | ### Chat Endpoints
314 |
315 | ```
316 | POST /chat/send-chat # Send a message
317 | GET /chat/get-my-chat/:id/:contactId # Get conversation history
318 | ```
319 |
320 | ### Firebase FCM Endpoints
321 |
322 | ```
323 | POST /firebase/sendNotificationToCustomDevice # Send to specific device
324 | POST /firebase/sendNotificationToTopic/:topic # Send to topic subscribers
325 | POST /firebase/sendBatchNotificationsMultipleFCMS # Batch send to multiple devices
326 | ```
327 |
328 | ### LLM Endpoints
329 |
330 | ```
331 | POST /llm/ask-llm # Send prompt to LLM and get response
332 | ```
333 |
334 | ## 🛡️ **Security Features**
335 |
336 | - **JWT Authentication**: Secure token-based authentication
337 | - **Password Hashing**: bcryptjs for secure password storage
338 | - **CORS Protection**: Cross-origin request handling
339 | - **Environment Variables**: Sensitive data protection
340 | - **Input Validation**: Request data validation and sanitization
341 | - **Error Handling**: Comprehensive error management without exposing sensitive info
342 |
343 | ## 🔧 **Development Commands**
344 |
345 | ```bash
346 | npm run server # Start development server with nodemon
347 | npm run watch # Alternative development command
348 | ```
349 |
350 | ## 🐣 **New to Development? We're Here to Help!**
351 |
352 | If you are just starting your journey in server development or find certain concepts challenging, we have resources to assist you:
353 |
354 | - **YouTube Tutorials**: Visit our [YouTube channel](https://www.youtube.com/@CodeGenieKush) for comprehensive tutorials
355 | - **In-Depth Documentation**: Detailed documentation covering features, commands, and best practices
356 | - **Community Support**: Connect with us on LinkedIn for support and questions:
357 | - [Kush Kapadia](https://www.linkedin.com/in/kushkapadia/)
358 | - [Mit Shah](https://mitshah.dev)
359 | - [Atharva Jadhav](https://www.linkedin.com/in/atharvajadhav88/)
360 |
361 | ## 🤝 **Contributing**
362 |
363 | Contributions, issues, and feature requests are welcome! Feel free to check the [issues page](https://github.com/kushkapadia/MVCGenerator/issues).
364 |
365 | ### How to Contribute
366 |
367 | 1. Fork the repository
368 | 2. Create your feature branch (`git checkout -b feature/AmazingFeature`)
369 | 3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
370 | 4. Push to the branch (`git push origin feature/AmazingFeature`)
371 | 5. Open a Pull Request
372 |
373 | ## 📈 **Stats**
374 |
375 | - 🎯 **1000+** weekly downloads on npm
376 | - ⭐ **Growing** GitHub stars
377 | - 🚀 **Production-ready** code generation
378 | - 🔧 **10+** integrated modules and features
379 |
380 | ## 📄 **License**
381 |
382 | This project is [MIT](https://opensource.org/licenses/MIT) licensed.
383 |
384 | ## 🙏 **Acknowledgements**
385 |
386 | - Thanks to all contributors who have helped shape Servitect
387 | - Inspired by the need for rapid, scalable server development in the Node.js ecosystem
388 | - Built with modern development practices and industry standards
389 |
390 | ## 🔗 **Links**
391 |
392 | - **NPM Package**: [https://www.npmjs.com/package/servitect](https://www.npmjs.com/package/servitect)
393 | - **GitHub Repository**: [https://github.com/kushkapadia/MVCGenerator](https://github.com/kushkapadia/MVCGenerator)
394 | - **YouTube Channel**: [CodeGenieKush](https://www.youtube.com/@CodeGenieKush)
395 |
396 | ---
397 |
398 | ## 🚀 **Happy Coding!**
399 |
400 | Made with ❤️ by **Elite Coders**
401 |
402 | **Server + Architect = Servitect** - Your one-click solution for Node.js MVC backend development!
403 |
--------------------------------------------------------------------------------
/bin/fileContents.js:
--------------------------------------------------------------------------------
1 | import removeIndentation from "./fileFormatter.js";
2 |
3 | // all Essential MVC File contents
4 | const fileContent = {
5 | nodemailerFileContent:
6 | removeIndentation(`const nodemailer = require("nodemailer")
7 | require("dotenv").config()
8 | class Nodemailer{
9 | recieverEmail
10 | subject
11 | content
12 |
13 | constructor(recieverEmail,subject, content){
14 | this.recieverEmail = recieverEmail
15 | this.subject = subject
16 | this.content = content
17 | }
18 |
19 | sendMail(){
20 |
21 | const transporter = nodemailer.createTransport({
22 | service: 'gmail',
23 | host: 'smtp.gmail.com',
24 | port: 465,
25 | secure: true,
26 | auth: {
27 | user: process.env.NODEMAILER_ADMIN_EMAIL,
28 | pass: process.env.NODEMAILER_ADMIN_PASSWORD
29 | }
30 | });
31 |
32 | let details = {
33 | from: process.env.NODEMAILER_ADMIN_EMAIL,
34 | to: this.recieverEmail,
35 | subject: this.subject,
36 | html: this.content
37 | }
38 |
39 |
40 | transporter.sendMail(details, (err) => {
41 | if (err) {
42 | return err;
43 | } else {
44 | return 'Sent Mail Successfully';
45 | }
46 | })
47 | }
48 | }
49 | module.exports = Nodemailer
50 | `),
51 | whatsappFileContent: removeIndentation(`import axios from "axios"
52 | require("dotenv").config()
53 | class WhatsappNotification {
54 | numberToSend;
55 | msgBody
56 |
57 | constructor(numberToSend, msgBody) {
58 | this.numberToSend = numberToSend
59 | this.msgBody = msgBody
60 | }
61 |
62 | sendWhatsappNotification() {
63 | const url = process.env.WHATSAPP_URL;
64 | const accessToken = process.env.WHATSAPP_ACCESS_TOKEN;
65 |
66 | const data = {
67 | messaging_product: 'whatsapp',
68 | to: this.numberToSend,
69 | type: 'text',
70 | text: {
71 | body: this.msgBody
72 | },
73 | };
74 |
75 | const headers = {
76 | Authorization: "Bearer " + accessToken,
77 | 'Content-Type': 'application/json',
78 | };
79 |
80 | axios.post(url, data, { headers })
81 | .then(response => {
82 | return response.data;
83 | })
84 | .catch(error => {
85 | return error.response.data;
86 | });
87 | }
88 | }
89 | module.exports = WhatsappNotification
90 | `),
91 | dbFileContent: removeIndentation(`const {MongoClient} = require('mongodb')
92 |
93 | const dotenv = require('dotenv')
94 | dotenv.config()
95 |
96 | const client = new MongoClient(process.env.CONNECTION_STRING)
97 |
98 | async function start(){
99 | await client.connect()
100 |
101 | console.log("Connected")
102 | module.exports = client
103 | const app = require('./app')
104 | app.listen(process.env.PORT)
105 | }
106 | start()`),
107 | appFileContent: (routePrefix) =>
108 | removeIndentation(`const express = require("express");
109 | const routes = require("./routes/router.js");
110 | const morgan = require("morgan");
111 |
112 | const cors = require("cors");
113 |
114 |
115 | //imports here
116 |
117 |
118 | //code here
119 |
120 | // Initialize our server
121 | const app = express();
122 | //To access the data user inputs in form.
123 | app.use(express.urlencoded({ extended: false }));
124 | //just a bolierplate code, tells our express server to add the user submitted data to request object.
125 | app.use(express.json());
126 |
127 | app.use(express.static("public"));
128 | app.use(morgan("dev"));
129 | app.use(cors());
130 | app.use("${routePrefix}", routes);
131 | module.exports = app;
132 | `),
133 | envFileContent: (PORT, CONNECTION_STRING) =>
134 | `PORT=${!PORT || isNaN(PORT) || PORT < 1 || PORT > 65535 ? "4000" : PORT
135 | }\nCONNECTION_STRING=${CONNECTION_STRING}\nJWTSECRET=qwertyqwertyqwerty`,
136 | gitIgnoreFileContent: `/node_modules\n.env`,
137 | packageJsonFileContent: `{
138 | "name": "backend",
139 | "version": "1.0.0",
140 | "description": "",
141 | "main": "index.js",
142 | "scripts": {
143 | "server": "nodemon db.js",
144 | "watch": "start nodemon db.js"
145 | },
146 | "keywords": [],
147 | "author": "",
148 | "license": "ISC",
149 | "dependencies": {
150 |
151 | }
152 | }
153 | `,
154 | messageFileContent: removeIndentation(`
155 | let Messages = function () {
156 |
157 | };
158 |
159 | //Login Messages
160 | Messages.prototype.INVAID_AUTHORIZATION =
161 | "This authorization code validate unsuccessfull.";
162 | Messages.prototype.VALIDATE_AUTHORIZATION =
163 | "This authorization code validate successfully.";
164 | Messages.prototype.INVALID_CREDENTIALS = "Invalid credentials.";
165 | Messages.prototype.UNAUTHORIZED = "Unauthorized to access";
166 | Messages.prototype.UNAUTHORIZED_SESSION_EXPIRED =
167 | "Unauthorized to access, session expired";
168 |
169 | //CRUD Messages
170 | Messages.prototype.CREATE_FAILED = "Failed to Create New Entry";
171 | Messages.prototype.SUCCESSFULLY_DELETED = "Data successfully deleted";
172 | Messages.prototype.SUCCESSFULLY_RECORD_DELETED = "Data successfully deleted";
173 | Messages.prototype.SUCCESSFULLY_UPDATED = "Data successfully updated";
174 |
175 | //JWT Messages
176 | Messages.prototype.TOKEN_ERROR = "Token not generated.";
177 | Messages.prototype.TOKEN_SUCCESS = "Token generated successfully.";
178 | Messages.prototype.INVALID_SECRET = "Invalid secret.";
179 |
180 | //DATA Messages
181 | Messages.prototype.SUCCESSFULLY_RECEIVED = 'Successfully received.';
182 |
183 | // Cloudinary Messages
184 | Messages.prototype.FILE_NOT_FOUND = "File not found.";
185 |
186 | Messages.prototype.SUCCESSFULLY_SAVED_TO_CLOUDINARY =
187 | "Successfully saved to cloudinary.";
188 | Messages.prototype.FAILED_TO_SAVE_TO_CLOUDINARY =
189 | "Failed to save to cloudinary.";
190 | Messages.prototype.UPLOAD_SUCCESS = "File uploaded successfully.";
191 | Messages.prototype.SUCCESSFULLY_FILE_DELETED = "Successfully file deleted.";
192 | Messages.prototype.FAILED_TO_DELETE_FROM_CLOUDINARY =
193 | "Failed to delete from cloudinary.";
194 | Messages.prototype.SUCCESSFULLY_GENERATED = "LLM Response Success";
195 |
196 | module.exports = Messages;
197 |
198 | `),
199 | JsonResponseFileContent: removeIndentation(`
200 |
201 | const HttpStatus = require("http-status-codes");
202 |
203 | let JsonResponse = function(req, res, method_name){
204 | this.req = req,
205 | this.res = res,
206 | this.method_name =method_name;
207 | }
208 |
209 |
210 | JsonResponse.prototype.jsonSuccess = function(data, message) {
211 | const obj = {
212 | success: true,
213 | data: data,
214 | message: message,
215 | };
216 | this.res.status(HttpStatus.OK).send(obj);
217 | }
218 |
219 | JsonResponse.prototype.jsonError = function() {
220 |
221 | const obj = {
222 | success: false,
223 | data: this.res.locals.data,
224 | message:
225 | this.res.locals.message.toString() || "Something went wrong, please contact to admin.",
226 | };
227 | if (!this.res.locals.status_code) {
228 | this.res.status(HttpStatus.BAD_REQUEST);
229 | } else {
230 | this.res.status(this.res.locals.status_code);
231 | }
232 | this.res.send(obj);
233 | }
234 |
235 | module.exports = JsonResponse
236 | `),
237 | JWTAuthHelperFileContent: removeIndentation(`
238 | const jwt = require("jsonwebtoken");
239 | const JsonResponse = require('./JsonResponse');
240 |
241 | exports.verifyToken = function (req, res, next) {
242 | try{
243 | const bearerToken = req.headers["authorization"];
244 |
245 | const bearer = bearerToken.split(" ");
246 |
247 | const token = bearer[1];
248 |
249 | console.log(token);
250 |
251 | req.apiUser = jwt.verify(token, process.env.JWTSECRET);
252 | console.log(req.apiUser)
253 | next()
254 | } catch(error){
255 | console.log("here")
256 | res.locals.data = {
257 | isVaild: false,
258 | authorizationFailed: true,
259 | };
260 |
261 | res.locals.message = error;
262 | new JsonResponse(req, res).jsonError();
263 | // next(error)
264 | // return true
265 | }
266 | };
267 | `),
268 | tryCatchFileContent: removeIndentation(`
269 | const JsonResponse = require('./JsonResponse')
270 |
271 | let TryCatch = function(handler){
272 | this.handler = handler
273 | }
274 | /**
275 | * tryCatchGlobe
276 | */
277 | TryCatch.prototype.tryCatchGlobe = function() {
278 | return async (req, res, next) => {
279 | try {
280 | await this.handler(req,res);
281 | } catch (error) {
282 | res.locals.data = {
283 | isVaild:false
284 | };
285 | res.locals.message = error;
286 | // console.log(error);
287 | // new JsonResponse.jsonError(req, res);
288 | new JsonResponse(req, res).jsonError();
289 |
290 | next(error);
291 | }
292 | }
293 | }
294 |
295 | module.exports = TryCatch
296 | `),
297 | routerFileContent: removeIndentation(`
298 | const express = require('express');
299 | const router = express.Router();
300 |
301 | //imports here
302 |
303 | //code here
304 | router.get("/health-check", (req,res)=>{
305 | res.json("Server Health: OK");
306 | })
307 |
308 | module.exports = router;
309 | `),
310 | // addIndexFileRouteContent: (modelName) => {
311 | // removeIndentation(`
312 | // const ${modelName.toLowerCase()}Routes = require('./${modelName.toLowerCase()}Routes');
313 |
314 | // router.use('/${modelName.toLowerCase()}', ${modelName.toLowerCase()}Routes);
315 | // `);
316 | // },
317 | actorRouterFileContent: (modelName) =>
318 | removeIndentation(`
319 | const express = require('express');
320 | const router = express.Router();
321 | const AuthHelper = require('../helper/JWTAuthHelper');
322 | const TryCatch = require('../helper/TryCatch');
323 | const Messages = require('../constants/Message');
324 | const ${modelName.toLowerCase()}Controller = require('../controllers/${modelName.toLowerCase()}Controller');
325 |
326 | //imports here
327 |
328 | //code here
329 |
330 | //Entity - ${modelName} --start
331 | //Authentication - ${modelName}
332 | router.post('/register', new TryCatch(${modelName.toLowerCase()}Controller.apiRegister).tryCatchGlobe());
333 | router.post('/login', new TryCatch(${modelName.toLowerCase()}Controller.apiLogin).tryCatchGlobe());
334 |
335 | //CRUD Operations - ${modelName}
336 | router.post('/does-email-exists', AuthHelper.verifyToken, new TryCatch(${modelName.toLowerCase()}Controller.doesEmailExist).tryCatchGlobe());
337 | router.get('/get-by-id/:id', AuthHelper.verifyToken, new TryCatch(${modelName.toLowerCase()}Controller.getById).tryCatchGlobe());
338 | router.get('/get-by-email/:email', AuthHelper.verifyToken, new TryCatch(${modelName.toLowerCase()}Controller.getByEmail).tryCatchGlobe());
339 | router.get('/get-all', AuthHelper.verifyToken, new TryCatch(${modelName.toLowerCase()}Controller.getAll${modelName}s).tryCatchGlobe());
340 | router.delete('/delete-by-id/:id', AuthHelper.verifyToken, new TryCatch(${modelName.toLowerCase()}Controller.deleteById).tryCatchGlobe());
341 | router.post("/update-by-id/:id", AuthHelper.verifyToken, new TryCatch(${modelName.toLowerCase()}Controller.updateById).tryCatchGlobe());
342 | router.post("/update-by-email/:email", new TryCatch(${modelName.toLowerCase()}Controller.updateByEmail).tryCatchGlobe());
343 | //Entity - ${modelName} - End
344 |
345 | module.exports = router;
346 | `),
347 | nonActorRouterFileContent: (modelName) =>
348 | removeIndentation(`
349 | const express = require('express');
350 | const router = express.Router();
351 | const AuthHelper = require('../helper/JWTAuthHelper');
352 | const TryCatch = require('../helper/TryCatch');
353 | const Messages = require('../constants/Message');
354 | const ${modelName.toLowerCase()}Controller = require('../controllers/${modelName.toLowerCase()}Controller');
355 |
356 | //imports here
357 |
358 | //code here
359 |
360 | //Entity - ${modelName} --start
361 | //CRUD Operations - ${modelName}
362 | router.post('/create', AuthHelper.verifyToken, new TryCatch(${modelName.toLowerCase()}Controller.create${modelName}).tryCatchGlobe());
363 | router.get('/get-by-id/:id', AuthHelper.verifyToken, new TryCatch(${modelName.toLowerCase()}Controller.getById).tryCatchGlobe());
364 | router.get('/get-all', AuthHelper.verifyToken, new TryCatch(${modelName.toLowerCase()}Controller.getAll${modelName}s).tryCatchGlobe());
365 | router.delete('/delete-by-id/:id', AuthHelper.verifyToken, new TryCatch(${modelName.toLowerCase()}Controller.deleteById).tryCatchGlobe());
366 | router.post("/update-by-id/:id", AuthHelper.verifyToken, new TryCatch(${modelName.toLowerCase()}Controller.updateById).tryCatchGlobe());
367 | //Entity - ${modelName} - End
368 |
369 | module.exports = router;
370 | `),
371 | chatRouterFileContent: (modelName) =>
372 | removeIndentation(`
373 | const express = require('express');
374 | const router = express.Router();
375 | const AuthHelper = require('../helper/JWTAuthHelper');
376 | const TryCatch = require('../helper/TryCatch');
377 | const Messages = require('../constants/Message');
378 | const chatController = require('../controllers/chatController');
379 |
380 | //imports here
381 |
382 | //code here
383 |
384 | //Entity - ${modelName} --start
385 | router.post('/send-chat', AuthHelper.verifyToken, new TryCatch(chatController.sendChat).tryCatchGlobe());
386 | router.get('/get-my-chat/:id/:chatContactId', AuthHelper.verifyToken, new TryCatch(chatController.getChatConvo).tryCatchGlobe());
387 | //Entity - ${modelName} - End
388 |
389 | module.exports = router;
390 | `),
391 | fileUploadRouterFileContent: (modelName) =>
392 | removeIndentation(`
393 | const express = require('express');
394 | const router = express.Router();
395 | const AuthHelper = require('../helper/JWTAuthHelper');
396 | const TryCatch = require('../helper/TryCatch');
397 | const Messages = require('../constants/Message');
398 | const uploadController = require('../controllers/uploadController');
399 | const upload = require('../middleware/multer');
400 |
401 | //imports here
402 |
403 | //code here
404 |
405 | //Entity - ${modelName} --start
406 | // Add Single file to Cloudinary
407 | router.post("/uploadSingleFile", AuthHelper.verifyToken, upload.single("image"), new TryCatch(uploadController.uploadSingleFile).tryCatchGlobe());
408 |
409 | // Add Multiple files to cloudinary - {Array of Attachments}
410 | router.post("/uploadMultipleFiles", AuthHelper.verifyToken, upload.array("attachments"), new TryCatch(uploadController.uploadMultipleFiles).tryCatchGlobe());
411 |
412 | // Add files according to fields to cloudinary
413 | // [
414 | // { name: 'avatar', maxCount: 1 },
415 | // { name: 'gallery', maxCount: 8 }
416 | // ]
417 | router.post("/uploadFiles",AuthHelper.verifyToken,upload.fields([{name: "userImage"},{name: "coverPhoto",}]),new TryCatch(uploadController.uploadFiles).tryCatchGlobe());
418 |
419 | // Delete Single file from cloudinary
420 | router.post("/deleteSingleFile", AuthHelper.verifyToken, new TryCatch(uploadController.deleteSingleFile).tryCatchGlobe());
421 |
422 | // Delete Multiple files from cloudinary - {Array of Public Ids}
423 | router.post("/deleteMultipleFiles", AuthHelper.verifyToken, new TryCatch(uploadController.deleteMultipleFiles).tryCatchGlobe());
424 | //Entity - ${modelName} - End
425 |
426 | module.exports = router;
427 | `),
428 | firebaseRouterFileContent: (modelName) =>
429 | removeIndentation(`
430 | const express = require('express');
431 | const router = express.Router();
432 | const AuthHelper = require('../helper/JWTAuthHelper');
433 | const TryCatch = require('../helper/TryCatch');
434 | const Messages = require('../constants/Message');
435 | const firebaseController = require("../controllers/firebaseController")
436 |
437 | //imports here
438 |
439 | //code here
440 | //Firebase Push Notification Routes - Start
441 | router.post("/sendNotificationToCustomDevice", AuthHelper.verifyToken,
442 | new TryCatch(firebaseController.sendNotificationToCustomDevice).tryCatchGlobe());
443 |
444 | router.post("/sendNotificationToTopic/:topic", AuthHelper.verifyToken,
445 | new TryCatch(firebaseController.sendNotificationToTopic).tryCatchGlobe());
446 |
447 | router.post("/sendBatchNotificationsMultipleFCMS", AuthHelper.verifyToken,
448 | new TryCatch(firebaseController.sendBatchNotificationsMultipleFCMS).tryCatchGlobe());
449 |
450 | router.post("/sendNotificationsToMultipleTopics", AuthHelper.verifyToken,
451 | new TryCatch(firebaseController.sendNotificationsToMultipleTopics).tryCatchGlobe());
452 | //Firebase Push Notification Routes - End
453 |
454 | module.exports = router;
455 | `),
456 | firebaseControllerFile: removeIndentation(`
457 | const admin = require("firebase-admin");
458 | const { firebase } = require("googleapis/build/src/apis/firebase");
459 | const JsonResponse = require("../helper/JsonResponse");
460 | const Messages = require("../constants/Message");
461 |
462 |
463 | exports.sendNotificationToCustomDevice = async (req, res) => {
464 | const token = req.body.fcmToken;
465 |
466 | const message = {
467 | notification: {
468 | title: req.body.title,
469 | body: req.body.desc,
470 | },
471 | data: {
472 | url: req.body.url,
473 | },
474 | token: token,
475 | };
476 |
477 | admin
478 | .messaging()
479 | .send(message)
480 | .then((response) => {
481 | // console.log("Successfully sent message:", response);
482 | new JsonResponse(req, res).jsonSuccess(response, new Messages().PUSH_NOTIFICATION_SENT)
483 | })
484 | .catch((error) => {
485 | // console.log("Error sending message:");
486 | new JsonResponse(req, res).jsonError(error, new Messages().PUSH_NOTIFICATION_FAILED)
487 | });
488 | };
489 |
490 | exports.sendNotificationToTopic = async (req, res) => {
491 | const topic = req.params.topic;
492 |
493 | console.log("Topic:");
494 | console.log(topic);
495 |
496 | const message = {
497 | notification: {
498 | title: req.body.title,
499 | body: req.body.desc,
500 | },
501 | data: {
502 | url: req.body.url,
503 | },
504 | topic: topic,
505 | };
506 |
507 | admin
508 | .messaging()
509 | .send(message)
510 | .then((response) => {
511 | // console.log("Successfully sent message:", response);
512 | new JsonResponse(req, res).jsonSuccess(response, new Messages().PUSH_NOTIFICATION_SENT)
513 | })
514 | .catch((error) => {
515 | // console.log("Error sending message:");
516 | new JsonResponse(req, res).jsonError(error, new Messages().PUSH_NOTIFICATION_FAILED)
517 | });
518 | };
519 |
520 | exports.sendBatchNotificationsMultipleFCMS = async (req, res) => {
521 | try {
522 | const tokens = req.body.fcmTokens;
523 |
524 | const message = {
525 | notification: {
526 | title: req.body.title,
527 | body: req.body.desc,
528 | },
529 | tokens: tokens,
530 | data: {
531 | url: req.body.url,
532 | },
533 | };
534 |
535 | admin
536 | .messaging()
537 | .sendEachForMulticast(message)
538 | .then((response) => {
539 | // if (response.failureCount > 0) {
540 | // const failedTokens = [];
541 | // response.responses.forEach((resp, idx) => {
542 | // if (!resp.success) {
543 | // failedTokens.push(tokens[idx]);
544 | // }
545 | // });
546 | // console.log("List of tokens that caused failures: " + failedTokens);
547 | // new JsonResponse(req, res).jsonSuccess(response.failureCount, new Messages().PUSH_NOTIFICATION_SENT)
548 |
549 | // } else {
550 | new JsonResponse(req, res).jsonSuccess(response, new Messages().PUSH_NOTIFICATION_SENT)
551 | // }
552 | });
553 | } catch (err) {
554 | console.log(err);
555 | // res.status(500).json({ message: "Error sending push notification" });
556 | new JsonResponse(req, res).jsonSuccess(err, new Messages().PUSH_NOTIFICATION_FAILED)
557 |
558 | }
559 | };
560 | exports.sendNotificationsToMultipleTopics = async (req, res) => {
561 | const topics = req.body.topics;
562 |
563 | if (!Array.isArray(topics) || topics.length === 0) {
564 | return res
565 | .status(400)
566 | .send({ error: "Topics should be a non-empty array" });
567 | }
568 |
569 | // Helper function to chunk an array into smaller arrays of a specific size
570 | const chunkArray = (array, size) => {
571 | const result = [];
572 | for (let i = 0; i < array.length; i += size) {
573 | result.push(array.slice(i, i + size));
574 | }
575 | return result;
576 | };
577 |
578 | const topicChunks = chunkArray(topics, 5);
579 |
580 | const notificationPromises = topicChunks.map(async (chunk) => {
581 | const condition = chunk
582 | .map((topic) => \`'\${topic.replace(/'/g, "\\'").replace(/ /g, "_")}' in topics\`)
583 | .join(" || ");
584 | console.log("Condition:", condition);
585 |
586 | const message = {
587 | notification: {
588 | title: req.body.title,
589 | body: req.body.desc,
590 | },
591 | data: {
592 | url: req.body.url,
593 | },
594 | condition: condition,
595 | };
596 |
597 | const results = await Promise.all(notificationPromises);
598 | try {
599 | const response = await admin.messaging().send(message);
600 | console.log("Successfully sent message:", response);
601 | // return { status: "success", response };
602 | new JsonResponse(req, res).jsonSuccess(response, new Messages().PUSH_NOTIFICATION_SENT)
603 | } catch (error) {
604 | console.log("Error sending message:", error);
605 | // return { status: "error", error };
606 | new JsonResponse(req, res).jsonError(error, new Messages().PUSH_NOTIFICATION_FAILED)
607 |
608 | }
609 | });
610 | }
611 | `),
612 | uploadControllerFile: removeIndentation(`
613 | const Messages = require("../constants/Message");
614 | const JsonResponse = require("../helper/JsonResponse");
615 | const jwt = require("jsonwebtoken");
616 | const {
617 | uploadSingleFileOnCloudinary,
618 | uploadMultipleFilesOnCloudinary,
619 | deleteSingleFileFromCloudinary,
620 | deleteMultipleFilesFromCloudinary,
621 | } = require("../helper/cloudinary");
622 |
623 | // Upload Files
624 |
625 | exports.uploadSingleFile = async function (req, res) {
626 | console.log("Request File");
627 |
628 | // If you are sending only one file only use req.file
629 | console.log(req.file);
630 |
631 | // Get the Local Path from the server which will be stored by multer defined in the middleware
632 | const image = req.file?.path;
633 |
634 | console.log("LocalPath: ", image);
635 |
636 | // Use the local Path to upload the file to Cloudinary
637 | const result = await uploadSingleFileOnCloudinary(image);
638 |
639 | console.log("Result" + result);
640 |
641 | // Make sure if the file has been uploaded to Cloudinary, store the cloudinary URL in the database
642 | if (result == null) {
643 | new JsonResponse(req, res).jsonSuccess(
644 | null,
645 | new Messages().FAILED_TO_SAVE_TO_CLOUDINARY
646 | );
647 | return;
648 | }
649 |
650 | console.log("Cloudinary Result: ", result);
651 |
652 | new JsonResponse(req, res).jsonSuccess(
653 | result.url,
654 | new Messages().SUCCESSFULLY_RECEIVED
655 | );
656 | };
657 |
658 | exports.uploadMultipleFiles = async function (req, res) {
659 | // Get the Local Path from the server which will be stored by multer defined in the middleware
660 | const attachments = req.files;
661 |
662 | // If there are no attachments
663 | if (attachments.length == 0) {
664 | new JsonResponse(req, res).jsonSuccess(null, new Messages().FILE_NOT_FOUND);
665 | return;
666 | }
667 |
668 | // Use the local Path to upload the file to Cloudinary
669 | const result = await uploadMultipleFilesOnCloudinary(attachments);
670 |
671 | // Make sure if the file has been uploaded to Cloudinary, store the cloudinary URL in the database
672 | if (result == null) {
673 | new JsonResponse(req, res).jsonSuccess(
674 | null,
675 | new Messages().FAILED_TO_SAVE_TO_CLOUDINARY
676 | );
677 | return;
678 | }
679 |
680 | console.log("Cloudinary Result: ", result);
681 |
682 | new JsonResponse(req, res).jsonSuccess(
683 | result,
684 | new Messages().SUCCESSFULLY_RECEIVED
685 | );
686 | };
687 |
688 | exports.uploadFiles = async function (req, res) {
689 | // Get the Local Path from the server which will be stored by multer defined in the middleware
690 | const userImagePath = req.files?.userImage[0].path;
691 | const coverPhotoPath = req.files?.coverPhoto[0].path;
692 |
693 | // If there are no images
694 | if (userImagePath == null || coverPhotoPath == null) {
695 | new JsonResponse(req, res).jsonSuccess(null, new Messages().FILE_NOT_FOUND);
696 | return;
697 | }
698 |
699 | // Use the local Path to upload the file to Cloudinary
700 | const userImageURL = await uploadSingleFileOnCloudinary(userImagePath);
701 | const coverPhotoURL = await uploadSingleFileOnCloudinary(coverPhotoPath);
702 |
703 | // Make sure if the file has been uploaded to Cloudinary, store the cloudinary URL in the database
704 | if (!userImageURL || !coverPhotoURL) {
705 | new JsonResponse(req, res).jsonSuccess(
706 | null,
707 | new Messages().FAILED_TO_SAVE_TO_CLOUDINARY
708 | );
709 | return;
710 | }
711 |
712 | console.log("Cloudinary Result: ", userImageURL, coverPhotoURL);
713 |
714 | new JsonResponse(req, res).jsonSuccess(
715 | {
716 | userImageURL: userImageURL.url,
717 | coverPhotoURL: coverPhotoURL.url,
718 | },
719 | new Messages().SUCCESSFULLY_RECEIVED
720 | );
721 | };
722 |
723 | // Delete Files
724 |
725 | exports.deleteSingleFile = async function (req, res) {
726 | const publicId = req.body.publicId;
727 |
728 | // Delete the file from Cloudinary
729 | const result = await deleteSingleFileFromCloudinary(publicId);
730 |
731 | // Make sure if the file has been deleted from Cloudinary
732 | if (!result) {
733 | new JsonResponse(req, res).jsonSuccess(
734 | null,
735 | new Messages().FAILED_TO_DELETE_FROM_CLOUDINARY
736 | );
737 | return;
738 | }
739 |
740 | new JsonResponse(req, res).jsonSuccess(
741 | null,
742 | new Messages().SUCCESSFULLY_FILE_DELETED
743 | );
744 | };
745 |
746 | exports.deleteMultipleFiles = async function (req, res) {
747 | const publicIds = req.body.publicIds;
748 |
749 | // Delete the file from Cloudinary
750 | const result = await deleteMultipleFilesFromCloudinary(publicIds);
751 |
752 | // Make sure if the file has been deleted from Cloudinary
753 | if (!result) {
754 | new JsonResponse(req, res).jsonSuccess(
755 | null,
756 | new Messages().FAILED_TO_DELETE_FROM_CLOUDINARY
757 | );
758 | return;
759 | }
760 |
761 | new JsonResponse(req, res).jsonSuccess(
762 | null,
763 | new Messages().SUCCESSFULLY_FILE_DELETED
764 | );
765 | };
766 |
767 | `),
768 | cloudinaryHelperFileContent: removeIndentation(`
769 | const cloudinary = require("cloudinary").v2;
770 | const fs = require("fs");
771 | const Messages = require("../constants/Message");
772 | const JsonResponse = require("../helper/JsonResponse");
773 |
774 | cloudinary.config({
775 | cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
776 | api_key: process.env.CLOUDINARY_API_KEY,
777 | api_secret: process.env.CLOUDINARY_API_SECRET,
778 | });
779 |
780 | const uploadSingleFileOnCloudinary = async (localFilePath) => {
781 | if (!localFilePath) {
782 | new JsonResponse(req, res).jsonSuccess(null, new Messages().FILE_NOT_FOUND);
783 | return;
784 | }
785 |
786 | const result = await cloudinary.uploader.upload(localFilePath, {
787 | resource_type: "auto",
788 | });
789 | fs.unlinkSync(localFilePath);
790 |
791 | console.log("Result" + result.url);
792 |
793 | return result;
794 | };
795 |
796 | const uploadMultipleFilesOnCloudinary = async (attachments) => {
797 | const urls = await Promise.all(
798 | attachments.map(async (attachment) => {
799 | const result = await cloudinary.uploader.upload(attachment.path, {
800 | resource_type: "auto",
801 | });
802 | return result.url;
803 | })
804 | );
805 |
806 | attachments.map((attachment) => {
807 | fs.unlinkSync(attachment.path);
808 | });
809 |
810 | console.log("Result" + urls);
811 |
812 | return urls;
813 | };
814 |
815 | const deleteSingleFileFromCloudinary = async (publicId) => {
816 | if (!publicId) {
817 | new JsonResponse(req, res).jsonSuccess(null, new Messages().FILE_NOT_FOUND);
818 | return;
819 | }
820 |
821 | const result = await cloudinary.uploader.destroy(publicId);
822 | console.log("Result" + result);
823 |
824 | return result;
825 | };
826 |
827 | const deleteMultipleFilesFromCloudinary = async (publicIds) => {
828 | if (!publicIds) {
829 | new JsonResponse(req, res).jsonSuccess(null, new Messages().FILE_NOT_FOUND);
830 | return;
831 | }
832 |
833 | const result = await cloudinary.api.delete_resources(publicIds);
834 | console.log("Result" + result);
835 |
836 | return result;
837 | };
838 |
839 | module.exports = {
840 | uploadSingleFileOnCloudinary,
841 | uploadMultipleFilesOnCloudinary,
842 | deleteSingleFileFromCloudinary,
843 | deleteMultipleFilesFromCloudinary,
844 | };
845 | `),
846 | uploadMiddlewareFileContent: removeIndentation(`
847 | const multer = require("multer");
848 |
849 | const storage = multer.diskStorage({
850 | destination: function (req, file, cb) {
851 | cb(null, "public/uploads/");
852 | },
853 | filename: function (req, file, cb) {
854 | const uniqueSuffix = new Date().getTime().toString();
855 | console.log(req + " " + file);
856 |
857 | cb(null, uniqueSuffix + "-" + file.originalname);
858 | },
859 | });
860 |
861 | const upload = multer({ storage: storage });
862 |
863 | module.exports = upload;
864 | `),
865 | nonActorControllerFileContent: (modelname) =>
866 | removeIndentation(`
867 | const Messages = require("../constants/Message");
868 | const JsonResponse = require("../helper/JsonResponse");
869 | const TryCatch = require("../helper/TryCatch");
870 | const ${modelname} = require("../models/${modelname}");
871 | const jwt = require("jsonwebtoken");
872 |
873 | exports.create${modelname} = async function(req, res){
874 | let ${modelname.toLowerCase()} = new ${modelname}(req.body)
875 | let ${modelname.toLowerCase()}Doc = await ${modelname.toLowerCase()}.create${modelname}();
876 | new JsonResponse(req, res).jsonSuccess(${modelname.toLowerCase()}Doc, "Created")
877 | }
878 |
879 | exports.getById = async function (req, res) {
880 | let ${modelname.toLowerCase()} = new ${modelname} ()
881 | let ${modelname.toLowerCase()}Doc = await ${modelname.toLowerCase()}.getById(req.params.id)
882 | new JsonResponse(req, res).jsonSuccess(${modelname.toLowerCase()}Doc, new Messages().SUCCESSFULLY_RECEIVED)
883 |
884 | }
885 |
886 | exports.updateById = async function (req, res) {
887 | let ${modelname.toLowerCase()} = new ${modelname}();
888 | let ${modelname.toLowerCase()}Doc = await ${modelname.toLowerCase()}.updateById(req.params.id, req.body);
889 | new JsonResponse(req, res).jsonSuccess(${modelname.toLowerCase()}Doc, new Messages().SUCCESSFULLY_UPDATED);
890 | };
891 |
892 | exports.getAll${modelname}s = async function (req, res) {
893 | let ${modelname.toLowerCase()} = new ${modelname} ()
894 | let ${modelname.toLowerCase()}s = await ${modelname.toLowerCase()}.getAll${modelname}s()
895 | new JsonResponse(req, res).jsonSuccess(${modelname.toLowerCase()}s, new Messages().SUCCESSFULLY_RECEIVED)
896 | return ${modelname.toLowerCase()}s
897 | }
898 |
899 | exports.deleteById = async function (req, res) {
900 | let ${modelname.toLowerCase()} = new ${modelname} ();
901 | await ${modelname.toLowerCase()}.deleteById()
902 | new JsonResponse(req, res).jsonSuccess(true, new Messages().SUCCESSFULLY_DELETED)
903 | }
904 | `),
905 | actorControllerFileContent: (modelname) =>
906 | removeIndentation(`
907 | const Messages = require("../constants/Message");
908 | const JsonResponse = require("../helper/JsonResponse");
909 | const TryCatch = require("../helper/TryCatch");
910 | const ${modelname} = require("../models/${modelname}");
911 | const jwt = require("jsonwebtoken");
912 |
913 |
914 | // how long a token lasts before expiring
915 | const tokenLasts = "365d";
916 |
917 |
918 | //LOGIN
919 | exports.apiLogin = async function (req, res) {
920 | let ${modelname.toLowerCase()} = new ${modelname}(req.body);
921 |
922 | let result = await ${modelname.toLowerCase()}.login();
923 | if (result) {
924 | let data = {
925 | token: jwt.sign(
926 | { _id: ${modelname.toLowerCase()}.data._id, name: ${modelname.toLowerCase()}.data.name, email: ${modelname.toLowerCase()}.data.email },
927 | process.env.JWTSECRET,
928 | { expiresIn: tokenLasts }
929 | ),
930 | id: ${modelname.toLowerCase()}.data._id,
931 | name: ${modelname.toLowerCase()}.data.name,
932 | role: "${modelname.toLowerCase()}",
933 | };
934 |
935 | new JsonResponse(req, res).jsonSuccess(data, "Login success");
936 | } else {
937 | res.locals.data = {
938 | isValid: false,
939 | loginFailed: true,
940 | };
941 | res.locals.message = new Messages().INVALID_CREDENTIALS;
942 | new JsonResponse(req, res).jsonError();
943 | }
944 | };
945 |
946 | //REGISTER
947 | exports.apiRegister = async function (req, res) {
948 | let ${modelname.toLowerCase()} = new ${modelname}(req.body);
949 | console.log(req.body);
950 |
951 | let result = await ${modelname.toLowerCase()}.register();
952 | if (result) {
953 | let data = {
954 | token: jwt.sign(
955 | { _id: ${modelname.toLowerCase()}.data._id, name: ${modelname.toLowerCase()}.data.fName, email: ${modelname.toLowerCase()}.data.email },
956 | process.env.JWTSECRET,
957 | { expiresIn: tokenLasts }
958 | ),
959 | id: ${modelname.toLowerCase()}.data._id,
960 | name: ${modelname.toLowerCase()}.data.name,
961 | role: "${modelname.toLowerCase()}",
962 | };
963 | new JsonResponse(req, res).jsonSuccess(data, "Register success");
964 | } else {
965 | res.locals.data = {
966 | isVaild: false,
967 | authorizationFailed: true,
968 | };
969 | res.locals.message = regErrors;
970 | new JsonResponse(req, res).jsonError();
971 | }
972 | };
973 |
974 | //${modelname} Exists?
975 | exports.doesEmailExist = async function (req, res) {
976 | // throw new Error("This is a dummy exception for testing");
977 | console.log(${modelname}.doesEmailExist(req.body.email));
978 | let emailBool = await ${modelname}.doesEmailExist(req.body.email);
979 | new JsonResponse(req, res).jsonSuccess(
980 | emailBool,
981 | new Messages().SUCCESSFULLY_RECEIVED
982 | );
983 | };
984 |
985 |
986 |
987 | exports.getById = async function(req, res){
988 | let ${modelname.toLowerCase()} = new ${modelname}()
989 | let ${modelname.toLowerCase()}Doc = await ${modelname.toLowerCase()}.getById(req.params.id)
990 | new JsonResponse(req, res).jsonSuccess(${modelname.toLowerCase()}Doc, new Messages().SUCCESSFULLY_RECEIVED)
991 |
992 | }
993 |
994 | exports.getByEmail = async function(req, res){
995 | let ${modelname.toLowerCase()} = new ${modelname}()
996 | let ${modelname.toLowerCase()}Doc = await ${modelname.toLowerCase()}.findByEmail(req.params.email)
997 | console.log(${modelname.toLowerCase()}Doc)
998 | new JsonResponse(req, res).jsonSuccess(${modelname.toLowerCase()}Doc, new Messages().SUCCESSFULLY_RECEIVED)
999 | }
1000 |
1001 | exports.updateById = async function (req, res) {
1002 | let ${modelname.toLowerCase()} = new ${modelname}();
1003 | let ${modelname.toLowerCase()}Doc = await ${modelname.toLowerCase()}.updateById(req.params.id, req.body);
1004 | new JsonResponse(req, res).jsonSuccess(${modelname.toLowerCase()}Doc, new Messages().SUCCESSFULLY_UPDATED);
1005 | };
1006 |
1007 | exports.updateByEmail = async function (req, res) {
1008 | let ${modelname.toLowerCase()} = new ${modelname}();
1009 | let ${modelname.toLowerCase()}Doc = await ${modelname.toLowerCase()}.updateByEmail(req.params.email, req.body);
1010 | new JsonResponse(req, res).jsonSuccess(${modelname.toLowerCase()}Doc, new Messages().SUCCESSFULLY_UPDATED);
1011 | };
1012 |
1013 |
1014 | exports.getAll${modelname}s = async function(req, res){
1015 | let ${modelname.toLowerCase()} = new ${modelname}()
1016 | let ${modelname.toLowerCase()}s = await ${modelname.toLowerCase()}.getAll${modelname}s()
1017 | new JsonResponse(req, res).jsonSuccess(${modelname.toLowerCase()}s, new Messages().SUCCESSFULLY_RECEIVED)
1018 | return ${modelname.toLowerCase()}s
1019 | }
1020 |
1021 | exports.deleteById= async function(req, res){
1022 | let ${modelname.toLowerCase()} = new ${modelname}();
1023 | await ${modelname.toLowerCase()}.deleteById()
1024 | new JsonResponse(req, res).jsonSuccess(true, new Messages().SUCCESSFULLY_DELETED)
1025 | }
1026 | `),
1027 | nonActorModelFileContent: (modelName, nonActorAttributes) =>
1028 | removeIndentation(`
1029 | const bcrypt = require("bcryptjs");
1030 | const Messages = require("../constants/Message");
1031 | const TryCatch = require("../helper/TryCatch");
1032 | const { ObjectId } = require('mongodb');
1033 | const ${modelName.toLowerCase()}sCollection = require("../db").db().collection("${modelName.toLowerCase()}");
1034 |
1035 | let ${modelName} = function (data) {
1036 | this.data = data;
1037 | this.errors = [];
1038 | };
1039 |
1040 | ${modelName}.prototype.cleanUp = function () {
1041 | // get rid of any bogus properties
1042 | this.data = {
1043 |
1044 | ${nonActorAttributes}
1045 | //predfined start
1046 | createdAt: new Date(),
1047 | //predefined end
1048 | };
1049 | };
1050 |
1051 | ${modelName}.prototype.create${modelName} = async function(){
1052 | this.cleanUp()
1053 | const ${modelName.toLowerCase()}Doc = await ${modelName.toLowerCase()}sCollection.insertOne(this.data);
1054 | return ${modelName.toLowerCase()}Doc
1055 | }
1056 |
1057 | ${modelName}.prototype.getById = async function (id){
1058 | let ${modelName.toLowerCase()}Doc = await ${modelName.toLowerCase()}sCollection.findOne({_id: new ObjectId(id)})
1059 | return ${modelName.toLowerCase()}Doc
1060 | }
1061 |
1062 | ${modelName}.prototype.updateById = async function (id, data) {
1063 | let ${modelName.toLowerCase()}Doc = await ${modelName.toLowerCase()}sCollection.findOneAndUpdate(
1064 | { _id: new ObjectId(id) },
1065 | {
1066 | $set: {
1067 | ...data,
1068 | updatedAt: new Date(),
1069 | },
1070 | },
1071 | {
1072 | returnDocument: "after",
1073 | }
1074 | );
1075 |
1076 | return ${modelName.toLowerCase()}Doc;
1077 | };
1078 |
1079 | ${modelName}.prototype.getAll${modelName}s = async function (){
1080 | let ${modelName.toLowerCase()}Doc = await ${modelName.toLowerCase()}sCollection.find({}).toArray()
1081 | return ${modelName.toLowerCase()}Doc
1082 | }
1083 |
1084 | ${modelName}.prototype.deleteById = async function (id){
1085 | await ${modelName.toLowerCase()}sCollection.deleteOne({_id: new ObjectId(id)})
1086 | return
1087 | }
1088 |
1089 | module.exports = ${modelName};
1090 | `),
1091 | actorModelFileContent: (modelName, attributes) =>
1092 | removeIndentation(`
1093 | const bcrypt = require("bcryptjs");
1094 | const Messages = require("../constants/Message");
1095 | const TryCatch = require("../helper/TryCatch");
1096 | const { ObjectId } = require('mongodb');
1097 | const ${modelName.toLowerCase()}sCollection = require("../db").db().collection("${modelName.toLowerCase()}");
1098 |
1099 | let ${modelName} = function (data) {
1100 | this.data = data;
1101 | this.errors = [];
1102 | };
1103 |
1104 | ${modelName}.prototype.cleanUp = function () {
1105 | // get rid of any bogus properties
1106 | this.data = {
1107 | //predfined start
1108 | name: this.data.name,
1109 | lName: this.data.lName,
1110 | email: this.data.email.trim().toLowerCase(),
1111 | password: this.data.password,
1112 | contactNumber: this.data.contactNumber,
1113 | address: this.data.address,
1114 | city: this.data.city,
1115 | role: "${modelName.toLowerCase()}",
1116 | createdAt: new Date(),
1117 | //predefined end
1118 | ${attributes}
1119 | };
1120 | };
1121 |
1122 | ${modelName}.prototype.login = async function () {
1123 | let attemptedUser = await ${modelName.toLowerCase()}sCollection.findOne({ email: this.data.email });
1124 | this.cleanUp();
1125 | if (
1126 | attemptedUser &&
1127 | bcrypt.compareSync(this.data.password, attemptedUser.password)
1128 | ) {
1129 | this.data = attemptedUser;
1130 | return true;
1131 | } else {
1132 | return false;
1133 | }
1134 | };
1135 |
1136 | ${modelName}.prototype.register =async function () {
1137 | this.cleanUp();
1138 |
1139 | let salt = bcrypt.genSaltSync(10);
1140 | this.data.password = bcrypt.hashSync(this.data.password, salt);
1141 | await ${modelName.toLowerCase()}sCollection.insertOne(this.data);
1142 | return true
1143 |
1144 | };
1145 |
1146 | ${modelName}.prototype.findByEmail = async function (email) {
1147 | let ${modelName.toLowerCase()}Doc = await ${modelName.toLowerCase()}sCollection.findOne({ email: email })
1148 | return ${modelName.toLowerCase()}Doc
1149 |
1150 | };
1151 |
1152 | ${modelName}.prototype.doesEmailExist = async function (email) {
1153 |
1154 | let ${modelName.toLowerCase()} = await ${modelName.toLowerCase()}sCollection.findOne({ email: email });
1155 | if (${modelName.toLowerCase()}) {
1156 | return true;
1157 | } else {
1158 | return false;
1159 | }
1160 | }
1161 |
1162 | ${modelName}.prototype.getById = async function (id){
1163 | let ${modelName.toLowerCase()}Doc = await ${modelName.toLowerCase()}sCollection.findOne({_id: new ObjectId(id)})
1164 | return ${modelName.toLowerCase()}Doc
1165 | }
1166 |
1167 | ${modelName}.prototype.updateById = async function (id, data) {
1168 | let ${modelName.toLowerCase()}Doc = await ${modelName.toLowerCase()}sCollection.findOneAndUpdate(
1169 | { _id: new ObjectId(id) },
1170 | {
1171 | $set: {
1172 | name: data.name,
1173 | lName: data.lName,
1174 | email: data.email,
1175 | contactNumber: data.contactNumber,
1176 | address: data.address,
1177 | city: data.city,
1178 | role: "${modelName.toLowerCase()}",
1179 | updatedAt: new Date(),
1180 | },
1181 | },
1182 | { returnDocument: "after" }
1183 | );
1184 | return ${modelName.toLowerCase()}Doc;
1185 | };
1186 |
1187 | ${modelName}.prototype.updateByEmail = async function (email, data) {
1188 | let ${modelName.toLowerCase()}Doc = await ${modelName.toLowerCase()}sCollection.findOneAndUpdate(
1189 | { email: email },
1190 | {
1191 | $set: {
1192 | name: data.name,
1193 | lName: data.lName,
1194 | email: data.email,
1195 | contactNumber: data.contactNumber,
1196 | address: data.address,
1197 | city: data.city,
1198 | role: "${modelName.toLowerCase()}",
1199 | updatedAt: new Date(),
1200 | },
1201 | },
1202 | { returnDocument: "after" }
1203 | );
1204 | return ${modelName.toLowerCase()}Doc;
1205 | };
1206 |
1207 |
1208 | ${modelName}.prototype.getAll${modelName}s = async function (){
1209 | let ${modelName.toLowerCase()}Doc = await ${modelName.toLowerCase()}sCollection.find({}).toArray()
1210 | return ${modelName.toLowerCase()}Doc
1211 | }
1212 |
1213 | ${modelName}.prototype.deleteById = async function (id){
1214 | await ${modelName.toLowerCase()}sCollection.deleteOne({_id: new ObjectId(id)})
1215 | return
1216 | }
1217 |
1218 | module.exports = ${modelName};
1219 | `),
1220 |
1221 | chatModelFileContent: removeIndentation(`
1222 | const chatsCollection = require('../db').db().collection("chats");
1223 |
1224 | const { ObjectId } = require('mongodb');
1225 | // const validator = require("validator")
1226 | // const md5 = require('md5')
1227 | let Chat = function(data){
1228 | this.data = data
1229 | this.errors =[]
1230 | }
1231 |
1232 | Chat.prototype.cleanUp = async function () {
1233 | // Clean up the data as before
1234 | this.data = {
1235 | senderName: this.data.senderName,
1236 | messageContent: this.data.messageContent,
1237 | image: this.data.image,
1238 | receiverId: new ObjectId(this.data.receiverId),
1239 | senderId: new ObjectId(this.data.senderId),
1240 | sentTime: new Date(),
1241 | };
1242 | }
1243 |
1244 | Chat.prototype.sendChat = async function(){
1245 | this.cleanUp()
1246 | try {
1247 | await chatsCollection.insertOne(this.data);
1248 | } catch (error) {
1249 | console.error("Error storing message in the database:", error);
1250 | }
1251 | };
1252 |
1253 | Chat.prototype.getChatConvo = async function(requesterId, chatContactId) {
1254 | try {
1255 | let chats = await chatsCollection.find({
1256 | $or: [
1257 | { senderId: new ObjectId(requesterId), receiverId: new ObjectId(chatContactId) },
1258 | { senderId: new ObjectId(chatContactId), receiverId: new ObjectId(requesterId) }
1259 | ]
1260 | }).toArray();
1261 | return chats;
1262 | } catch (error) {
1263 | console.error("Error retrieving chat conversation:", error);
1264 | }
1265 | };
1266 |
1267 | module.exports = Chat
1268 | `),
1269 | chatControllerFileContent: removeIndentation(`
1270 |
1271 | const Messages = require("../constants/Message");
1272 | const JsonResponse = require("../helper/JsonResponse");
1273 | const Chat = require("../models/Chat");
1274 |
1275 | exports.sendChat = async function(req, res){
1276 | let chat = new Chat(req.body);
1277 | await chat.sendChat();
1278 | new JsonResponse(req, res).jsonSuccess("sent", new Messages().SUCCESSFULLY_RECEIVED)
1279 | }
1280 |
1281 | exports.getChatConvo = async function(req, res){
1282 |
1283 | let chat = new Chat();
1284 | let chats = await chat.getChatConvo(req.params.id, req.params.chatContactId);
1285 | new JsonResponse(req, res).jsonSuccess(chats, new Messages().SUCCESSFULLY_RECEIVED)
1286 |
1287 | }
1288 |
1289 | `),
1290 |
1291 | dockerFileContent: removeIndentation(`FROM node
1292 |
1293 | WORKDIR /app
1294 |
1295 | COPY package.json .
1296 |
1297 | RUN npm install
1298 |
1299 | COPY . .
1300 |
1301 | EXPOSE 4000
1302 |
1303 | CMD ["npm","run", "server"]`),
1304 |
1305 |
1306 | llmUsingOllamaRouterContent: removeIndentation(`
1307 | const express = require('express');
1308 | const router = express.Router();
1309 | const AuthHelper = require('../helper/JWTAuthHelper');
1310 | const TryCatch = require('../helper/TryCatch');
1311 | const Messages = require('../constants/Message');
1312 | const LlmHelperOllama = require('../helper/LlmHelperOllama');
1313 |
1314 |
1315 |
1316 | //imports here
1317 |
1318 | //code here
1319 |
1320 | //LlmUsingOllama --start
1321 |
1322 | router.post("/ask-llm", AuthHelper.verifyToken, new TryCatch(LlmHelperOllama.llmModel).tryCatchGlobe())
1323 |
1324 | //LlmUsingOllama --end
1325 |
1326 | module.exports = router;
1327 | `),
1328 |
1329 | llmUsingOllamaHelperFileContent: removeIndentation(`
1330 |
1331 | const { ChatOllama } = require("@langchain/ollama")
1332 | const { ChatPromptTemplate } = require("@langchain/core/prompts")
1333 | const { SystemMessage, HumanMessage, AIMessage } = require("@langchain/core/messages")
1334 | const JsonResponse = require("./JsonResponse")
1335 | const Messages = require("../constants/Message")
1336 | const axios = require("axios")
1337 | const conversationHistories = new Map()
1338 |
1339 | const getOrCreateHistory = sessionId => {
1340 | if (!conversationHistories.has(sessionId)) {
1341 | // Initialize conversation history with the system message if it doesn't exist
1342 | conversationHistories.set(sessionId, [new SystemMessage("You are a friend of anyone who talks to you! Your name is Servi")]) //Input The system prompt here.
1343 | }
1344 | return conversationHistories.get(sessionId)
1345 | }
1346 |
1347 | exports.llmModel = async function (req, res) {
1348 | const chatModel = new ChatOllama({
1349 | baseUrl: "http://localhost:11434", // Default value
1350 | model: "llama3.1" //Change the name of the model to the one you want to use.
1351 | })
1352 | console.log(req.apiUser._id)
1353 | const sessionId = req.apiUser._id // Using a header for session ID;
1354 | const conversationHistory = getOrCreateHistory(sessionId)
1355 |
1356 | const sendMessage = async input => {
1357 | // Add user input to conversation history
1358 | conversationHistory.push(new HumanMessage(input))
1359 |
1360 | // Create the prompt with the current conversation history
1361 | const prompt = ChatPromptTemplate.fromMessages(conversationHistory)
1362 |
1363 | // Call the model with the updated prompt
1364 | const response = await prompt.pipe(chatModel).invoke({})
1365 | // Add model's response to the conversation history
1366 | conversationHistory.push(new AIMessage(response.content))
1367 | // Return the response content
1368 | return response.content
1369 | }
1370 |
1371 | // Extract the user input from the request body
1372 | console.log(req.body.input)
1373 | const userInput = req.body.input
1374 |
1375 | // Send the user input to the model and get the response
1376 | const modelResponse = await sendMessage(userInput)
1377 | // res.status(200).json({ response: modelResponse })
1378 |
1379 | new JsonResponse(req, res).jsonSuccess(modelResponse, new Messages().SUCCESSFULLY_GENERATED)
1380 | }
1381 |
1382 | `),
1383 | };
1384 | export default fileContent;
1385 |
--------------------------------------------------------------------------------