UNPKG

build-capibara

Version:

A CLI tool to create a Capibara project structure.

678 lines (567 loc) â€ĸ 24 kB
#!/usr/bin/env node const fs = require('fs'); const path = require('path'); const { exec } = require('child_process'); const readline = require('readline'); // Define the folder structure const structure = { '.capi': { 'additional': { 'cli': { 'create.js': ` const fs = require('fs'); const path = require('path'); const readline = require('readline'); const createutilitySystemForRoles = require("./utility"); // Function to create a directory if it doesn't exist const createDirectory = (dirPath) => { if (!fs.existsSync(dirPath)) { fs.mkdirSync(dirPath, { recursive: true }); console.log(\`Directory created: \${dirPath}\`); } }; // Function to create a file with a basic template const createFile = (filePath, template) => { fs.writeFileSync(filePath, template); console.log(\`File created: \${filePath}\`); }; // Main function to handle the creation of controllers, middleware, and models const createCapiControllerFile = (folderName, fileName) => { const baseDir = path.join('./workspace', 'controller'); const dirPath = folderName ? path.join(baseDir, folderName) : baseDir; createDirectory(dirPath); const template = \`// Controller: \${fileName} require('module-alias/register'); const capi = require("@capi"); const \${fileName}Controller = (req, res) => { const { name } = req.body; // Return a new Promise return new Promise((resolve, reject) => { try { // Simulate some asynchronous operation if needed setTimeout(() => { // Resolve the promise with the response resolve(res.status(201).json({ message: name })); }, 0); } catch (error) { // Reject the promise with the error reject(res.status(500).json({ error: error.message })); } }); }; module.exports = \${fileName}Controller;\`; const filePath = path.join(dirPath, \`\${fileName}.js\`); createFile(filePath, template); }; const createCapiModelsFile = (folderName, fileName) => { const baseDir = path.join('./workspace', 'models'); const dirPath = folderName ? path.join(baseDir, folderName) : baseDir; createDirectory(dirPath); const template = \`// Model: \${fileName} const { capiMongo } = require("@capi"); const \${fileName}SchemaDefinition = { name: capiMongo.String(true).def("Name"), //Field Name : name, Type : String(Required = true), Default("Name") description: capiMongo.String().def("this is Description") //Field Name : description, Type : String(Required = false), Default("this is Description") }; const \${fileName} = capiMongo.Model('\${fileName}', \${fileName}SchemaDefinition); module.exports = \${fileName};\`; const filePath = path.join(dirPath, \`\${fileName}.js\`); createFile(filePath, template); }; const createCapiMiddlewareFile = (folderName, fileName) => { const baseDir = path.join('./workspace', 'middleware'); const dirPath = folderName ? path.join(baseDir, folderName) : baseDir; createDirectory(dirPath); const template = \`// Middleware: \${fileName} const { capi } = require("@capi"); const middleware = (req, res, next) => { next(); }; module.exports = middleware;\`; const filePath = path.join(dirPath, \`\${fileName}.js\`); createFile(filePath, template); }; // Create readline interface const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); // Function to ask questions const askQuestions = () => { rl.question('Type? (controller/middleware/models/utility): ', (type) => { if (!['controller', 'middleware', 'models', 'utility'].includes(type)) { console.error('Invalid type. Use "controller", "middleware", or "models", or "utility".'); rl.close(); return; } if (type === "utility") { createutilitySystemForRoles(); } rl.question('Folder name? (Press Enter to skip): ', (folderName) => { folderName = folderName || ''; rl.question('File names? (use comma to add more file): ', (fileNames) => { if (!fileNames) { console.error('File names cannot be empty.'); rl.close(); return; } const fileNameArray = fileNames.split(',').map(name => name.trim()); console.log('Creating Files...'); fileNameArray.forEach(fileName => { if (type === "controller") { createCapiControllerFile(folderName, fileName); } else if (type === "models") { createCapiModelsFile(folderName, fileName); } else if (type === "middleware") { createCapiMiddlewareFile(folderName, fileName); } }); console.log('Success!'); rl.close(); }); }); }); }; // Start asking questions askQuestions(); `, 'utility.js': ` const fs = require('fs'); const path = require('path'); const readline = require('readline'); const createDirectory = (dirPath) => { if (!fs.existsSync(dirPath)) { fs.mkdirSync(dirPath, { recursive: true }); console.log(\`Directory created: \${dirPath}\`); } }; const createFile = (filePath, template) => { fs.writeFileSync(filePath, template); console.log(\`File created: \${filePath}\`); }; const createutilitySystemForRoles = () => { const baseDir = path.join('./workspace', 'controller'); const dirPath = path.join(baseDir, "CapiAuthentication"); createDirectory(dirPath); const template = \`// Controller: AuthMiddleware const test = 1; \`; const filePath = path.join(dirPath, \`AuthMiddleware.js\`); createFile(filePath, template); } module.exports = createutilitySystemForRoles; `, }, 'connection.js': ` const mongoose = require("mongoose"); require('module-alias/register') const {db, corsOptions} = require("@capiConfig") mongoose.connect(db.uri, { useNewUrlParser: true, useUnifiedTopology: true }) .then(() => { console.log('MongoDB connected successfully'); }) .catch(err => { console.error('MongoDB connection error:', err); }); `, 'sanitize.js': ` const mongoose = require("mongoose"); require('module-alias/register') const sanitizeBody = (req, model) => { const allowedFields = Object.keys(model.schema.paths); return Object.keys(req.body).every(field => allowedFields.includes(field)); }; const sanitizeTypes = (req, model) => { const allowedFields = Object.keys(model.schema.paths); return allowedFields.every(field => { if (req.body[field] !== undefined) { const expectedType = model.schema.paths[field].instance; const actualType = typeof req.body[field]; return expectedType === 'String' && actualType === 'string' || expectedType === 'Number' && actualType === 'number' || expectedType === 'Boolean' && actualType === 'boolean' || expectedType === 'ObjectID' && mongoose.Types.ObjectId.isValid(req.body[field]) || expectedType === 'Array' && Array.isArray(req.body[field]); } return true; }); }; const validateFields = (body, model) => { // Assuming model has a schema with required fields const requiredFields = Object.keys(model.schema.paths).filter(key => model.schema.paths[key].isRequired); for (const field of requiredFields) { if (!body[field] || body[field].trim() === '') { return false; // Field is missing or empty } } return true; // All required fields are valid }; module.exports = {sanitizeBody, sanitizeTypes, validateFields} ` }, '.capi.js': ` /* CAPIBARA FRAMEWORK BY ANDREW MADE IN 2024 VER 1.0.6 STILL IN PROGRESS NEXT LIST OF WORK - CUSTOM FUNCTION FOR EVERY ENDPOINT - ROLE BASE SYSTEM - RESTRICT ENDPOINT BY URL - JWT TOKEN GENERATOR FOR PUBLIC ETC - PUT - DEL THE VERY VERY SOON - FRONT END DEVELOPMENT KINDA LIKE NEXT JS USING REACT - MYSQL SUPPORT - NPX INSTALL */ require("dotenv").config(); require('module-alias/register') require("@connection") const express = require('express'); const bcrypt = require("bcrypt"); const jwt = require("jsonwebtoken"); const {sanitizeBody, sanitizeTypes} = require("@additional/sanitize"); const mongoose = require('mongoose'); const app = express(); const TOKEN = process.env.TOKEN; app.use(express.json()); class capi { constructor(app) { this.app = app; } Get(model, endpoint, middlewareOrOptions, options = { findbyId: false, strict: false, pagination: false }) { let middleware = null; if (typeof middlewareOrOptions === 'function') { middleware = middlewareOrOptions; } else if (typeof middlewareOrOptions === 'object' && middlewareOrOptions !== null) { options = middlewareOrOptions; } options = options || {}; this.app.get(endpoint, middleware || ((req, res, next) => next()), async (req, res) => { try { let result; if (options.findbyId) { const id = req.params.id; if (!mongoose.Types.ObjectId.isValid(id)) { return res.status(404).send("Invalid Object Id"); } const validate = await model.findById(id); if (!validate) { return res.status(404).send("Invalid Object Id"); } result = validate; } else { const queryData = req.query; // Pagination logic if (options.pagination) { const limit = parseInt(req.query.limit) || 10; // Default limit const page = parseInt(req.query.page) || 1; // Default page const skip = (page - 1) * limit; result = await model.find(queryData).limit(limit).skip(skip); const totalCount = await model.countDocuments(queryData); const totalPages = Math.ceil(totalCount / limit); // Include pagination info in the response res.json({ totalCount, totalPages, currentPage: page, limit, data: result }); } else { result = await model.find(queryData); res.json(result); } } } catch (error) { res.status(500).json({ error: error.message }); } }); } Post(model, endpoint, middlewareOrOptions, options = { users: false, strict: false }, customfunc) { let middleware = null; if (typeof middlewareOrOptions === 'function') { middleware = middlewareOrOptions; } else if (typeof middlewareOrOptions === 'object' && middlewareOrOptions !== null) { options = middlewareOrOptions; } options = options || {}; // Define the default request handler const defaultHandler = async (req, res) => { try { const sanitizedData = req.body; const isBodyValid = sanitizeBody(req, model); const isTypesValid = sanitizeTypes(req, model); if (options.users) { if (sanitizedData.password) { sanitizedData.password = bcrypt.hashSync(sanitizedData.password, 10); } if (sanitizedData.Password) { sanitizedData.Password = bcrypt.hashSync(sanitizedData.Password, 10); } } if (options.users && options.strict) { if (req.body.email) { const email = await model.findOne({ email: sanitizedData.email }); if (email) { return res.status(400).json({ error: "Email already exists" }); } } if (req.body.username) { const user = await model.findOne({ username: sanitizedData.username }); if (user) { return res.status(400).json({ error: "Username already exists" }); } } } if (!isTypesValid) return res.status(400).json({ error: "Invalid input data!" }); if (!isBodyValid) return res.status(400).json({ error: "Invalid field!" }); const newDocument = new model(sanitizedData); await newDocument.save(); res.status(201).json({ message: "Document created successfully", data: newDocument }); } catch (error) { res.status(500).json({ error: error.message }); } }; this.app.post(endpoint, middleware || ((req, res, next) => next()), async (req, res) => { if (customfunc) { await customfunc(req, res); } else { await defaultHandler(req, res); } }); } Login(model, endpoint, middleware = null) { this.app.post(endpoint, middleware || ((req, res, next) => next()), async (req, res) => { try { const sanitizedData = req.body; const isBodyValid = sanitizeBody(req, model); const isTypesValid = sanitizeTypes(req, model); if (!isTypesValid || !isBodyValid) { return res.status(400).json({ error: "Invalid input data!" }); } const username = sanitizedData.username; const email = sanitizedData.email; let user // Find user by username or email if(username){ user = await model.findOne({username: username}); }else if(email){ user = await model.findOne({email: email}) } console.log(user) if (!user) { return res.status(401).json({ error: "Invalid credentials!" }); } // Check if password is provided const inputPassword = sanitizedData.password; // Use sanitizedData.Password if (!inputPassword) { return res.status(400).json({ error: "Password is required!" }); } // Log the user password and input password for debugging console.log("Input Password:", inputPassword); console.log("User Password (hashed):", user.password || user.Password); // Validate the password const userPassword = user.password || user.Password; // Assuming user.Password is the alternative field const isPasswordValid = bcrypt.compareSync(inputPassword, userPassword); if (!isPasswordValid) { return res.status(401).json({ error: "Invalid credentials!" }); } // Generate a JWT token const token = jwt.sign({ username: user.username }, TOKEN, { expiresIn: '1h' }); res.json({ message: "Login successful", token }); } catch (error) { res.status(500).json({ error: error.message }); } }); } generateToken(user) { return "your_generated_token"; } } class capiDb { constructor(mongoose) { this.mongoose = mongoose; } /** * Generates a Mongoose model. * @param {string} modelName - The name of the model. * @param {Object} schemaDefinition - The schema definition for the model. * @returns {mongoose.Model} - The generated Mongoose model. */ Model(modelName, schemaDefinition) { const schema = new this.mongoose.Schema(schemaDefinition); return this.mongoose.model(modelName, schema); } // Create a new field definition object createFieldDefinition(type, required = false) { const fieldDefinition = { type, required, default: undefined, // Initialize default as undefined def: (value = "" && required === false) => { fieldDefinition.default = value; // Set the default value in the field definition return fieldDefinition; // Return the field definition for chaining }, set: function(value) { // If the value is an empty string, return the default value return value === "" ? this.default : value; } }; return fieldDefinition; } String(required = false) { const field = this.createFieldDefinition(String, required); field.set = field.set.bind(field); // Bind the setter to the field return field; } Number(required = false) { return this.createFieldDefinition(Number, required); } Bool(required = false) { return this.createFieldDefinition(Boolean, required); } Text(required = false) { return this.createFieldDefinition(String, required); // Use String for text } ObjectId(required = false, ref = null) { const definition = { type: this.mongoose.Schema.Types.ObjectId, required }; if (ref) definition.ref = ref; return definition; } } const PORT = process.env.PORT || 1337; app.listen(PORT, () => { console.log(\`Server is running on port \${PORT}\`); }); const capibara = new capi(app); const capiMongo = new capiDb(mongoose) module.exports = {capibara, capiMongo}; `, }, 'node_modules': {}, // Placeholder for node_modules 'workspace': { 'controller': {}, 'middleware': {}, 'models': {}, 'utils': {}, 'app.js': `require('module-alias/register') const {capibara} = require("@capi")`, }, '.env': ` MONGODB_URI=mongodb+srv://alluserdev347:aj6YJft4WMRPKGVw@sulutinfo.tmsbe.mongodb.net/capibaratest?retryWrites=true&w=majority&appName=sulutinfo TOKEN=JWkCapItC*\ID12233345@TKnVs `, '.gitignore': './node_modules', 'capi.config.js': ` require("dotenv").config(); const db = { uri: process.env.MONGODB_URI } const corsOptions = { origin:"", credentials: false } module.exports = {db, corsOptions} `, 'package-lock.json': '', 'package.json': JSON.stringify({ "name": "capibara", "version": "1.0.0", "main": "index.js", "type": "commonjs", "scripts": { "dev": "nodemon ./workspace/app", "create": "node .capi/additional/cli/create.js" }, "keywords": [], "author": "", "license": "ISC", "description": "", "dependencies": { "bcrypt": "^5.1.1", "body-parser": "^1.20.3", "cors": "^2.8.5", "dotenv": "^16.4.7", "dotnet": "^1.1.4", "express": "^4.21.2", "jsonwebtoken": "^9.0.2", "module-alias": "^2.2.3", "mongoose": "^8.10.0", "nodemon": "^3.1.9" }, "_moduleAliases": { "@capiConfig": "./capi.config.js", "@capi": "./.capi/.capi", "@connection": "./.capi/additional/connection", "@additional": "./.capi/additional", "models": "./workspace/models", "controller": "./workspace/controller", "middleware": "./workspace/middleware", "utils": "./workspace/utils" } }, null, 4), 'Capi.md': '# Project Title\n\nBasic Script For Setup :\n- npm run create\nBasic running Script :\n- npm run dev', }; // Function to create the folder structure const createStructure = (basePath, structure) => { for (const key in structure) { const newPath = path.join(basePath, key); if (typeof structure[key] === 'object') { fs.mkdirSync(newPath, { recursive: true }); createStructure(newPath, structure[key]); } else { fs.writeFileSync(newPath, structure[key]); } } }; // Read user inputs using readline const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); rl.question('Are you sure you want to create this App? (Yes/No): ', (answer) => { if (answer.toLowerCase() === 'yes' || answer.toLowerCase() === 'y') { rl.question('Application Name: ', (dirName) => { fs.mkdirSync(dirName, { recursive: true }); createStructure(path.join(process.cwd(), dirName), structure); // Create a basic .env file fs.writeFileSync(path.join(dirName, '.env'), ''); // Start loading animation // Start loading animation with more interesting messages const loadingMessages = [ '🚀 Going to Capibara Planet', '🌍 Almost There.', 'đŸĻĢ Waking the capibara..', '🔧 Setting Things Up...', 'âš™ī¸ Just a Few More Seconds....' ]; let loadIndex = 0; const loadingInterval = setInterval(() => { process.stdout.clearLine(); process.stdout.cursorTo(0); process.stdout.write(loadingMessages[loadIndex]); loadIndex = (loadIndex + 1) % loadingMessages.length; }, 1000); // Increased time for better visibility // Initialize npm and install packages exec(`cd ${dirName} && npm i && code .`, (error, stdout, stderr) => { clearInterval(loadingInterval); // Stop loading animation process.stdout.clearLine(); process.stdout.cursorTo(0); if (error) { console.error(`exec error: ${error}`); return; } console.log(`\nInstallation Output:\n${stdout}`); console.log(`Success! Project Created. Use "cd ${dirName}" and "npm run dev" to start the app.`); rl.close(); }); }); } else { console.log('Project creation aborted.'); rl.close(); } });