UNPKG

complete-auth-system

Version:

A powerful CLI tool to set up a complete authentication system for MERN stack projects, featuring user registration, email verification, 2FA, OTP-based login, password authentication, and advanced security measures like account lock after failed attempts.

275 lines (232 loc) 9.86 kB
#!/usr/bin/env node const { execSync } = require("child_process"); const fs = require("fs-extra"); const path = require("path"); const git = require("simple-git")(); const readline = require("readline"); const FRONTEND_REPO = "https://github.com/RaghavOG/complete-auth-frontend.git"; const BACKEND_REPO = "https://github.com/RaghavOG/complete-mern-auth.git"; // Animation frames for loading spinner const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']; // Function to create animated loading indicator function createLoader(message) { let i = 0; const loader = setInterval(() => { readline.clearLine(process.stdout, 0); readline.cursorTo(process.stdout, 0); process.stdout.write(`${frames[i]} ${message}`); i = (i + 1) % frames.length; }, 80); return { stop: (successMessage) => { clearInterval(loader); readline.clearLine(process.stdout, 0); readline.cursorTo(process.stdout, 0); if (successMessage) { console.log(`✅ ${successMessage}`); } }, fail: (errorMessage) => { clearInterval(loader); readline.clearLine(process.stdout, 0); readline.cursorTo(process.stdout, 0); console.log(`❌ ${errorMessage}`); } }; } // Animated banner function // Improved banner function - Option 1: Display at once function displayBanner() { const banner = ` ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ __ __ _____ ____ _ _ ┃ ┃ | \\/ | ____| _ \\| \\ | | ┃ ┃ | |\\/| | _| | |_) | \\| | ┃ ┃ | | | | |___| _ <| |\\ | ┃ ┃ |_| |_|_____|_| \\_\\_| \\_| ┃ ┃ ┃ ┃ Complete Auth System Generator ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ `; console.log(banner); return Promise.resolve(); } // Alternative Option 2: Display with animation but fix the issue function displayBannerAnimated() { // Clear any previous content console.clear(); const bannerLines = [ " ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓", " ┃ __ __ _____ ____ _ _ ┃", " ┃ | \\/ | ____| _ \\| \\ | | ┃", " ┃ | |\\/| | _| | |_) | \\| | ┃", " ┃ | | | | |___| _ <| |\\ | ┃", " ┃ |_| |_|_____|_| \\_\\_| \\_| ┃", " ┃ ┃", " ┃ Complete Auth System Generator ┃", " ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛" ]; let displayedLines = 0; return new Promise(resolve => { const interval = setInterval(() => { if (displayedLines >= bannerLines.length) { clearInterval(interval); console.log('\n'); resolve(); return; } console.log(bannerLines[displayedLines]); displayedLines++; }, 100); }); } // Function to animate text with typing effect async function animateText(text) { const chars = text.split(''); let displayedText = ''; return new Promise(resolve => { const interval = setInterval(() => { if (chars.length === 0) { clearInterval(interval); console.log(''); resolve(); return; } displayedText += chars.shift(); readline.clearLine(process.stdout, 0); readline.cursorTo(process.stdout, 0); process.stdout.write(displayedText); }, 20); }); } // Progress bar function function createProgressBar(total, message) { const barLength = 30; let current = 0; const update = (increment) => { current += increment; const progress = Math.min(Math.floor((current / total) * barLength), barLength); const percentage = Math.min(Math.floor((current / total) * 100), 100); readline.clearLine(process.stdout, 0); readline.cursorTo(process.stdout, 0); process.stdout.write(`${message} [${Array(progress).fill('█').join('')}${Array(barLength - progress).fill(' ').join('')}] ${percentage}%`); if (current >= total) { process.stdout.write('\n'); } }; return { update }; } // Parse command line arguments function parseArgs() { const args = {}; const argv = process.argv.slice(2); args.projectName = argv[0]; for (let i = 1; i < argv.length; i++) { if (argv[i] === '--frontend' && i + 1 < argv.length) { args.frontendName = argv[i + 1]; i++; } else if (argv[i] === '--backend' && i + 1 < argv.length) { args.backendName = argv[i + 1]; i++; } } return args; } // Display help message async function displayHelp() { await animateText("Usage: npx script-name <project-name> [options]"); console.log("\nOptions:"); console.log(" --frontend <name> Custom name for the frontend folder (default: frontend)"); console.log(" --backend <name> Custom name for the backend folder (default: backend)"); console.log("\nExample:"); console.log(" npx script-name my-auth-app --frontend client --backend server"); process.exit(0); } async function createProject() { // Display banner await displayBanner(); // Parse command line arguments const args = parseArgs(); const projectName = args.projectName; const frontendFolderName = args.frontendName || "frontend"; const backendFolderName = args.backendName || "backend"; if (!projectName) { await animateText("❌ Please provide a project folder name."); console.log(""); await displayHelp(); return; } if (process.argv.includes("--help") || process.argv.includes("-h")) { await displayHelp(); return; } const projectPath = path.join(process.cwd(), projectName); await animateText(`📂 Creating project in: ${projectPath}`); fs.ensureDirSync(projectPath); await animateText(`📁 Frontend folder: ${frontendFolderName}`); await animateText(`📁 Backend folder: ${backendFolderName}`); try { // Clone frontend with animation const frontendLoader = createLoader(`Cloning frontend repository into ${frontendFolderName}...`); await git.clone(FRONTEND_REPO, path.join(projectPath, frontendFolderName)) .then(() => { frontendLoader.stop(`Frontend repository cloned successfully into ${frontendFolderName}!`); }) .catch((err) => { frontendLoader.fail(`Failed to clone frontend: ${err.message}`); throw err; }); // Clone backend with animation const backendLoader = createLoader(`Cloning backend repository into ${backendFolderName}...`); await git.clone(BACKEND_REPO, path.join(projectPath, backendFolderName)) .then(() => { backendLoader.stop(`Backend repository cloned successfully into ${backendFolderName}!`); }) .catch((err) => { backendLoader.fail(`Failed to clone backend: ${err.message}`); throw err; }); // Install frontend dependencies with progress bar await animateText(`📦 Installing ${frontendFolderName} dependencies...`); const frontendProgressBar = createProgressBar(100, `Installing ${frontendFolderName} packages`); let frontendProgressValue = 0; const frontendInterval = setInterval(() => { const increment = Math.random() * 10; frontendProgressValue += increment; if (frontendProgressValue > 100) { clearInterval(frontendInterval); frontendProgressValue = 100; } frontendProgressBar.update(increment); }, 300); execSync(`cd ${path.join(projectPath, frontendFolderName)} && npm install`, { stdio: 'ignore' }); clearInterval(frontendInterval); frontendProgressBar.update(100); // Install backend dependencies with progress bar await animateText(`📦 Installing ${backendFolderName} dependencies...`); const backendProgressBar = createProgressBar(100, `Installing ${backendFolderName} packages`); let backendProgressValue = 0; const backendInterval = setInterval(() => { const increment = Math.random() * 10; backendProgressValue += increment; if (backendProgressValue > 100) { clearInterval(backendInterval); backendProgressValue = 100; } backendProgressBar.update(increment); }, 300); execSync(`cd ${path.join(projectPath, backendFolderName)} && npm install`, { stdio: 'ignore' }); clearInterval(backendInterval); backendProgressBar.update(100); // Completion animation console.log("\n"); await animateText("🎉 Project setup complete!"); console.log("\n"); await animateText(`To start the project:`); console.log(` cd ${projectName}/${frontendFolderName} && npm run dev`); console.log(` cd ${projectName}/${backendFolderName} && npm start`); } catch (error) { console.error("❌ Error setting up the project:", error); } } createProject();