├── .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 | Servitect Logo 9 |
10 | 11 | [![npm version](https://badge.fury.io/js/servitect.svg)](https://badge.fury.io/js/servitect) 12 | [![npm downloads](https://img.shields.io/npm/dm/servitect.svg)](https://www.npmjs.com/package/servitect) 13 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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 | image 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 | --------------------------------------------------------------------------------