Refactor CLI tool: Simplify environment configuration process by removing internal access token and enhancing error handling. Introduce displayHeader function for consistent ASCII art presentation. Create default .env.example file for easier project setup and improve user prompts for environment setup options.
This commit is contained in:
parent
14606f0ada
commit
3883e5ef20
@ -14,16 +14,8 @@ const gradient = require('gradient-string');
|
||||
const https = require('https');
|
||||
const http = require('http');
|
||||
|
||||
// Configuration URLs - can be updated as needed
|
||||
const ENV_CONFIG_URLS = {
|
||||
internal: 'https://raw.githubusercontent.com/corradaf/env-configs/main/internal.env'
|
||||
};
|
||||
|
||||
// Internal access token for secure env access
|
||||
const INTERNAL_ACCESS_TOKEN = 'corrad_internal_2024_secure_token_af';
|
||||
|
||||
// Function to fetch content from URL with security
|
||||
function fetchFromUrl(url, token = null) {
|
||||
// Function to fetch content from URL
|
||||
function fetchFromUrl(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const client = url.startsWith('https') ? https : http;
|
||||
|
||||
@ -32,15 +24,9 @@ function fetchFromUrl(url, token = null) {
|
||||
hostname: urlObj.hostname,
|
||||
port: urlObj.port,
|
||||
path: urlObj.pathname + urlObj.search,
|
||||
method: 'GET',
|
||||
headers: {}
|
||||
method: 'GET'
|
||||
};
|
||||
|
||||
if (token) {
|
||||
options.headers['Authorization'] = `Bearer ${token}`;
|
||||
options.headers['X-Access-Token'] = token;
|
||||
}
|
||||
|
||||
const req = client.request(options, (res) => {
|
||||
let data = '';
|
||||
|
||||
@ -122,6 +108,30 @@ function createProgressBar(message, duration = 3000) {
|
||||
});
|
||||
}
|
||||
|
||||
// Clear screen and display ASCII art
|
||||
function displayHeader() {
|
||||
clearScreen();
|
||||
|
||||
// Show ASCII "CORRAD AF" with gradient
|
||||
const asciiText = figlet.textSync('CORRAD AF', {
|
||||
font: 'Big',
|
||||
horizontalLayout: 'default',
|
||||
verticalLayout: 'default'
|
||||
});
|
||||
|
||||
console.log(gradient.rainbow(asciiText));
|
||||
console.log(boxen(
|
||||
chalk.white.bold('🚀 Welcome to CORRAD Application Framework CLI\n') +
|
||||
chalk.gray('The fastest way to bootstrap your next project'),
|
||||
{
|
||||
padding: 1,
|
||||
margin: 1,
|
||||
borderStyle: 'round',
|
||||
borderColor: 'cyan'
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
// Clear screen and position cursor at top
|
||||
function clearScreen() {
|
||||
console.clear();
|
||||
@ -163,73 +173,59 @@ function extractComment(line) {
|
||||
return commentMatch ? commentMatch[1].trim() : '';
|
||||
}
|
||||
|
||||
// Prompt for environment configurations
|
||||
async function promptForEnvConfig(envVars, projectName) {
|
||||
const envConfig = {};
|
||||
|
||||
console.log(chalk.cyan.bold('\n⚙️ Environment Configuration'));
|
||||
console.log(chalk.gray('Configure your project environment variables:\n'));
|
||||
|
||||
for (const envVar of envVars) {
|
||||
let message = `${envVar.key}`;
|
||||
if (envVar.description) {
|
||||
message += chalk.gray(` (${envVar.description})`);
|
||||
}
|
||||
|
||||
let defaultValue = envVar.defaultValue;
|
||||
|
||||
// Replace placeholder values
|
||||
if (defaultValue.includes('${projectName}') || defaultValue.includes('your-project')) {
|
||||
defaultValue = defaultValue.replace(/\${projectName}|your-project/g, projectName);
|
||||
}
|
||||
|
||||
const { value } = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'value',
|
||||
message: message + ':',
|
||||
default: defaultValue,
|
||||
validate: function(input) {
|
||||
if (envVar.key.includes('SECRET') || envVar.key.includes('PASSWORD')) {
|
||||
if (input.trim().length < 8) {
|
||||
return 'Security values should be at least 8 characters long';
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
envConfig[envVar.key] = value;
|
||||
// Create default .env.example file if it doesn't exist
|
||||
function createDefaultEnvExample() {
|
||||
const defaultEnvContent = `# Database Configuration
|
||||
DATABASE_URL="mysql://username:password@localhost:3306/database_name" # Your MySQL connection string
|
||||
|
||||
# Authentication
|
||||
JWT_SECRET="your-super-secret-jwt-key-change-this-in-production" # Secret key for JWT tokens
|
||||
AUTH_ORIGIN="http://localhost:3000" # Allowed origin for authentication
|
||||
|
||||
# Application
|
||||
NUXT_SECRET_KEY="your-nuxt-secret-key-for-session-encryption" # Nuxt secret key
|
||||
APP_NAME="Your Application Name" # Your application name
|
||||
APP_URL="http://localhost:3000" # Your application URL
|
||||
|
||||
# Email Configuration (Optional)
|
||||
MAIL_HOST="smtp.example.com" # SMTP server host
|
||||
MAIL_PORT="587" # SMTP server port
|
||||
MAIL_USERNAME="your-email@example.com" # SMTP username
|
||||
MAIL_PASSWORD="your-email-password" # SMTP password
|
||||
MAIL_FROM_ADDRESS="noreply@yourapp.com" # From email address
|
||||
MAIL_FROM_NAME="Your App Name" # From name
|
||||
|
||||
# Development
|
||||
NODE_ENV="development" # Environment (development, production)
|
||||
NUXT_HOST="localhost" # Nuxt host
|
||||
NUXT_PORT="3000" # Nuxt port
|
||||
`;
|
||||
|
||||
fs.writeFileSync('.env.example', defaultEnvContent);
|
||||
return defaultEnvContent;
|
||||
}
|
||||
|
||||
// Check if environment is properly configured
|
||||
function isEnvConfigured() {
|
||||
if (!fs.existsSync('.env')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return envConfig;
|
||||
const envContent = fs.readFileSync('.env', 'utf-8');
|
||||
// Check if DATABASE_URL is properly set (not empty or default)
|
||||
const dbUrlMatch = envContent.match(/DATABASE_URL\s*=\s*["'](.+?)["']/);
|
||||
|
||||
if (!dbUrlMatch) return false;
|
||||
|
||||
const dbUrl = dbUrlMatch[1];
|
||||
return dbUrl && !dbUrl.includes('username:password');
|
||||
}
|
||||
|
||||
// Main CLI function
|
||||
async function main() {
|
||||
try {
|
||||
// Clear console and show welcome screen (only once)
|
||||
clearScreen();
|
||||
|
||||
// Step 1: Show ASCII "CORRAD AF" with gradient (only once)
|
||||
const asciiText = figlet.textSync('CORRAD AF', {
|
||||
font: 'Big',
|
||||
horizontalLayout: 'default',
|
||||
verticalLayout: 'default'
|
||||
});
|
||||
|
||||
console.log(gradient.rainbow(asciiText));
|
||||
console.log(boxen(
|
||||
chalk.white.bold('🚀 Welcome to CORRAD Application Framework CLI\n') +
|
||||
chalk.gray('The fastest way to bootstrap your next project'),
|
||||
{
|
||||
padding: 1,
|
||||
margin: 1,
|
||||
borderStyle: 'round',
|
||||
borderColor: 'cyan'
|
||||
}
|
||||
));
|
||||
// Show the header once at the beginning
|
||||
displayHeader();
|
||||
|
||||
// Check for existing project files
|
||||
const hasFiles = hasProjectFiles();
|
||||
@ -264,18 +260,8 @@ async function main() {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Clear screen and show project name prompt
|
||||
clearScreen();
|
||||
console.log(boxen(
|
||||
chalk.white.bold('🚀 CORRAD Application Framework CLI\n') +
|
||||
chalk.gray('The fastest way to bootstrap your next project'),
|
||||
{
|
||||
padding: 1,
|
||||
margin: 1,
|
||||
borderStyle: 'round',
|
||||
borderColor: 'cyan'
|
||||
}
|
||||
));
|
||||
// Display header again for consistency
|
||||
displayHeader();
|
||||
|
||||
// Get project name (only for new projects)
|
||||
let projectName = path.basename(process.cwd());
|
||||
@ -302,94 +288,32 @@ async function main() {
|
||||
projectName = inputProjectName;
|
||||
}
|
||||
|
||||
// Clear screen and show project type selection
|
||||
clearScreen();
|
||||
console.log(boxen(
|
||||
chalk.white.bold('🚀 CORRAD Application Framework CLI\n') +
|
||||
chalk.gray('The fastest way to bootstrap your next project'),
|
||||
{
|
||||
padding: 1,
|
||||
margin: 1,
|
||||
borderStyle: 'round',
|
||||
borderColor: 'cyan'
|
||||
}
|
||||
));
|
||||
// Display header again for consistency
|
||||
displayHeader();
|
||||
|
||||
// Step 3: Ask if public or internal project
|
||||
console.log(chalk.cyan.bold('\n🔐 Project Type'));
|
||||
const { projectType } = await inquirer.prompt([
|
||||
// Step 3: Ask about environment setup
|
||||
console.log(chalk.cyan.bold('\n🔐 Environment Setup'));
|
||||
const { envSetupType } = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'projectType',
|
||||
message: 'Is this a public or internal project?',
|
||||
name: 'envSetupType',
|
||||
message: 'How would you like to set up your environment?',
|
||||
choices: [
|
||||
{
|
||||
name: chalk.green('🌍 Public') + chalk.gray(' - Configure environment manually'),
|
||||
value: 'public'
|
||||
name: chalk.green('🔧 Manual Setup') + chalk.gray(' - Configure environment later'),
|
||||
value: 'manual'
|
||||
},
|
||||
{
|
||||
name: chalk.red('🔒 Internal') + chalk.gray(' - Use company configuration'),
|
||||
value: 'internal'
|
||||
name: chalk.blue('🔗 Import from URL') + chalk.gray(' - Paste a configuration URL'),
|
||||
value: 'url'
|
||||
}
|
||||
],
|
||||
default: 0
|
||||
}
|
||||
]);
|
||||
|
||||
let isInternal = false;
|
||||
let useCompanyEnv = false;
|
||||
|
||||
if (projectType === 'internal') {
|
||||
isInternal = true;
|
||||
|
||||
// Clear screen and show password prompt
|
||||
clearScreen();
|
||||
console.log(boxen(
|
||||
chalk.white.bold('🚀 CORRAD Application Framework CLI\n') +
|
||||
chalk.gray('The fastest way to bootstrap your next project'),
|
||||
{
|
||||
padding: 1,
|
||||
margin: 1,
|
||||
borderStyle: 'round',
|
||||
borderColor: 'cyan'
|
||||
}
|
||||
));
|
||||
|
||||
// Password verification for internal projects
|
||||
console.log(chalk.yellow('\n🔒 Internal project detected - Password verification required'));
|
||||
const { password } = await inquirer.prompt([
|
||||
{
|
||||
type: 'password',
|
||||
name: 'password',
|
||||
message: 'Enter company password:',
|
||||
mask: '*'
|
||||
}
|
||||
]);
|
||||
|
||||
if (password !== 'Rahsia@123456') {
|
||||
console.log(chalk.red('❌ Incorrect password. Access denied.'));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(chalk.green('✓ Password verified. Access granted.'));
|
||||
useCompanyEnv = true;
|
||||
|
||||
} else {
|
||||
console.log(chalk.green('✓ Public project setup selected.'));
|
||||
}
|
||||
|
||||
// Clear screen and show development tools prompt
|
||||
clearScreen();
|
||||
console.log(boxen(
|
||||
chalk.white.bold('🚀 CORRAD Application Framework CLI\n') +
|
||||
chalk.gray('The fastest way to bootstrap your next project'),
|
||||
{
|
||||
padding: 1,
|
||||
margin: 1,
|
||||
borderStyle: 'round',
|
||||
borderColor: 'cyan'
|
||||
}
|
||||
));
|
||||
// Display header again for consistency
|
||||
displayHeader();
|
||||
|
||||
// Step 4: Ask about Cursor rules
|
||||
console.log(chalk.cyan.bold('\n⚙️ Development Tools'));
|
||||
@ -406,18 +330,8 @@ async function main() {
|
||||
}
|
||||
]);
|
||||
|
||||
// Clear screen and show setup progress
|
||||
clearScreen();
|
||||
console.log(boxen(
|
||||
chalk.white.bold('🚀 CORRAD Application Framework CLI\n') +
|
||||
chalk.gray('The fastest way to bootstrap your next project'),
|
||||
{
|
||||
padding: 1,
|
||||
margin: 1,
|
||||
borderStyle: 'round',
|
||||
borderColor: 'cyan'
|
||||
}
|
||||
));
|
||||
// Display header again for consistency
|
||||
displayHeader();
|
||||
|
||||
// Step 5: Show loading screen and setup project
|
||||
console.log(chalk.cyan.bold('\n🚀 Setting up your project...'));
|
||||
@ -467,75 +381,83 @@ async function main() {
|
||||
}
|
||||
|
||||
// Setup environment file
|
||||
if (useCompanyEnv && isInternal) {
|
||||
const envSpinner = ora('Setting up company environment...').start();
|
||||
if (envSetupType === 'url') {
|
||||
// Display header for consistency
|
||||
displayHeader();
|
||||
|
||||
const { envUrl } = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'envUrl',
|
||||
message: 'Enter the URL for your environment configuration:',
|
||||
validate: function(input) {
|
||||
if (!input.trim() || !input.startsWith('http')) {
|
||||
return 'Please enter a valid URL (starting with http:// or https://)';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
const envSpinner = ora('Fetching environment configuration from URL...').start();
|
||||
|
||||
try {
|
||||
// Fetch .env content from secure URL
|
||||
const envContent = await fetchFromUrl(ENV_CONFIG_URLS.internal, INTERNAL_ACCESS_TOKEN);
|
||||
// Fetch .env content from provided URL
|
||||
const envContent = await fetchFromUrl(envUrl);
|
||||
const processedContent = envContent.replace(/\${projectName}/g, projectName);
|
||||
|
||||
fs.writeFileSync('.env', processedContent);
|
||||
envSpinner.succeed('Company environment configured');
|
||||
envSpinner.succeed('Environment configuration imported successfully');
|
||||
|
||||
// Run Prisma commands if the env is properly configured
|
||||
if (isEnvConfigured()) {
|
||||
const prismaSpinner = ora('Setting up Prisma database...').start();
|
||||
if (fs.existsSync('prisma/schema.prisma')) {
|
||||
const prismaSuccess = runCommand('npx prisma generate', { stdio: 'pipe' });
|
||||
if (prismaSuccess) {
|
||||
prismaSpinner.succeed('Prisma database configured');
|
||||
} else {
|
||||
prismaSpinner.fail('Prisma setup failed - please check your DATABASE_URL');
|
||||
}
|
||||
} else {
|
||||
prismaSpinner.warn('No Prisma schema found - skipping database setup');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
envSpinner.fail('Failed to fetch company environment config');
|
||||
console.log(chalk.yellow('⚠️ Unable to access secure company environment.'));
|
||||
console.log(chalk.gray('Please contact your system administrator for access.'));
|
||||
process.exit(1);
|
||||
envSpinner.fail('Failed to fetch environment configuration');
|
||||
console.log(chalk.yellow(`⚠️ Unable to access URL: ${error.message}`));
|
||||
console.log(chalk.gray('Creating a basic .env file you can edit later...'));
|
||||
|
||||
// Create basic .env file from .env.example
|
||||
try {
|
||||
let exampleContent;
|
||||
if (fs.existsSync('.env.example')) {
|
||||
exampleContent = fs.readFileSync('.env.example', 'utf8');
|
||||
} else {
|
||||
exampleContent = createDefaultEnvExample();
|
||||
}
|
||||
fs.copyFileSync('.env.example', '.env');
|
||||
console.log(chalk.green('✓ Basic environment file created'));
|
||||
console.log(chalk.yellow('⚠️ Please edit the .env file and configure your DATABASE_URL before running Prisma commands'));
|
||||
} catch (error) {
|
||||
console.log(chalk.red(`Error creating .env file: ${error.message}`));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Create environment file based on .env.example
|
||||
const envSpinner = ora('Setting up environment configuration...').start();
|
||||
// Manual environment setup
|
||||
const envSpinner = ora('Setting up environment files...').start();
|
||||
|
||||
try {
|
||||
if (fs.existsSync('.env.example')) {
|
||||
const exampleContent = fs.readFileSync('.env.example', 'utf8');
|
||||
const envVars = parseEnvExample(exampleContent);
|
||||
|
||||
envSpinner.succeed('Environment template loaded');
|
||||
|
||||
// Clear screen for env configuration
|
||||
clearScreen();
|
||||
console.log(boxen(
|
||||
chalk.white.bold('🚀 CORRAD Application Framework CLI\n') +
|
||||
chalk.gray('The fastest way to bootstrap your next project'),
|
||||
{
|
||||
padding: 1,
|
||||
margin: 1,
|
||||
borderStyle: 'round',
|
||||
borderColor: 'cyan'
|
||||
}
|
||||
));
|
||||
|
||||
// Prompt for each environment variable
|
||||
const envConfig = await promptForEnvConfig(envVars, projectName);
|
||||
|
||||
// Generate .env file
|
||||
let envContent = '';
|
||||
for (const [key, value] of Object.entries(envConfig)) {
|
||||
envContent += `${key}="${value}"\n`;
|
||||
}
|
||||
|
||||
fs.writeFileSync('.env', envContent);
|
||||
console.log(chalk.green('✓ Environment file created'));
|
||||
} else {
|
||||
envSpinner.warn('No .env.example found - creating basic environment');
|
||||
|
||||
// Fallback to basic template
|
||||
const fallbackEnv = `# Basic Environment Configuration
|
||||
DATABASE_URL="postgresql://username:password@localhost:5432/database_name"
|
||||
JWT_SECRET="your-super-secret-jwt-key"
|
||||
AUTH_ORIGIN="http://localhost:3000"
|
||||
NUXT_SECRET_KEY="your-nuxt-secret-key"
|
||||
APP_NAME="${projectName}"
|
||||
APP_URL="http://localhost:3000"
|
||||
NODE_ENV="development"
|
||||
NUXT_HOST="localhost"
|
||||
NUXT_PORT="3000"
|
||||
`;
|
||||
fs.writeFileSync('.env', fallbackEnv);
|
||||
console.log(chalk.green('✓ Basic environment created'));
|
||||
// Create or ensure .env.example exists
|
||||
if (!fs.existsSync('.env.example')) {
|
||||
createDefaultEnvExample();
|
||||
}
|
||||
|
||||
// Copy .env.example to .env without filling in values
|
||||
fs.copyFileSync('.env.example', '.env');
|
||||
envSpinner.succeed('Environment files created');
|
||||
console.log(chalk.green('✓ Created .env file from .env.example'));
|
||||
console.log(chalk.yellow('⚠️ Please edit the .env file and configure your DATABASE_URL before running Prisma commands'));
|
||||
} catch (error) {
|
||||
envSpinner.fail('Failed to set up environment');
|
||||
console.log(chalk.red(`Error: ${error.message}`));
|
||||
@ -640,43 +562,37 @@ Security:
|
||||
yarnSpinner.succeed('Dependencies installed with Yarn');
|
||||
}
|
||||
|
||||
// Setup Prisma
|
||||
const prismaSpinner = ora('Setting up Prisma database...').start();
|
||||
try {
|
||||
// Check if Prisma is available and generate client
|
||||
if (fs.existsSync('prisma/schema.prisma')) {
|
||||
const prismaSuccess = runCommand('npx prisma generate', { stdio: 'pipe' });
|
||||
if (prismaSuccess) {
|
||||
prismaSpinner.succeed('Prisma database configured');
|
||||
} else {
|
||||
prismaSpinner.warn('Prisma setup incomplete - configure manually if needed');
|
||||
}
|
||||
} else {
|
||||
prismaSpinner.warn('No Prisma schema found - skipping database setup');
|
||||
}
|
||||
} catch (error) {
|
||||
prismaSpinner.warn('Prisma setup skipped');
|
||||
}
|
||||
|
||||
// Display header again for consistency at the end
|
||||
displayHeader();
|
||||
|
||||
// Step 6: Success message and next steps
|
||||
const prismaNotes = isEnvConfigured() ?
|
||||
'' :
|
||||
chalk.yellow('\n⚠️ Important: Edit your .env file and configure your DATABASE_URL before running:\n') +
|
||||
chalk.gray(' npx prisma generate\n');
|
||||
|
||||
const nextSteps = setupAction === 'new' ? [
|
||||
` cd ${projectName}`,
|
||||
' git init # Initialize git repository',
|
||||
' yarn dev # Start development server',
|
||||
' yarn build # Build for production',
|
||||
' yarn start # Start production server'
|
||||
' git init # Initialize git repository',
|
||||
' # Edit your .env file with proper configuration',
|
||||
' npx prisma generate # Generate Prisma client after configuring DATABASE_URL',
|
||||
' yarn dev # Start development server',
|
||||
' yarn build # Build for production',
|
||||
' yarn start # Start production server'
|
||||
] : [
|
||||
' yarn dev # Start development server',
|
||||
' yarn build # Build for production',
|
||||
' yarn start # Start production server'
|
||||
' # Edit your .env file with proper configuration',
|
||||
' npx prisma generate # Generate Prisma client after configuring DATABASE_URL',
|
||||
' yarn dev # Start development server',
|
||||
' yarn build # Build for production',
|
||||
' yarn start # Start production server'
|
||||
];
|
||||
|
||||
console.log('\n' + boxen(
|
||||
chalk.green.bold('🎉 Project setup completed successfully!\n\n') +
|
||||
chalk.white(`Project: ${chalk.cyan.bold(projectName)}\n`) +
|
||||
chalk.white(`Type: ${chalk.yellow.bold(projectType)}\n`) +
|
||||
chalk.white(`Action: ${chalk.yellow.bold(setupAction === 'new' ? 'New Project' : 'Updated Existing')}\n`) +
|
||||
chalk.white(`Location: ${chalk.gray(process.cwd())}\n\n`) +
|
||||
chalk.white(`Location: ${chalk.gray(process.cwd())}\n`) +
|
||||
prismaNotes +
|
||||
chalk.white.bold('Next steps:\n') +
|
||||
nextSteps.map(step => chalk.gray(step)).join('\n'),
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "corradaf",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.2",
|
||||
"description": "CLI tool to quickly set up CORRAD Application Framework projects",
|
||||
"main": "index.js",
|
||||
"bin": {
|
||||
@ -19,12 +19,12 @@
|
||||
"template"
|
||||
],
|
||||
"dependencies": {
|
||||
"figlet": "^1.7.0",
|
||||
"ora": "^5.4.1",
|
||||
"inquirer": "^8.2.6",
|
||||
"chalk": "^4.1.2",
|
||||
"boxen": "^5.1.2",
|
||||
"gradient-string": "^2.0.2"
|
||||
"chalk": "^4.1.2",
|
||||
"figlet": "^1.8.1",
|
||||
"gradient-string": "^2.0.2",
|
||||
"inquirer": "^8.2.6",
|
||||
"ora": "^5.4.1"
|
||||
},
|
||||
"author": "Corrad Team",
|
||||
"license": "MIT",
|
||||
@ -36,4 +36,4 @@
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user