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
JavaScript
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();