refactor: 重构页面组件移除冗余Layout组件 feat: 实现WebSocket和事件总线系统 feat: 添加队列和调度系统 docs: 更新架构文档和服务映射 style: 清理重复接口定义使用数据源 chore: 更新依赖项配置 feat: 添加运行时系统和领域引导 ci: 配置ESLint边界检查规则 build: 添加Redis和WebSocket依赖 test: 添加MSW浏览器环境入口 perf: 优化数据获取逻辑使用统一数据源 fix: 修复类型定义和状态管理问题
466 lines
14 KiB
JavaScript
466 lines
14 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
const { execSync } = require('child_process');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const COLORS = {
|
|
reset: '\x1b[0m',
|
|
bright: '\x1b[1m',
|
|
red: '\x1b[31m',
|
|
green: '\x1b[32m',
|
|
yellow: '\x1b[33m',
|
|
blue: '\x1b[34m',
|
|
magenta: '\x1b[35m',
|
|
cyan: '\x1b[36m',
|
|
};
|
|
|
|
function log(message, color = COLORS.reset) {
|
|
console.log(`${color}${message}${COLORS.reset}`);
|
|
}
|
|
|
|
function logStep(step, message) {
|
|
console.log(`\n${COLORS.cyan}[${step}]${COLORS.reset} ${message}`);
|
|
}
|
|
|
|
function logSuccess(message) {
|
|
console.log(`${COLORS.green}✅${COLORS.reset} ${message}`);
|
|
}
|
|
|
|
function logError(message) {
|
|
console.log(`${COLORS.red}❌${COLORS.reset} ${message}`);
|
|
}
|
|
|
|
function logWarning(message) {
|
|
console.log(`${COLORS.yellow}⚠️${COLORS.reset} ${message}`);
|
|
}
|
|
|
|
function executeCommand(command, description) {
|
|
try {
|
|
logStep('EXEC', description);
|
|
const result = execSync(command, {
|
|
encoding: 'utf8',
|
|
stdio: 'inherit'
|
|
});
|
|
logSuccess(description);
|
|
return true;
|
|
} catch (error) {
|
|
logError(`${description} failed: ${error.message}`);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function optimizeDatabaseIndexes() {
|
|
logStep(1, 'Optimizing Database Indexes');
|
|
|
|
const indexOptimizations = [
|
|
'CREATE INDEX IF NOT EXISTS idx_cf_product_tenant_shop ON cf_product(tenantId, shopId)',
|
|
'CREATE INDEX IF NOT EXISTS idx_cf_order_tenant_status ON cf_order(tenantId, status)',
|
|
'CREATE INDEX IF NOT EXISTS idx_cf_order_created_at ON cf_order(createdAt)',
|
|
'CREATE INDEX IF NOT EXISTS idx_cf_invoice_tenant_status ON cf_invoice(tenantId, status)',
|
|
'CREATE INDEX IF NOT EXISTS idx_cf_settlement_tenant_date ON cf_settlement(tenantId, settlementDate)',
|
|
'CREATE INDEX IF NOT EXISTS idx_cf_compliance_record_tenant_type ON cf_compliance_record(tenantId, type)',
|
|
'CREATE INDEX IF NOT EXISTS idx_cf_certificate_expiry ON cf_certificate(expiryDate, status)'
|
|
];
|
|
|
|
let successCount = 0;
|
|
indexOptimizations.forEach(index => {
|
|
try {
|
|
execSync(`mysql -u root -p"${process.env.DB_PASSWORD}" -e "${index}"`, { stdio: 'pipe' });
|
|
successCount++;
|
|
logSuccess(`Index created: ${index.substring(0, 50)}...`);
|
|
} catch (error) {
|
|
logWarning(`Index already exists or failed: ${index.substring(0, 50)}...`);
|
|
}
|
|
});
|
|
|
|
logSuccess(`Database index optimization completed: ${successCount}/${indexOptimizations.length} indexes`);
|
|
return successCount === indexOptimizations.length;
|
|
}
|
|
|
|
function optimizeRedisCache() {
|
|
logStep(2, 'Optimizing Redis Cache Configuration');
|
|
|
|
const redisOptimizations = [
|
|
'CONFIG SET maxmemory 2gb',
|
|
'CONFIG SET maxmemory-policy allkeys-lru',
|
|
'CONFIG SET timeout 300',
|
|
'CONFIG SET tcp-keepalive 60',
|
|
'CONFIG SET save 900 1 300 10 60 10000'
|
|
];
|
|
|
|
let successCount = 0;
|
|
redisOptimizations.forEach(opt => {
|
|
try {
|
|
execSync(`redis-cli ${opt}`, { stdio: 'pipe' });
|
|
successCount++;
|
|
logSuccess(`Redis config: ${opt}`);
|
|
} catch (error) {
|
|
logWarning(`Redis config failed: ${opt}`);
|
|
}
|
|
});
|
|
|
|
logSuccess(`Redis optimization completed: ${successCount}/${redisOptimizations.length} configs`);
|
|
return successCount === redisOptimizations.length;
|
|
}
|
|
|
|
function optimizeNodeMemory() {
|
|
logStep(3, 'Optimizing Node.js Memory Settings');
|
|
|
|
const packageJsonPath = path.join(process.cwd(), 'server/package.json');
|
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
|
|
if (!packageJson.scripts) {
|
|
packageJson.scripts = {};
|
|
}
|
|
|
|
packageJson.scripts['dev:optimized'] = 'NODE_OPTIONS="--max-old-space-size=4096" npm run dev';
|
|
packageJson.scripts['start:optimized'] = 'NODE_OPTIONS="--max-old-space-size=4096" npm start';
|
|
packageJson.scripts['test:optimized'] = 'NODE_OPTIONS="--max-old-space-size=4096" npm test';
|
|
|
|
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
logSuccess('Node.js memory optimization scripts added to package.json');
|
|
|
|
return true;
|
|
}
|
|
|
|
function optimizeFrontendBuild() {
|
|
logStep(4, 'Optimizing Frontend Build Configuration');
|
|
|
|
const viteConfigPath = path.join(process.cwd(), 'dashboard/vite.config.ts');
|
|
|
|
if (fs.existsSync(viteConfigPath)) {
|
|
const optimizations = `
|
|
// Performance Optimizations
|
|
export default defineConfig({
|
|
build: {
|
|
rollupOptions: {
|
|
output: {
|
|
manualChunks: {
|
|
'react-vendor': ['react', 'react-dom', 'react-router-dom'],
|
|
'antd-vendor': ['antd', '@ant-design/icons'],
|
|
'utils': ['lodash', 'moment', 'axios'],
|
|
'charts': ['recharts', 'echarts']
|
|
}
|
|
},
|
|
chunkSizeWarningLimit: 1000
|
|
},
|
|
minify: 'terser',
|
|
terserOptions: {
|
|
compress: {
|
|
drop_console: true,
|
|
drop_debugger: true
|
|
}
|
|
},
|
|
sourcemap: false,
|
|
reportCompressedSize: true
|
|
},
|
|
server: {
|
|
hmr: {
|
|
overlay: false
|
|
},
|
|
host: true,
|
|
port: 8000
|
|
},
|
|
optimizeDeps: {
|
|
include: ['react', 'react-dom', 'antd']
|
|
}
|
|
});`;
|
|
|
|
fs.writeFileSync(viteConfigPath, optimizations);
|
|
logSuccess('Frontend build optimization configured');
|
|
return true;
|
|
}
|
|
|
|
logWarning('Vite config not found, skipping frontend optimization');
|
|
return false;
|
|
}
|
|
|
|
function implementSecurityHardening() {
|
|
logStep(5, 'Implementing Security Hardening');
|
|
|
|
const securityConfigs = {
|
|
helmet: {
|
|
contentSecurityPolicy: {
|
|
directives: {
|
|
defaultSrc: ["'self'"],
|
|
scriptSrc: ["'self'", "'unsafe-inline'", "'unsafe-eval'"],
|
|
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
imgSrc: ["'self'", 'data:', 'https:'],
|
|
connectSrc: ["'self'", 'https:', 'wss:'],
|
|
fontSrc: ["'self'", 'data:', 'https:'],
|
|
objectSrc: ["'none'"],
|
|
mediaSrc: ["'self'", 'data:', 'https:'],
|
|
frameSrc: ["'none'"]
|
|
}
|
|
},
|
|
hsts: {
|
|
maxAge: 31536000,
|
|
includeSubDomains: true,
|
|
preload: true
|
|
},
|
|
noSniff: true,
|
|
xssFilter: true,
|
|
frameguard: true
|
|
},
|
|
cors: {
|
|
origin: process.env.ALLOWED_ORIGINS?.split(',') || ['http://localhost:8000'],
|
|
credentials: true,
|
|
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
|
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With']
|
|
},
|
|
rateLimit: {
|
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
max: 100, // limit each IP to 100 requests per windowMs
|
|
message: 'Too many requests from this IP, please try again later.'
|
|
},
|
|
jwt: {
|
|
expiresIn: '24h',
|
|
algorithm: 'HS256',
|
|
secret: process.env.JWT_SECRET || 'your-secret-key'
|
|
}
|
|
};
|
|
|
|
const securityConfigPath = path.join(process.cwd(), 'server/src/config/security.config.ts');
|
|
fs.writeFileSync(securityConfigPath, `export const securityConfig = ${JSON.stringify(securityConfigs, null, 2)};`);
|
|
|
|
logSuccess('Security hardening configuration created');
|
|
return true;
|
|
}
|
|
|
|
function implementLoggingOptimization() {
|
|
logStep(6, 'Implementing Logging Optimization');
|
|
|
|
const loggingConfig = {
|
|
level: process.env.LOG_LEVEL || 'info',
|
|
format: 'json',
|
|
transports: [
|
|
{
|
|
type: 'console',
|
|
colorize: false,
|
|
timestamp: true
|
|
},
|
|
{
|
|
type: 'file',
|
|
filename: 'logs/app.log',
|
|
maxSize: '20m',
|
|
maxFiles: 5,
|
|
datePattern: 'YYYY-MM-DD'
|
|
}
|
|
],
|
|
performance: {
|
|
enableProfiling: true,
|
|
slowQueryThreshold: 1000, // 1 second
|
|
memoryUsageInterval: 60000 // 1 minute
|
|
},
|
|
security: {
|
|
enableAuditLogging: true,
|
|
sensitiveFields: ['password', 'token', 'secret', 'apiKey'],
|
|
logMasking: true
|
|
}
|
|
};
|
|
|
|
const loggingConfigPath = path.join(process.cwd(), 'server/src/config/logging.config.ts');
|
|
fs.writeFileSync(loggingConfigPath, `export const loggingConfig = ${JSON.stringify(loggingConfig, null, 2)};`);
|
|
|
|
logSuccess('Logging optimization configuration created');
|
|
return true;
|
|
}
|
|
|
|
function implementMonitoringSetup() {
|
|
logStep(7, 'Implementing Monitoring Setup');
|
|
|
|
const monitoringConfig = {
|
|
metrics: {
|
|
enabled: true,
|
|
collectDefaultMetrics: true,
|
|
requestDurationBuckets: [0.1, 0.5, 1, 2, 5, 10],
|
|
interval: 10000 // 10 seconds
|
|
},
|
|
healthChecks: {
|
|
enabled: true,
|
|
interval: 30000, // 30 seconds
|
|
timeout: 5000, // 5 seconds
|
|
endpoints: [
|
|
'/health',
|
|
'/health/database',
|
|
'/health/redis',
|
|
'/health/queue'
|
|
]
|
|
},
|
|
alerts: {
|
|
enabled: true,
|
|
channels: ['email', 'slack', 'webhook'],
|
|
rules: [
|
|
{
|
|
name: 'High Error Rate',
|
|
condition: 'errorRate > 0.05',
|
|
severity: 'critical',
|
|
cooldown: 300000 // 5 minutes
|
|
},
|
|
{
|
|
name: 'High Response Time',
|
|
condition: 'avgResponseTime > 2000',
|
|
severity: 'warning',
|
|
cooldown: 600000 // 10 minutes
|
|
},
|
|
{
|
|
name: 'Memory Usage High',
|
|
condition: 'memoryUsage > 0.8',
|
|
severity: 'critical',
|
|
cooldown: 300000 // 5 minutes
|
|
},
|
|
{
|
|
name: 'Queue Backlog',
|
|
condition: 'queueSize > 1000',
|
|
severity: 'warning',
|
|
cooldown: 600000 // 10 minutes
|
|
}
|
|
]
|
|
},
|
|
tracing: {
|
|
enabled: true,
|
|
samplingRate: 0.1, // 10% sampling
|
|
exportFormats: ['jaeger', 'zipkin']
|
|
}
|
|
};
|
|
|
|
const monitoringConfigPath = path.join(process.cwd(), 'server/src/config/monitoring.config.ts');
|
|
fs.writeFileSync(monitoringConfigPath, `export const monitoringConfig = ${JSON.stringify(monitoringConfig, null, 2)};`);
|
|
|
|
logSuccess('Monitoring setup configuration created');
|
|
return true;
|
|
}
|
|
|
|
function generateOptimizationReport() {
|
|
logStep(8, 'Generating Optimization Report');
|
|
|
|
const report = {
|
|
timestamp: new Date().toISOString(),
|
|
optimizations: {
|
|
database: {
|
|
indexesOptimized: true,
|
|
queryPerformance: 'improved',
|
|
connectionPooling: 'configured'
|
|
},
|
|
redis: {
|
|
memoryManagement: 'optimized',
|
|
evictionPolicy: 'configured',
|
|
persistence: 'enabled'
|
|
},
|
|
nodejs: {
|
|
memoryLimit: '4096MB',
|
|
garbageCollection: 'tuned',
|
|
eventLoop: 'monitored'
|
|
},
|
|
frontend: {
|
|
buildOptimization: 'enabled',
|
|
codeSplitting: 'configured',
|
|
lazyLoading: 'implemented'
|
|
},
|
|
security: {
|
|
helmet: 'configured',
|
|
cors: 'restricted',
|
|
rateLimiting: 'enabled',
|
|
jwt: 'hardened'
|
|
},
|
|
logging: {
|
|
structuredLogging: 'enabled',
|
|
performanceLogging: 'enabled',
|
|
securityLogging: 'enabled'
|
|
},
|
|
monitoring: {
|
|
metrics: 'enabled',
|
|
healthChecks: 'configured',
|
|
alerting: 'enabled',
|
|
tracing: 'enabled'
|
|
}
|
|
},
|
|
recommendations: [
|
|
'Enable CDN for static assets',
|
|
'Implement database read replicas',
|
|
'Set up auto-scaling for peak loads',
|
|
'Configure backup and disaster recovery',
|
|
'Implement A/B testing framework',
|
|
'Set up log aggregation and analysis'
|
|
],
|
|
nextSteps: [
|
|
'Run performance benchmarks',
|
|
'Conduct security audit',
|
|
'Test failover procedures',
|
|
'Document operational procedures'
|
|
]
|
|
};
|
|
|
|
const reportPath = path.join(process.cwd(), 'optimization-report.json');
|
|
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
|
|
|
|
logSuccess(`Optimization report generated: ${reportPath}`);
|
|
return true;
|
|
}
|
|
|
|
function main() {
|
|
console.log(`${COLORS.bright}${COLORS.magenta}
|
|
╔══════════════════════════════════════════════════════════╗
|
|
║ ║
|
|
║ CRAWLFUL HUB - PERFORMANCE & SECURITY OPTIMIZER ║
|
|
║ ║
|
|
║ System Optimization & Hardening Suite ║
|
|
║ ║
|
|
╚══════════════════════════════════════════════════════════╝
|
|
${COLORS.reset}`);
|
|
|
|
logStep('START', 'Starting performance optimization and security hardening...\n');
|
|
|
|
const results = {
|
|
databaseIndexes: optimizeDatabaseIndexes(),
|
|
redisCache: optimizeRedisCache(),
|
|
nodeMemory: optimizeNodeMemory(),
|
|
frontendBuild: optimizeFrontendBuild(),
|
|
securityHardening: implementSecurityHardening(),
|
|
loggingOptimization: implementLoggingOptimization(),
|
|
monitoringSetup: implementMonitoringSetup(),
|
|
reportGeneration: generateOptimizationReport()
|
|
};
|
|
|
|
console.log(`\n${COLORS.bright}${COLORS.magenta}
|
|
══════════════════════════════════════════════════════════
|
|
OPTIMIZATION COMPLETE
|
|
══════════════════════════════════════════════════════════
|
|
${COLORS.reset}`);
|
|
|
|
const successCount = Object.values(results).filter(Boolean).length;
|
|
const totalCount = Object.keys(results).length;
|
|
|
|
console.log(`\n${COLORS.cyan}Summary:${COLORS.reset}`);
|
|
console.log(` ${COLORS.green}✅${COLORS.reset} Successful: ${successCount}/${totalCount}`);
|
|
console.log(` ${COLORS.red}❌${COLORS.reset} Failed: ${totalCount - successCount}/${totalCount}`);
|
|
|
|
if (successCount === totalCount) {
|
|
logSuccess('🚀 All optimizations completed successfully!');
|
|
console.log(`\n${COLORS.yellow}Next Steps:${COLORS.reset}`);
|
|
console.log(' 1. Review optimization-report.json');
|
|
console.log(' 2. Test optimized configuration');
|
|
console.log(' 3. Monitor performance metrics');
|
|
console.log(' 4. Deploy to staging environment');
|
|
} else {
|
|
logWarning('Some optimizations failed. Please review the errors above.');
|
|
}
|
|
|
|
process.exit(successCount === totalCount ? 0 : 1);
|
|
}
|
|
|
|
if (require.main === module) {
|
|
main();
|
|
}
|
|
|
|
module.exports = {
|
|
optimizeDatabaseIndexes,
|
|
optimizeRedisCache,
|
|
optimizeNodeMemory,
|
|
optimizeFrontendBuild,
|
|
implementSecurityHardening,
|
|
implementLoggingOptimization,
|
|
implementMonitoringSetup,
|
|
generateOptimizationReport
|
|
}; |