279 lines
8.4 KiB
JavaScript
279 lines
8.4 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Build script for production deployment
|
|
* Handles CSS compilation, asset optimization, and production setup
|
|
*/
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const { exec } = require('child_process');
|
|
const { promisify } = require('util');
|
|
|
|
const execAsync = promisify(exec);
|
|
|
|
// Configuration
|
|
const BUILD_DIR = path.join(__dirname, '..', 'dist');
|
|
const PUBLIC_DIR = path.join(__dirname, '..', 'public');
|
|
const VIEWS_DIR = path.join(__dirname, '..', 'views');
|
|
|
|
async function buildForProduction() {
|
|
try {
|
|
console.log('🏗️ Starting production build...');
|
|
|
|
// Create build directory
|
|
await createBuildDirectory();
|
|
|
|
// Install production dependencies
|
|
await installProductionDependencies();
|
|
|
|
// Optimize assets
|
|
await optimizeAssets();
|
|
|
|
// Generate service worker with workbox
|
|
await generateServiceWorker();
|
|
|
|
// Create production environment file
|
|
await createProductionEnv();
|
|
|
|
// Copy necessary files
|
|
await copyProductionFiles();
|
|
|
|
console.log('✅ Production build completed successfully!');
|
|
console.log('📦 Build output available in:', BUILD_DIR);
|
|
console.log('🚀 Ready for deployment!');
|
|
|
|
} catch (error) {
|
|
console.error('❌ Production build failed:', error);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
async function createBuildDirectory() {
|
|
try {
|
|
if (fs.existsSync(BUILD_DIR)) {
|
|
console.log('🗑️ Cleaning existing build directory...');
|
|
await execAsync(`rm -rf ${BUILD_DIR}`);
|
|
}
|
|
|
|
fs.mkdirSync(BUILD_DIR, { recursive: true });
|
|
console.log('📁 Created build directory');
|
|
} catch (error) {
|
|
console.error('❌ Error creating build directory:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async function installProductionDependencies() {
|
|
try {
|
|
console.log('📦 Installing production dependencies...');
|
|
await execAsync('npm ci --only=production');
|
|
console.log('✅ Production dependencies installed');
|
|
} catch (error) {
|
|
console.error('❌ Error installing dependencies:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async function optimizeAssets() {
|
|
try {
|
|
console.log('🎨 Optimizing CSS and JavaScript...');
|
|
|
|
// Create optimized CSS
|
|
const cssContent = fs.readFileSync(path.join(PUBLIC_DIR, 'css', 'main.css'), 'utf8');
|
|
const optimizedCSS = await optimizeCSS(cssContent);
|
|
|
|
const buildCSSDir = path.join(BUILD_DIR, 'public', 'css');
|
|
fs.mkdirSync(buildCSSDir, { recursive: true });
|
|
fs.writeFileSync(path.join(buildCSSDir, 'main.min.css'), optimizedCSS);
|
|
|
|
// Create optimized JavaScript
|
|
const jsContent = fs.readFileSync(path.join(PUBLIC_DIR, 'js', 'main.js'), 'utf8');
|
|
const optimizedJS = await optimizeJS(jsContent);
|
|
|
|
const buildJSDir = path.join(BUILD_DIR, 'public', 'js');
|
|
fs.mkdirSync(buildJSDir, { recursive: true });
|
|
fs.writeFileSync(path.join(buildJSDir, 'main.min.js'), optimizedJS);
|
|
|
|
// Copy other assets
|
|
await copyDirectory(path.join(PUBLIC_DIR, 'images'), path.join(BUILD_DIR, 'public', 'images'));
|
|
|
|
console.log('✅ Assets optimized');
|
|
} catch (error) {
|
|
console.error('❌ Error optimizing assets:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async function optimizeCSS(css) {
|
|
// Simple CSS minification (remove comments, extra whitespace)
|
|
return css
|
|
.replace(/\/\*[\s\S]*?\*\//g, '') // Remove comments
|
|
.replace(/\s{2,}/g, ' ') // Replace multiple spaces with single space
|
|
.replace(/;\s*}/g, '}') // Remove semicolon before closing brace
|
|
.replace(/\s*{\s*/g, '{') // Remove spaces around opening brace
|
|
.replace(/;\s*/g, ';') // Remove spaces after semicolons
|
|
.replace(/,\s*/g, ',') // Remove spaces after commas
|
|
.trim();
|
|
}
|
|
|
|
async function optimizeJS(js) {
|
|
// Simple JS minification (remove comments, extra whitespace)
|
|
return js
|
|
.replace(/\/\/.*$/gm, '') // Remove single-line comments
|
|
.replace(/\/\*[\s\S]*?\*\//g, '') // Remove multi-line comments
|
|
.replace(/\s{2,}/g, ' ') // Replace multiple spaces with single space
|
|
.replace(/\n\s*/g, '') // Remove newlines and indentation
|
|
.trim();
|
|
}
|
|
|
|
async function generateServiceWorker() {
|
|
try {
|
|
console.log('⚙️ Generating optimized service worker...');
|
|
|
|
const swContent = fs.readFileSync(path.join(PUBLIC_DIR, 'sw.js'), 'utf8');
|
|
const optimizedSW = await optimizeJS(swContent);
|
|
|
|
fs.writeFileSync(path.join(BUILD_DIR, 'public', 'sw.js'), optimizedSW);
|
|
|
|
// Copy manifest
|
|
fs.copyFileSync(
|
|
path.join(PUBLIC_DIR, 'manifest.json'),
|
|
path.join(BUILD_DIR, 'public', 'manifest.json')
|
|
);
|
|
|
|
console.log('✅ Service worker generated');
|
|
} catch (error) {
|
|
console.error('❌ Error generating service worker:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async function createProductionEnv() {
|
|
try {
|
|
console.log('🔧 Creating production environment configuration...');
|
|
|
|
const prodEnv = `
|
|
# Production Environment Configuration
|
|
NODE_ENV=production
|
|
PORT=3000
|
|
|
|
# Database
|
|
MONGODB_URI=mongodb://localhost:27017/smartsoltech
|
|
|
|
# Security
|
|
SESSION_SECRET=your-super-secret-session-key-change-this-in-production
|
|
JWT_SECRET=your-super-secret-jwt-key-change-this-in-production
|
|
|
|
# File Upload
|
|
UPLOAD_PATH=./uploads
|
|
MAX_FILE_SIZE=10485760
|
|
|
|
# Email Configuration
|
|
SMTP_HOST=smtp.gmail.com
|
|
SMTP_PORT=587
|
|
SMTP_USER=your-email@gmail.com
|
|
SMTP_PASS=your-app-password
|
|
|
|
# Telegram Bot (Optional)
|
|
TELEGRAM_BOT_TOKEN=your-telegram-bot-token
|
|
|
|
# Admin Account
|
|
ADMIN_EMAIL=admin@smartsoltech.kr
|
|
ADMIN_PASSWORD=change-this-password
|
|
|
|
# Security Headers
|
|
RATE_LIMIT_WINDOW_MS=900000
|
|
RATE_LIMIT_MAX_REQUESTS=100
|
|
`.trim();
|
|
|
|
fs.writeFileSync(path.join(BUILD_DIR, '.env.production'), prodEnv);
|
|
console.log('✅ Production environment file created');
|
|
} catch (error) {
|
|
console.error('❌ Error creating production environment:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async function copyProductionFiles() {
|
|
try {
|
|
console.log('📋 Copying production files...');
|
|
|
|
// Copy main application files
|
|
const filesToCopy = [
|
|
'server.js',
|
|
'package.json',
|
|
'package-lock.json'
|
|
];
|
|
|
|
for (const file of filesToCopy) {
|
|
fs.copyFileSync(
|
|
path.join(__dirname, '..', file),
|
|
path.join(BUILD_DIR, file)
|
|
);
|
|
}
|
|
|
|
// Copy directories
|
|
await copyDirectory(
|
|
path.join(__dirname, '..', 'models'),
|
|
path.join(BUILD_DIR, 'models')
|
|
);
|
|
|
|
await copyDirectory(
|
|
path.join(__dirname, '..', 'routes'),
|
|
path.join(BUILD_DIR, 'routes')
|
|
);
|
|
|
|
await copyDirectory(
|
|
path.join(__dirname, '..', 'views'),
|
|
path.join(BUILD_DIR, 'views')
|
|
);
|
|
|
|
await copyDirectory(
|
|
path.join(__dirname, '..', 'middleware'),
|
|
path.join(BUILD_DIR, 'middleware')
|
|
);
|
|
|
|
// Create uploads directory
|
|
fs.mkdirSync(path.join(BUILD_DIR, 'uploads'), { recursive: true });
|
|
|
|
console.log('✅ Production files copied');
|
|
} catch (error) {
|
|
console.error('❌ Error copying production files:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async function copyDirectory(src, dest) {
|
|
try {
|
|
if (!fs.existsSync(src)) {
|
|
return;
|
|
}
|
|
|
|
fs.mkdirSync(dest, { recursive: true });
|
|
|
|
const items = fs.readdirSync(src);
|
|
|
|
for (const item of items) {
|
|
const srcPath = path.join(src, item);
|
|
const destPath = path.join(dest, item);
|
|
|
|
const stat = fs.statSync(srcPath);
|
|
|
|
if (stat.isDirectory()) {
|
|
await copyDirectory(srcPath, destPath);
|
|
} else {
|
|
fs.copyFileSync(srcPath, destPath);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error(`❌ Error copying directory ${src}:`, error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
if (require.main === module) {
|
|
buildForProduction();
|
|
}
|
|
|
|
module.exports = { buildForProduction }; |