veko
Version:
๐ Ultra-modern Node.js framework with hot reload, plugins, authentication, and advanced security features
737 lines (657 loc) โข 24.1 kB
JavaScript
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;