UNPKG

veko

Version:

๐Ÿš€ Ultra-modern Node.js framework with hot reload, plugins, authentication, and advanced security features

737 lines (657 loc) โ€ข 24.1 kB
const chalk = require('chalk'); const inquirer = require('inquirer'); const ora = require('ora'); const boxen = require('boxen'); const figlet = require('figlet'); const gradient = require('gradient-string'); const { createSpinner } = require('nanospinner'); const path = require('path'); const fs = require('fs').promises; /** * Assistant de configuration interactif pour les projets Veko.js * Fournit une interface utilisateur complรจte pour la crรฉation de projets */ class SetupWizard { constructor() { this.config = { projectName: '', template: 'default', features: [], database: 'sqlite', auth: { enabled: false }, plugins: [], styling: 'bootstrap', theme: 'light', git: true, install: true, description: '', author: '', license: 'MIT', scripts: true, docker: false, env: true }; // Configuration de sรฉcuritรฉ this.securityConfig = { maxProjectNameLength: 50, maxDescriptionLength: 200, maxAuthorLength: 100, allowedFileNameChars: /^[a-zA-Z0-9\-_]+$/, maxFeatures: 20, maxPlugins: 15 }; // Templates prรฉdรฉfinis this.templates = new Map([ ['default', { name: '๐ŸŒŸ Default - Full-featured web application', description: 'Complete web application with all features', files: ['views/', 'routes/', 'public/', 'plugins/', 'middleware/', 'app.js'] }], ['api', { name: '๐Ÿ”Œ API Only - REST API server', description: 'RESTful API server with authentication', files: ['routes/api/', 'middleware/', 'models/', 'tests/', 'server.js'] }], ['blog', { name: '๐Ÿ“ Blog - Content management system', description: 'Blog engine with admin interface', files: ['views/blog/', 'content/posts/', 'admin/', 'uploads/', 'blog.js'] }], ['admin', { name: '๐Ÿ‘‘ Admin Dashboard - Management interface', description: 'Administrative dashboard and management tools', files: ['admin/views/', 'dashboard/', 'auth/', 'api/admin/', 'admin.js'] }], ['ecommerce', { name: '๐Ÿ›๏ธ E-commerce - Online store', description: 'Complete e-commerce solution', files: ['shop/views/', 'products/', 'orders/', 'payments/', 'store.js'] }], ['portfolio', { name: '๐ŸŽญ Portfolio - Personal showcase', description: 'Personal portfolio and project showcase', files: ['portfolio/views/', 'projects/', 'gallery/', 'blog/', 'portfolio.js'] }], ['pwa', { name: '๐Ÿ“ฑ PWA - Progressive Web App', description: 'Progressive Web Application with offline support', files: ['pwa/', 'sw/', 'manifest/', 'offline/', 'pwa.js'] }] ]); } /** * Point d'entrรฉe principal du wizard */ async start() { try { console.clear(); await this.showWelcome(); await this.gatherProjectInfo(); await this.selectTemplate(); await this.selectFeatures(); await this.configureDatabase(); await this.configureAuth(); await this.selectPlugins(); await this.selectStyling(); await this.finalOptions(); await this.confirmOptions(); await this.executeSetup(); await this.showCompletion(); } catch (error) { await this.handleError(error); } } /** * Affichage de l'รฉcran d'accueil avec titre stylisรฉ */ async showWelcome() { try { const title = figlet.textSync('VEKO.JS', { font: 'ANSI Shadow', horizontalLayout: 'fitted' }); console.log(gradient.rainbow(title)); } catch (error) { // Fallback si figlet รฉchoue console.log(chalk.cyan.bold('โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—')); console.log(chalk.cyan.bold('โ•‘ VEKO.JS โ•‘')); console.log(chalk.cyan.bold('โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•')); } console.log(chalk.cyan.bold('\nโœจ Interactive Project Setup Wizard โœจ\n')); const welcomeBox = boxen( chalk.white('๐ŸŽ‰ Welcome to Veko.js Setup Wizard!\n\n') + chalk.gray('This wizard will guide you through creating a new\n') + chalk.gray('Veko.js project with all the features you need.\n\n') + chalk.blue('โœ“ Templates & Examples\n') + chalk.blue('โœ“ Authentication System\n') + chalk.blue('โœ“ Database Integration\n') + chalk.blue('โœ“ Plugin Ecosystem\n') + chalk.blue('โœ“ Beautiful UI Frameworks'), { padding: 1, margin: 1, borderStyle: 'double', borderColor: 'cyan', textAlignment: 'center' } ); console.log(welcomeBox); const { ready } = await inquirer.prompt([{ type: 'confirm', name: 'ready', message: '๐Ÿš€ Ready to create something amazing?', default: true }]); if (!ready) { console.log(chalk.yellow('\n๐Ÿ‘‹ See you later! Happy coding!')); process.exit(0); } } /** * Collecte des informations de base du projet */ async gatherProjectInfo() { console.log(chalk.blue.bold('\n๐Ÿ“ Project Information\n')); const questions = [ { type: 'input', name: 'projectName', message: '๐Ÿ“ What\'s your project name?', default: 'my-veko-app', validate: (input) => this.validateProjectName(input) }, { type: 'input', name: 'description', message: '๐Ÿ“„ Project description:', default: 'A modern web application built with Veko.js', validate: (input) => this.validateDescription(input) }, { type: 'input', name: 'author', message: '๐Ÿ‘ค Author name:', default: process.env.USER || process.env.USERNAME || '', validate: (input) => this.validateAuthor(input) }, { type: 'list', name: 'license', message: '๐Ÿ“œ Choose a license:', choices: [ { name: '๐Ÿ“‹ MIT - Most permissive', value: 'MIT' }, { name: '๐Ÿ”’ ISC - Simple and permissive', value: 'ISC' }, { name: 'โš–๏ธ Apache-2.0 - Patent protection', value: 'Apache-2.0' }, { name: '๐Ÿ†“ GPL-3.0 - Copyleft', value: 'GPL-3.0' }, { name: '๐Ÿšซ Unlicense - Public domain', value: 'Unlicense' } ], default: 'MIT' } ]; const answers = await inquirer.prompt(questions); Object.assign(this.config, this.sanitizeProjectInfo(answers)); } /** * Sรฉlection du template de projet */ async selectTemplate() { console.log(chalk.blue.bold('\n๐ŸŽจ Choose Your Template\n')); const templateChoices = Array.from(this.templates.entries()).map(([value, template]) => ({ name: template.name, value })); const { template } = await inquirer.prompt([{ type: 'list', name: 'template', message: '๐ŸŽฏ Select a template:', choices: templateChoices, pageSize: 10 }]); this.config.template = template; this.showTemplatePreview(template); } /** * Affichage de l'aperรงu du template sรฉlectionnรฉ */ showTemplatePreview(templateName) { const template = this.templates.get(templateName); if (!template) return; const preview = template.files.map(file => `๐Ÿ“ ${file}`).join('\n'); const previewBox = boxen( chalk.cyan('๐Ÿ“‹ Template Structure:\n\n') + chalk.gray(preview) + '\n\n' + chalk.blue('Description: ') + chalk.white(template.description), { padding: 1, margin: { top: 1, bottom: 1, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'blue', title: '๐Ÿ“ฆ Project Structure', titleAlignment: 'center' } ); console.log(previewBox); } /** * Sรฉlection des fonctionnalitรฉs */ async selectFeatures() { console.log(chalk.blue.bold('\nโšก Select Features & Add-ons\n')); const featureChoices = [ { name: '๐Ÿ”ฅ Hot Reload Development Server', value: 'hotreload', checked: true }, { name: '๐Ÿ“ฑ Progressive Web App (PWA)', value: 'pwa' }, { name: '๐ŸŽจ Advanced Layout System', value: 'layouts', checked: true }, { name: '๐Ÿ” SEO Optimization', value: 'seo' }, { name: '๐Ÿ“Š Analytics Integration', value: 'analytics' }, { name: '๐Ÿ’ฌ Real-time WebSocket Support', value: 'websocket' }, { name: '๐Ÿ“ง Email System (Nodemailer)', value: 'email' }, { name: '๐Ÿ”’ Rate Limiting & Security', value: 'security' }, { name: '๐Ÿ“ File Upload System', value: 'upload' }, { name: '๐ŸŒ Multi-language (i18n)', value: 'i18n' }, { name: '๐Ÿ“‹ Form Validation', value: 'validation' }, { name: '๐ŸŽญ Component System', value: 'components' }, { name: '๐Ÿ—œ๏ธ Image Processing', value: 'imageprocessing' }, { name: '๐Ÿ”„ Backup System', value: 'backup' } ]; const { features } = await inquirer.prompt([{ type: 'checkbox', name: 'features', message: '๐ŸŽ Which features would you like to include?', choices: featureChoices, pageSize: 15, validate: (input) => this.validateFeatures(input) }]); this.config.features = features; } /** * Configuration de la base de donnรฉes */ async configureDatabase() { const dbRequiredTemplates = ['api', 'blog', 'admin', 'ecommerce']; if (dbRequiredTemplates.includes(this.config.template)) { console.log(chalk.blue.bold('\n๐Ÿ—„๏ธ Database Configuration\n')); const databaseChoices = [ { name: '๐Ÿ“„ SQLite - File-based (recommended for dev)', value: 'sqlite' }, { name: '๐Ÿ˜ PostgreSQL - Advanced relational database', value: 'postgresql' }, { name: '๐Ÿฌ MySQL - Popular relational database', value: 'mysql' }, { name: '๐Ÿƒ MongoDB - Document database', value: 'mongodb' }, { name: 'โšก Redis - In-memory cache/database', value: 'redis' }, { name: '๐Ÿšซ None - No database', value: 'none' } ]; const { database } = await inquirer.prompt([{ type: 'list', name: 'database', message: '๐Ÿ’พ Choose your database:', choices: databaseChoices }]); this.config.database = database; } } /** * Configuration du systรจme d'authentification */ async configureAuth() { const authRequiredTemplates = ['default', 'blog', 'admin', 'ecommerce']; if (authRequiredTemplates.includes(this.config.template)) { console.log(chalk.blue.bold('\n๐Ÿ” Authentication System\n')); const { enableAuth } = await inquirer.prompt([{ type: 'confirm', name: 'enableAuth', message: '๐Ÿ”‘ Enable authentication system?', default: ['admin', 'ecommerce'].includes(this.config.template) }]); if (enableAuth) { const authConfig = await this.configureAuthDetails(); this.config.auth = { enabled: true, ...authConfig }; } else { this.config.auth = { enabled: false }; } } } /** * Configuration dรฉtaillรฉe de l'authentification */ async configureAuthDetails() { return await inquirer.prompt([ { type: 'checkbox', name: 'methods', message: '๐Ÿšช Authentication methods:', choices: [ { name: '๐Ÿ“ง Email/Password (Local)', value: 'local', checked: true }, { name: '๐ŸŒ Google OAuth', value: 'google' }, { name: '๐Ÿ“˜ Facebook OAuth', value: 'facebook' }, { name: '๐Ÿ™ GitHub OAuth', value: 'github' }, { name: '๐Ÿ’ผ LinkedIn OAuth', value: 'linkedin' }, { name: '๐Ÿ”— JWT Tokens', value: 'jwt' } ], validate: (input) => input.length > 0 || 'At least one method is required' }, { type: 'checkbox', name: 'features', message: '๐Ÿ›ก๏ธ Authentication features:', choices: [ { name: '๐Ÿ‘ค User profiles', value: 'profiles', checked: true }, { name: '๐Ÿ‘‘ Role-based access control', value: 'roles' }, { name: '๐Ÿ“ง Email verification', value: 'emailVerification' }, { name: '๐Ÿ”„ Password reset', value: 'passwordReset' }, { name: '๐Ÿ”’ Two-factor authentication', value: '2fa' }, { name: '๐Ÿ“Š Login analytics', value: 'analytics' }, { name: '๐Ÿšซ Account lockout', value: 'lockout' } ] } ]); } /** * Sรฉlection des plugins */ async selectPlugins() { console.log(chalk.blue.bold('\n๐Ÿ”Œ Plugins & Extensions\n')); const pluginChoices = [ { name: '๐Ÿ“Š Logger - Advanced request/error logging', value: 'logger', checked: true }, { name: '๐Ÿ›ก๏ธ Security - Helmet, CORS, rate limiting', value: 'security', checked: true }, { name: 'โšก Cache - Redis/Memory caching system', value: 'cache' }, { name: '๐Ÿ“ˆ Monitoring - Health checks & metrics', value: 'monitoring' }, { name: '๐Ÿ“ฆ Compression - Gzip response compression', value: 'compression' }, { name: '๐Ÿ”„ Backup - Automated data backups', value: 'backup' }, { name: '๐ŸŽจ Image Processing - Sharp/Jimp integration', value: 'images' }, { name: '๐Ÿ“ง Mailer - Email templates & sending', value: 'mailer' }, { name: '๐Ÿ“… Scheduler - Cron jobs & tasks', value: 'scheduler' }, { name: '๐Ÿ” Search - Full-text search engine', value: 'search' }, { name: '๐Ÿ“ฑ Push Notifications', value: 'notifications' }, { name: '๐Ÿช Session Store - Persistent sessions', value: 'sessionstore' } ]; const { plugins } = await inquirer.prompt([{ type: 'checkbox', name: 'plugins', message: '๐ŸŽฏ Select plugins to install:', choices: pluginChoices, pageSize: 12, validate: (input) => this.validatePlugins(input) }]); this.config.plugins = plugins; } /** * Sรฉlection du framework de style */ async selectStyling() { console.log(chalk.blue.bold('\n๐ŸŽจ Styling & UI Framework\n')); const stylingQuestions = [ { type: 'list', name: 'framework', message: '๐ŸŽญ Choose a CSS framework:', choices: [ { name: '๐Ÿ…ฑ๏ธ Bootstrap 5 - Popular component library', value: 'bootstrap' }, { name: '๐ŸŽฏ Tailwind CSS - Utility-first framework', value: 'tailwind' }, { name: '๐ŸŽช Bulma - Modern CSS framework', value: 'bulma' }, { name: 'โšก Material Design - Google Material UI', value: 'material' }, { name: '๐ŸŽจ Foundation - Responsive front-end framework', value: 'foundation' }, { name: '๐ŸŽญ Semantic UI - Human-friendly HTML', value: 'semantic' }, { name: '๐Ÿ–ผ๏ธ Custom CSS - Write your own styles', value: 'custom' }, { name: '๐Ÿšซ None - No CSS framework', value: 'none' } ] }, { type: 'list', name: 'theme', message: '๐ŸŒˆ Color theme preference:', choices: [ { name: '๐ŸŒ… Light - Clean and bright', value: 'light' }, { name: '๐ŸŒ™ Dark - Easy on the eyes', value: 'dark' }, { name: '๐ŸŽจ Auto - Follow system preference', value: 'auto' }, { name: '๐ŸŒˆ Custom - Define your own colors', value: 'custom' } ], when: (answers) => answers.framework !== 'none' } ]; const stylingAnswers = await inquirer.prompt(stylingQuestions); this.config.styling = stylingAnswers.framework; this.config.theme = stylingAnswers.theme || 'light'; } /** * Options finales de configuration */ async finalOptions() { console.log(chalk.blue.bold('\nโš™๏ธ Final Configuration\n')); const finalQuestions = [ { type: 'confirm', name: 'git', message: '๐Ÿ“ฆ Initialize Git repository?', default: true }, { type: 'confirm', name: 'install', message: '๐Ÿ“ฅ Install dependencies automatically?', default: true }, { type: 'confirm', name: 'scripts', message: '๐Ÿ“œ Add useful npm scripts?', default: true }, { type: 'confirm', name: 'docker', message: '๐Ÿณ Generate Docker configuration?', default: false }, { type: 'confirm', name: 'env', message: '๐Ÿ” Create environment configuration?', default: true } ]; const finalAnswers = await inquirer.prompt(finalQuestions); Object.assign(this.config, finalAnswers); } /** * Confirmation de la configuration */ async confirmOptions() { console.log(chalk.blue.bold('\n๐Ÿ“‹ Configuration Summary\n')); const summary = this.generateSummary(); const summaryBox = boxen(summary, { padding: 1, margin: 1, borderStyle: 'double', borderColor: 'green', title: '๐Ÿ“ฆ Project Configuration', titleAlignment: 'center' }); console.log(summaryBox); const { confirm } = await inquirer.prompt([{ type: 'confirm', name: 'confirm', message: 'โœ… Proceed with this configuration?', default: true }]); if (!confirm) { console.log(chalk.yellow('\n๐Ÿ‘‹ Setup cancelled. See you later!')); process.exit(0); } } /** * Gรฉnรฉration du rรฉsumรฉ de configuration */ generateSummary() { const { projectName, template, features, database, auth, plugins, styling, theme } = this.config; return chalk.white(` ๐Ÿท๏ธ Project: ${chalk.cyan.bold(projectName)} ๐Ÿ“ Description: ${chalk.gray(this.config.description)} ๐Ÿ‘ค Author: ${chalk.green(this.config.author)} ๐ŸŽจ Template: ${chalk.yellow(template)} ๐Ÿ—„๏ธ Database: ${chalk.blue(database)} ๐Ÿ” Auth: ${chalk.magenta(auth.enabled ? 'โœ… Enabled' : 'โŒ Disabled')} ๐ŸŽญ Styling: ${chalk.yellow(styling)} ${theme ? `(${theme})` : ''} ๐Ÿ“ฆ Features (${features.length}): ${features.map(f => ` โœ“ ${f}`).join('\n') || ' No additional features'} ๐Ÿ”Œ Plugins (${plugins.length}): ${plugins.map(p => ` โšก ${p}`).join('\n') || ' No plugins selected'} โš™๏ธ Options: ๐Ÿ“ฆ Git: ${this.config.git ? 'โœ…' : 'โŒ'} ๐Ÿ“ฅ Auto-install: ${this.config.install ? 'โœ…' : 'โŒ'} ๐Ÿณ Docker: ${this.config.docker ? 'โœ…' : 'โŒ'} ๐Ÿ” Environment: ${this.config.env ? 'โœ…' : 'โŒ'} `); } /** * Exรฉcution de la configuration */ async executeSetup() { try { const SetupExecutor = require('./setup-executor'); const executor = new SetupExecutor(this.config); await executor.execute(); } catch (error) { throw new Error(`Setup execution failed: ${error.message}`); } } /** * ร‰cran de finalisation */ async showCompletion() { console.log(chalk.green.bold('\n๐ŸŽ‰ Setup Complete!\n')); const completionMessage = this.generateCompletionMessage(); const completionBox = boxen(completionMessage, { padding: 1, margin: 1, borderStyle: 'round', borderColor: 'green', title: '๐ŸŽŠ Success!', titleAlignment: 'center' }); console.log(completionBox); console.log(chalk.rainbow('\nโœจ Happy coding with Veko.js! โœจ\n')); } /** * Gรฉnรฉration du message de finalisation */ generateCompletionMessage() { const { projectName } = this.config; return chalk.white(`Your project "${chalk.cyan.bold(projectName)}" has been created successfully!\n\n`) + chalk.gray('Next steps:\n') + chalk.white(` ๐Ÿ“ cd ${projectName}\n`) + chalk.white(' ๐Ÿš€ npm run dev\n') + chalk.white(' ๐ŸŒ veko dev\n\n`') + chalk.gray('Your app will be available at: ') + chalk.blue.underline('http://localhost:3000\n\n') + chalk.yellow('๐Ÿ“š Documentation: ') + chalk.blue.underline('https://veko.js.org'); } // === Mรฉthodes de validation sรฉcurisรฉes === /** * Validation du nom de projet */ validateProjectName(input) { if (!input || input.length < 1) { return 'Project name is required'; } if (input.length > this.securityConfig.maxProjectNameLength) { return `Project name must be less than ${this.securityConfig.maxProjectNameLength} characters`; } if (!this.securityConfig.allowedFileNameChars.test(input)) { return 'Use only letters, numbers, hyphens and underscores'; } try { // Vรฉrification synchrone de l'existence du rรฉpertoire const fs = require('fs'); if (fs.existsSync(input)) { return 'Directory already exists'; } } catch (error) { // Ignorer les erreurs de vรฉrification } return true; } /** * Validation de la description */ validateDescription(input) { if (input && input.length > this.securityConfig.maxDescriptionLength) { return `Description must be less than ${this.securityConfig.maxDescriptionLength} characters`; } return true; } /** * Validation de l'auteur */ validateAuthor(input) { if (input && input.length > this.securityConfig.maxAuthorLength) { return `Author name must be less than ${this.securityConfig.maxAuthorLength} characters`; } return true; } /** * Validation des fonctionnalitรฉs */ validateFeatures(input) { if (input.length > this.securityConfig.maxFeatures) { return `Maximum ${this.securityConfig.maxFeatures} features allowed`; } return true; } /** * Validation des plugins */ validatePlugins(input) { if (input.length > this.securityConfig.maxPlugins) { return `Maximum ${this.securityConfig.maxPlugins} plugins allowed`; } return true; } /** * Nettoyage sรฉcurisรฉ des informations de projet */ sanitizeProjectInfo(info) { return { projectName: this.sanitizeString(info.projectName, this.securityConfig.maxProjectNameLength), description: this.sanitizeString(info.description, this.securityConfig.maxDescriptionLength), author: this.sanitizeString(info.author, this.securityConfig.maxAuthorLength), license: info.license || 'MIT' }; } /** * Nettoyage gรฉnรฉrique de chaรฎne */ sanitizeString(str, maxLength) { if (typeof str !== 'string') return ''; return str.trim().substring(0, maxLength); } /** * Gestion centralisรฉe des erreurs */ async handleError(error) { console.log(chalk.red.bold('\nโŒ Setup Error\n')); const errorBox = boxen( chalk.red('An error occurred during setup:\n\n') + chalk.white(error.message) + '\n\n' + chalk.gray('Please try again or report this issue if it persists.'), { padding: 1, margin: 1, borderStyle: 'round', borderColor: 'red', title: '๐Ÿšจ Error', titleAlignment: 'center' } ); console.log(errorBox); const { retry } = await inquirer.prompt([{ type: 'confirm', name: 'retry', message: '๐Ÿ”„ Would you like to try again?', default: true }]); if (retry) { await this.start(); } else { process.exit(1); } } } module.exports = SetupWizard;