feat: 添加MSW模拟服务和数据源集成

refactor: 重构页面组件移除冗余Layout组件

feat: 实现WebSocket和事件总线系统

feat: 添加队列和调度系统

docs: 更新架构文档和服务映射

style: 清理重复接口定义使用数据源

chore: 更新依赖项配置

feat: 添加运行时系统和领域引导

ci: 配置ESLint边界检查规则

build: 添加Redis和WebSocket依赖

test: 添加MSW浏览器环境入口

perf: 优化数据获取逻辑使用统一数据源

fix: 修复类型定义和状态管理问题
This commit is contained in:
2026-03-19 01:39:34 +08:00
parent cd55097dbf
commit 0dac26d781
176 changed files with 47075 additions and 8404 deletions

1231
scripts/deploy-prep.js Normal file

File diff suppressed because it is too large Load Diff

421
scripts/deployment-check.js Normal file
View File

@@ -0,0 +1,421 @@
#!/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 checkFileExists(filePath, description) {
try {
const exists = fs.existsSync(filePath);
if (exists) {
logSuccess(`${description} exists`);
} else {
logError(`${description} not found`);
}
return exists;
}
}
function checkDirectoryStructure() {
logStep(1, 'Checking Directory Structure');
const requiredDirectories = [
'server/src',
'dashboard/src',
'client/src',
'extension/src',
'docs',
'.trae/rules'
];
let allExist = true;
requiredDirectories.forEach(dir => {
const exists = checkFileExists(dir, `Directory ${dir}`);
if (!exists) allExist = false;
});
return allExist;
}
function checkConfigurationFiles() {
logStep(2, 'Checking Configuration Files');
const requiredConfigs = [
'server/package.json',
'dashboard/package.json',
'client/package.json',
'extension/package.json',
'server/tsconfig.json',
'dashboard/tsconfig.json',
'server/.eslintrc.js'
];
let allExist = true;
requiredConfigs.forEach(config => {
const exists = checkFileExists(config, `Config ${config}`);
if (!exists) allExist = false;
});
return allExist;
}
function checkDatabaseSchema() {
logStep(3, 'Checking Database Schema');
const schemaFile = 'server/src/database/DatabaseSchema.ts';
const exists = checkFileExists(schemaFile, 'Database Schema');
if (exists) {
const schema = require(path.join(process.cwd(), schemaFile));
const requiredTables = [
'cf_product',
'cf_order',
'cf_customer',
'cf_tenant',
'cf_shop',
'cf_invoice',
'cf_settlement'
];
let allTablesExist = true;
requiredTables.forEach(table => {
if (schema.tables && schema.tables[table]) {
logSuccess(`Table ${table} exists in schema`);
} else {
logError(`Table ${table} missing from schema`);
allTablesExist = false;
}
});
return allTablesExist;
}
return false;
}
function checkCoreServices() {
logStep(4, 'Checking Core Services');
const requiredServices = [
'server/src/core/auth/RBACEngine.ts',
'server/src/core/cache/RedisService.ts',
'server/src/core/engine/CoreEngineService.ts',
'server/src/core/runtime/DomainEventBus.ts',
'server/src/core/telemetry/AlertService.ts'
];
let allExist = true;
requiredServices.forEach(service => {
const exists = checkFileExists(service, `Service ${service}`);
if (!exists) allExist = false;
});
return allExist;
}
function checkAPIControllers() {
logStep(5, 'Checking API Controllers');
const requiredControllers = [
'server/src/api/controllers/AuthController.ts',
'server/src/api/controllers/ProductController.ts',
'server/src/api/controllers/OrderController.ts',
'server/src/api/controllers/PricingController.ts',
'server/src/api/controllers/BillingController.ts',
'server/src/api/controllers/AnalyticsController.ts'
];
let allExist = true;
requiredControllers.forEach(controller => {
const exists = checkFileExists(controller, `Controller ${controller}`);
if (!exists) allExist = false;
});
return allExist;
}
function checkFrontendPages() {
logStep(6, 'Checking Frontend Pages');
const requiredPages = [
'dashboard/src/pages/DashboardPage.tsx',
'dashboard/src/pages/Product/index.tsx',
'dashboard/src/pages/Orders/index.tsx',
'dashboard/src/pages/TaskCenter/index.tsx',
'dashboard/src/pages/Compliance/index.tsx',
'dashboard/src/pages/Settings/SystemSettings.tsx',
'dashboard/src/pages/Analytics/index.tsx',
'dashboard/src/pages/Reports/index.tsx'
];
let allExist = true;
requiredPages.forEach(page => {
const exists = checkFileExists(page, `Page ${page}`);
if (!exists) allExist = false;
});
return allExist;
}
function checkDocumentation() {
logStep(7, 'Checking Documentation');
const requiredDocs = [
'docs/00_Business/Business_Blueprint.md',
'docs/00_Business/Business_ClosedLoops.md',
'docs/01_Architecture/System_Architecture.md',
'docs/01_Architecture/SERVICE_MAP.md',
'docs/01_Architecture/STATE_MACHINE.md',
'docs/02_Backend/API_Specs/Data_API_Specs.md',
'docs/03_Frontend/Frontend_Design.md',
'docs/05_AI/AI_Strategy.md',
'.trae/rules/project-specific-rules.md'
];
let allExist = true;
requiredDocs.forEach(doc => {
const exists = checkFileExists(doc, `Documentation ${doc}`);
if (!exists) allExist = false;
});
return allExist;
}
function checkSecurityConfiguration() {
logStep(8, 'Checking Security Configuration');
const securityFiles = [
'server/src/core/guards/rbac.guard.ts',
'server/src/core/guards/service.guard.ts',
'server/src/core/guards/sla.guard.ts',
'server/src/core/security/SecurityProfilingService.ts',
'server/src/core/security/DataComplianceService.ts'
];
let allExist = true;
securityFiles.forEach(file => {
const exists = checkFileExists(file, `Security ${file}`);
if (!exists) allExist = false;
});
return allExist;
}
function checkPluginSystem() {
logStep(9, 'Checking Plugin System');
const pluginFiles = [
'server/src/core/runtime/PluginManager.ts',
'server/src/core/connectors/AmazonConnector.ts',
'server/src/core/connectors/eBayConnector.ts',
'server/src/core/connectors/ShopifyConnector.ts',
'extension/src/background/DOMParser.ts',
'extension/src/background/FingerprintManager.ts'
];
let allExist = true;
pluginFiles.forEach(file => {
const exists = checkFileExists(file, `Plugin ${file}`);
if (!exists) allExist = false;
});
return allExist;
}
function checkTestingInfrastructure() {
logStep(10, 'Checking Testing Infrastructure');
const testFiles = [
'server/src/tests/system-integration.test.ts',
'dashboard/src/tests/frontend.functional.test.ts',
'dashboard/src/tests/frontend.integration.test.ts'
];
let allExist = true;
testFiles.forEach(file => {
const exists = checkFileExists(file, `Test ${file}`);
if (!exists) allExist = false;
});
return allExist;
}
function checkRuntimeComponents() {
logStep(11, 'Checking Runtime Components');
const runtimeFiles = [
'server/src/core/runtime/DomainEventBus.ts',
'server/src/core/scheduler/AdaptiveScheduler.ts',
'server/src/core/telemetry/GlobalTracingService.ts',
'server/src/core/workers/WorkerHub.ts'
];
let allExist = true;
runtimeFiles.forEach(file => {
const exists = checkFileExists(file, `Runtime ${file}`);
if (!exists) allExist = false;
});
return allExist;
}
function checkDeploymentReadiness() {
logStep(12, 'Checking Deployment Readiness');
const deploymentFiles = [
'.github/workflows/ci-cd.yml',
'scripts/db-init.sql',
'scripts/env-config.sh',
'server/package.json',
'dashboard/package.json'
];
let allExist = true;
deploymentFiles.forEach(file => {
const exists = checkFileExists(file, `Deployment ${file}`);
if (!exists) allExist = false;
});
return allExist;
}
function generateDeploymentReport() {
logStep(13, 'Generating Deployment Report');
const report = {
timestamp: new Date().toISOString(),
system: {
name: 'Crawlful Hub',
version: '1.0.0',
environment: process.env.NODE_ENV || 'development'
},
checks: {
directoryStructure: checkDirectoryStructure(),
configurationFiles: checkConfigurationFiles(),
databaseSchema: checkDatabaseSchema(),
coreServices: checkCoreServices(),
apiControllers: checkAPIControllers(),
frontendPages: checkFrontendPages(),
documentation: checkDocumentation(),
securityConfiguration: checkSecurityConfiguration(),
pluginSystem: checkPluginSystem(),
testingInfrastructure: checkTestingInfrastructure(),
runtimeComponents: checkRuntimeComponents(),
deploymentReadiness: checkDeploymentReadiness()
},
summary: {
totalChecks: 12,
passedChecks: 0,
failedChecks: 0,
readiness: 'unknown'
}
};
report.summary.passedChecks = Object.values(report.checks).filter(Boolean).length;
report.summary.failedChecks = Object.values(report.checks).filter(val => !val).length;
report.summary.readiness = report.summary.passedChecks === report.summary.totalChecks ? 'READY' : 'NOT_READY';
const reportPath = path.join(process.cwd(), 'deployment-report.json');
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
logSuccess(`Deployment report generated: ${reportPath}`);
if (report.summary.readiness === 'READY') {
logSuccess('🚀 System is READY for deployment!');
} else {
logWarning(`⚠️ System is NOT ready for deployment. ${report.summary.failedChecks} checks failed.`);
}
return report.summary.readiness === 'READY';
}
function main() {
console.log(`${COLORS.bright}${COLORS.cyan}
╔════════════════════════════════════════════════════════════╗
║ ║
║ CRAWLFUL HUB - SYSTEM DEPLOYMENT CHECKER ║
║ ║
║ Comprehensive System Validation & Deployment Prep ║
║ ║
╚════════════════════════════════════════════════════════════╝
${COLORS.reset}`);
logStep('START', 'Starting comprehensive system validation...\n');
const isReady = generateDeploymentReport();
console.log(`\n${COLORS.bright}${COLORS.cyan}
════════════════════════════════════════════════════════════
VALIDATION COMPLETE
════════════════════════════════════════════════════════════
${COLORS.reset}`);
process.exit(isReady ? 0 : 1);
}
if (require.main === module) {
main();
}
module.exports = {
checkDirectoryStructure,
checkConfigurationFiles,
checkDatabaseSchema,
checkCoreServices,
checkAPIControllers,
checkFrontendPages,
checkDocumentation,
checkSecurityConfiguration,
checkPluginSystem,
checkTestingInfrastructure,
checkRuntimeComponents,
checkDeploymentReadiness,
generateDeploymentReport
};

466
scripts/optimize-system.js Normal file
View File

@@ -0,0 +1,466 @@
#!/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
};

789
scripts/run-tests.js Normal file
View File

@@ -0,0 +1,789 @@
#!/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 runUnitTests() {
logStep(1, 'Running Unit Tests');
try {
const testResults = {
server: runServerUnitTests(),
dashboard: runDashboardUnitTests(),
client: runClientUnitTests(),
extension: runExtensionUnitTests()
};
const totalTests = Object.values(testResults).reduce((sum, result) => sum + result.total, 0);
const passedTests = Object.values(testResults).reduce((sum, result) => sum + result.passed, 0);
const failedTests = Object.values(testResults).reduce((sum, result) => sum + result.failed, 0);
console.log(`\n${COLORS.cyan}Unit Tests Summary:${COLORS.reset}`);
console.log(` Total: ${totalTests}`);
console.log(` ${COLORS.green}Passed: ${passedTests}${COLORS.reset}`);
console.log(` ${COLORS.red}Failed: ${failedTests}${COLORS.reset}`);
console.log(` Success Rate: ${((passedTests / totalTests) * 100).toFixed(2)}%`);
return failedTests === 0;
} catch (error) {
logError(`Unit tests failed: ${error.message}`);
return false;
}
}
function runServerUnitTests() {
try {
const output = execSync('cd server && npm test -- --coverage --json', {
encoding: 'utf8',
stdio: 'pipe'
});
const results = JSON.parse(output);
const total = results.numTotalTests;
const passed = results.numPassedTests;
const failed = results.numFailedTests;
console.log(`\n${COLORS.cyan}Server Unit Tests:${COLORS.reset}`);
console.log(` Total: ${total}, Passed: ${passed}, Failed: ${failed}`);
console.log(` Coverage: ${results.coverageMap ? 'Generated' : 'Not available'}`);
return { total, passed, failed };
} catch (error) {
logError('Server unit tests failed');
return { total: 0, passed: 0, failed: 1 };
}
}
function runDashboardUnitTests() {
try {
const output = execSync('cd dashboard && npm test -- --coverage --json', {
encoding: 'utf8',
stdio: 'pipe'
});
const results = JSON.parse(output);
const total = results.numTotalTests;
const passed = results.numPassedTests;
const failed = results.numFailedTests;
console.log(`\n${COLORS.cyan}Dashboard Unit Tests:${COLORS.reset}`);
console.log(` Total: ${total}, Passed: ${passed}, Failed: ${failed}`);
console.log(` Coverage: ${results.coverageMap ? 'Generated' : 'Not available'}`);
return { total, passed, failed };
} catch (error) {
logError('Dashboard unit tests failed');
return { total: 0, passed: 0, failed: 1 };
}
}
function runClientUnitTests() {
try {
const output = execSync('cd client && npm test -- --coverage --json', {
encoding: 'utf8',
stdio: 'pipe'
});
const results = JSON.parse(output);
const total = results.numTotalTests;
const passed = results.numPassedTests;
const failed = results.numFailedTests;
console.log(`\n${COLORS.cyan}Client Unit Tests:${COLORS.reset}`);
console.log(` Total: ${total}, Passed: ${passed}, Failed: ${failed}`);
console.log(` Coverage: ${results.coverageMap ? 'Generated' : 'Not available'}`);
return { total, passed, failed };
} catch (error) {
logError('Client unit tests failed');
return { total: 0, passed: 0, failed: 1 };
}
}
function runExtensionUnitTests() {
try {
const output = execSync('cd extension && npm test -- --coverage --json', {
encoding: 'utf8',
stdio: 'pipe'
});
const results = JSON.parse(output);
const total = results.numTotalTests;
const passed = results.numPassedTests;
const failed = results.numFailedTests;
console.log(`\n${COLORS.cyan}Extension Unit Tests:${COLORS.reset}`);
console.log(` Total: ${total}, Passed: ${passed}, Failed: ${failed}`);
console.log(` Coverage: ${results.coverageMap ? 'Generated' : 'Not available'}`);
return { total, passed, failed };
} catch (error) {
logError('Extension unit tests failed');
return { total: 0, passed: 0, failed: 1 };
}
}
function runIntegrationTests() {
logStep(2, 'Running Integration Tests');
try {
const testResults = {
api: runAPIIntegrationTests(),
database: runDatabaseIntegrationTests(),
redis: runRedisIntegrationTests(),
websocket: runWebSocketIntegrationTests()
};
const totalTests = Object.values(testResults).reduce((sum, result) => sum + result.total, 0);
const passedTests = Object.values(testResults).reduce((sum, result) => sum + result.passed, 0);
const failedTests = Object.values(testResults).reduce((sum, result) => sum + result.failed, 0);
console.log(`\n${COLORS.cyan}Integration Tests Summary:${COLORS.reset}`);
console.log(` Total: ${totalTests}`);
console.log(` ${COLORS.green}Passed: ${passedTests}${COLORS.reset}`);
console.log(` ${COLORS.red}Failed: ${failedTests}${COLORS.reset}`);
console.log(` Success Rate: ${((passedTests / totalTests) * 100).toFixed(2)}%`);
return failedTests === 0;
} catch (error) {
logError(`Integration tests failed: ${error.message}`);
return false;
}
}
function runAPIIntegrationTests() {
try {
const output = execSync('cd server && npm run test:integration', {
encoding: 'utf8',
stdio: 'pipe'
});
const results = parseTestOutput(output);
console.log(`\n${COLORS.cyan}API Integration Tests:${COLORS.reset}`);
console.log(` Total: ${results.total}, Passed: ${results.passed}, Failed: ${results.failed}`);
return results;
} catch (error) {
logError('API integration tests failed');
return { total: 0, passed: 0, failed: 1 };
}
}
function runDatabaseIntegrationTests() {
try {
const output = execSync('cd server && npm run test:database', {
encoding: 'utf8',
stdio: 'pipe'
});
const results = parseTestOutput(output);
console.log(`\n${COLORS.cyan}Database Integration Tests:${COLORS.reset}`);
console.log(` Total: ${results.total}, Passed: ${results.passed}, Failed: ${results.failed}`);
return results;
} catch (error) {
logError('Database integration tests failed');
return { total: 0, passed: 0, failed: 1 };
}
}
function runRedisIntegrationTests() {
try {
const output = execSync('cd server && npm run test:redis', {
encoding: 'utf8',
stdio: 'pipe'
});
const results = parseTestOutput(output);
console.log(`\n${COLORS.cyan}Redis Integration Tests:${COLORS.reset}`);
console.log(` Total: ${results.total}, Passed: ${results.passed}, Failed: ${results.failed}`);
return results;
} catch (error) {
logError('Redis integration tests failed');
return { total: 0, passed: 0, failed: 1 };
}
}
function runWebSocketIntegrationTests() {
try {
const output = execSync('cd server && npm run test:websocket', {
encoding: 'utf8',
stdio: 'pipe'
});
const results = parseTestOutput(output);
console.log(`\n${COLORS.cyan}WebSocket Integration Tests:${COLORS.reset}`);
console.log(` Total: ${results.total}, Passed: ${results.passed}, Failed: ${results.failed}`);
return results;
} catch (error) {
logError('WebSocket integration tests failed');
return { total: 0, passed: 0, failed: 1 };
}
}
function parseTestOutput(output) {
const lines = output.split('\n');
let total = 0, passed = 0, failed = 0;
lines.forEach(line => {
const totalMatch = line.match(/Tests:\s+(\d+)/);
const passedMatch = line.match(/Passed:\s+(\d+)/);
const failedMatch = line.match(/Failed:\s+(\d+)/);
if (totalMatch) total = parseInt(totalMatch[1]);
if (passedMatch) passed = parseInt(passedMatch[1]);
if (failedMatch) failed = parseInt(failedMatch[1]);
});
return { total, passed, failed };
}
function runE2ETests() {
logStep(3, 'Running End-to-End Tests');
try {
const testResults = {
userFlow: runUserFlowTests(),
businessFlow: runBusinessFlowTests(),
adminFlow: runAdminFlowTests()
};
const totalTests = Object.values(testResults).reduce((sum, result) => sum + result.total, 0);
const passedTests = Object.values(testResults).reduce((sum, result) => sum + result.passed, 0);
const failedTests = Object.values(testResults).reduce((sum, result) => sum + result.failed, 0);
console.log(`\n${COLORS.cyan}E2E Tests Summary:${COLORS.reset}`);
console.log(` Total: ${totalTests}`);
console.log(` ${COLORS.green}Passed: ${passedTests}${COLORS.reset}`);
console.log(` ${COLORS.red}Failed: ${failedTests}${COLORS.reset}`);
console.log(` Success Rate: ${((passedTests / totalTests) * 100).toFixed(2)}%`);
return failedTests === 0;
} catch (error) {
logError(`E2E tests failed: ${error.message}`);
return false;
}
}
function runUserFlowTests() {
try {
const output = execSync('cd dashboard && npm run test:e2e:user', {
encoding: 'utf8',
stdio: 'pipe'
});
const results = parseTestOutput(output);
console.log(`\n${COLORS.cyan}User Flow Tests:${COLORS.reset}`);
console.log(` Total: ${results.total}, Passed: ${results.passed}, Failed: ${results.failed}`);
return results;
} catch (error) {
logError('User flow tests failed');
return { total: 0, passed: 0, failed: 1 };
}
}
function runBusinessFlowTests() {
try {
const output = execSync('cd dashboard && npm run test:e2e:business', {
encoding: 'utf8',
stdio: 'pipe'
});
const results = parseTestOutput(output);
console.log(`\n${COLORS.cyan}Business Flow Tests:${COLORS.reset}`);
console.log(` Total: ${results.total}, Passed: ${results.passed}, Failed: ${results.failed}`);
return results;
} catch (error) {
logError('Business flow tests failed');
return { total: 0, passed: 0, failed: 1 };
}
}
function runAdminFlowTests() {
try {
const output = execSync('cd dashboard && npm run test:e2e:admin', {
encoding: 'utf8',
stdio: 'pipe'
});
const results = parseTestOutput(output);
console.log(`\n${COLORS.cyan}Admin Flow Tests:${COLORS.reset}`);
console.log(` Total: ${results.total}, Passed: ${results.passed}, Failed: ${results.failed}`);
return results;
} catch (error) {
logError('Admin flow tests failed');
return { total: 0, passed: 0, failed: 1 };
}
}
function runPerformanceTests() {
logStep(4, 'Running Performance Tests');
try {
const testResults = {
load: runLoadTests(),
stress: runStressTests(),
endurance: runEnduranceTests()
};
const totalTests = Object.values(testResults).reduce((sum, result) => sum + result.total, 0);
const passedTests = Object.values(testResults).reduce((sum, result) => sum + result.passed, 0);
const failedTests = Object.values(testResults).reduce((sum, result) => sum + result.failed, 0);
console.log(`\n${COLORS.cyan}Performance Tests Summary:${COLORS.reset}`);
console.log(` Total: ${totalTests}`);
console.log(` ${COLORS.green}Passed: ${passedTests}${COLORS.reset}`);
console.log(` ${COLORS.red}Failed: ${failedTests}${COLORS.reset}`);
console.log(` Success Rate: ${((passedTests / totalTests) * 100).toFixed(2)}%`);
return failedTests === 0;
} catch (error) {
logError(`Performance tests failed: ${error.message}`);
return false;
}
}
function runLoadTests() {
try {
const output = execSync('cd server && npm run test:load', {
encoding: 'utf8',
stdio: 'pipe'
});
const results = parseTestOutput(output);
console.log(`\n${COLORS.cyan}Load Tests:${COLORS.reset}`);
console.log(` Total: ${results.total}, Passed: ${results.passed}, Failed: ${results.failed}`);
return results;
} catch (error) {
logError('Load tests failed');
return { total: 0, passed: 0, failed: 1 };
}
}
function runStressTests() {
try {
const output = execSync('cd server && npm run test:stress', {
encoding: 'utf8',
stdio: 'pipe'
});
const results = parseTestOutput(output);
console.log(`\n${COLORS.cyan}Stress Tests:${COLORS.reset}`);
console.log(` Total: ${results.total}, Passed: ${results.passed}, Failed: ${results.failed}`);
return results;
} catch (error) {
logError('Stress tests failed');
return { total: 0, passed: 0, failed: 1 };
}
}
function runEnduranceTests() {
try {
const output = execSync('cd server && npm run test:endurance', {
encoding: 'utf8',
stdio: 'pipe'
});
const results = parseTestOutput(output);
console.log(`\n${COLORS.cyan}Endurance Tests:${COLORS.reset}`);
console.log(` Total: ${results.total}, Passed: ${results.passed}, Failed: ${results.failed}`);
return results;
} catch (error) {
logError('Endurance tests failed');
return { total: 0, passed: 0, failed: 1 };
}
}
function runSecurityTests() {
logStep(5, 'Running Security Tests');
try {
const testResults = {
vulnerability: runVulnerabilityTests(),
penetration: runPenetrationTests(),
compliance: runComplianceTests()
};
const totalTests = Object.values(testResults).reduce((sum, result) => sum + result.total, 0);
const passedTests = Object.values(testResults).reduce((sum, result) => sum + result.passed, 0);
const failedTests = Object.values(testResults).reduce((sum, result) => sum + result.failed, 0);
console.log(`\n${COLORS.cyan}Security Tests Summary:${COLORS.reset}`);
console.log(` Total: ${totalTests}`);
console.log(` ${COLORS.green}Passed: ${passedTests}${COLORS.reset}`);
console.log(` ${COLORS.red}Failed: ${failedTests}${COLORS.reset}`);
console.log(` Success Rate: ${((passedTests / totalTests) * 100).toFixed(2)}%`);
return failedTests === 0;
} catch (error) {
logError(`Security tests failed: ${error.message}`);
return false;
}
}
function runVulnerabilityTests() {
try {
const output = execSync('npm audit --json', {
encoding: 'utf8',
stdio: 'pipe'
});
const results = JSON.parse(output);
const vulnerabilities = results.vulnerabilities || {};
const total = Object.values(vulnerabilities).reduce((sum, vulns) => sum + vulns.length, 0);
const critical = vulnerabilities.critical ? vulnerabilities.critical.length : 0;
const high = vulnerabilities.high ? vulnerabilities.high.length : 0;
const moderate = vulnerabilities.moderate ? vulnerabilities.moderate.length : 0;
const low = vulnerabilities.low ? vulnerabilities.low.length : 0;
const passed = total - (critical + high);
const failed = critical + high;
console.log(`\n${COLORS.cyan}Vulnerability Tests:${COLORS.reset}`);
console.log(` Critical: ${critical}, High: ${high}, Moderate: ${moderate}, Low: ${low}`);
console.log(` Total: ${total}, Passed: ${passed}, Failed: ${failed}`);
return { total, passed, failed };
} catch (error) {
logError('Vulnerability tests failed');
return { total: 0, passed: 0, failed: 1 };
}
}
function runPenetrationTests() {
try {
const output = execSync('cd server && npm run test:security', {
encoding: 'utf8',
stdio: 'pipe'
});
const results = parseTestOutput(output);
console.log(`\n${COLORS.cyan}Penetration Tests:${COLORS.reset}`);
console.log(` Total: ${results.total}, Passed: ${results.passed}, Failed: ${results.failed}`);
return results;
} catch (error) {
logError('Penetration tests failed');
return { total: 0, passed: 0, failed: 1 };
}
}
function runComplianceTests() {
try {
const output = execSync('cd server && npm run test:compliance', {
encoding: 'utf8',
stdio: 'pipe'
});
const results = parseTestOutput(output);
console.log(`\n${COLORS.cyan}Compliance Tests:${COLORS.reset}`);
console.log(` Total: ${results.total}, Passed: ${results.passed}, Failed: ${results.failed}`);
return results;
} catch (error) {
logError('Compliance tests failed');
return { total: 0, passed: 0, failed: 1 };
}
}
function runCodeQualityChecks() {
logStep(6, 'Running Code Quality Checks');
try {
const checkResults = {
linting: runLintingChecks(),
formatting: runFormattingChecks(),
complexity: runComplexityChecks(),
duplication: runDuplicationChecks()
};
const totalChecks = Object.values(checkResults).reduce((sum, result) => sum + result.total, 0);
const passedChecks = Object.values(checkResults).reduce((sum, result) => sum + result.passed, 0);
const failedChecks = Object.values(checkResults).reduce((sum, result) => sum + result.failed, 0);
console.log(`\n${COLORS.cyan}Code Quality Checks Summary:${COLORS.reset}`);
console.log(` Total: ${totalChecks}`);
console.log(` ${COLORS.green}Passed: ${passedChecks}${COLORS.reset}`);
console.log(` ${COLORS.red}Failed: ${failedChecks}${COLORS.reset}`);
console.log(` Success Rate: ${((passedChecks / totalChecks) * 100).toFixed(2)}%`);
return failedChecks === 0;
} catch (error) {
logError(`Code quality checks failed: ${error.message}`);
return false;
}
}
function runLintingChecks() {
try {
const output = execSync('npm run lint -- --format json', {
encoding: 'utf8',
stdio: 'pipe'
});
const results = JSON.parse(output);
const total = results.length;
const passed = results.filter(r => r.errorCount === 0 && r.warningCount === 0).length;
const failed = total - passed;
console.log(`\n${COLORS.cyan}Linting Checks:${COLORS.reset}`);
console.log(` Total: ${total}, Passed: ${passed}, Failed: ${failed}`);
return { total, passed, failed };
} catch (error) {
logError('Linting checks failed');
return { total: 0, passed: 0, failed: 1 };
}
}
function runFormattingChecks() {
try {
const output = execSync('npm run format:check', {
encoding: 'utf8',
stdio: 'pipe'
});
const results = parseTestOutput(output);
console.log(`\n${COLORS.cyan}Formatting Checks:${COLORS.reset}`);
console.log(` Total: ${results.total}, Passed: ${results.passed}, Failed: ${results.failed}`);
return results;
} catch (error) {
logError('Formatting checks failed');
return { total: 0, passed: 0, failed: 1 };
}
}
function runComplexityChecks() {
try {
const output = execSync('npm run complexity:check', {
encoding: 'utf8',
stdio: 'pipe'
});
const results = parseTestOutput(output);
console.log(`\n${COLORS.cyan}Complexity Checks:${COLORS.reset}`);
console.log(` Total: ${results.total}, Passed: ${results.passed}, Failed: ${results.failed}`);
return results;
} catch (error) {
logError('Complexity checks failed');
return { total: 0, passed: 0, failed: 1 };
}
}
function runDuplicationChecks() {
try {
const output = execSync('npm run duplication:check', {
encoding: 'utf8',
stdio: 'pipe'
});
const results = parseTestOutput(output);
console.log(`\n${COLORS.cyan}Duplication Checks:${COLORS.reset}`);
console.log(` Total: ${results.total}, Passed: ${results.passed}, Failed: ${results.failed}`);
return results;
} catch (error) {
logError('Duplication checks failed');
return { total: 0, passed: 0, failed: 1 };
}
}
function generateTestReport() {
logStep(7, 'Generating Test Report');
const report = {
timestamp: new Date().toISOString(),
project: {
name: 'Crawlful Hub',
version: '1.0.0'
},
summary: {
totalTests: 0,
passedTests: 0,
failedTests: 0,
skippedTests: 0,
successRate: 0,
duration: 0
},
categories: {
unitTests: {
total: 0,
passed: 0,
failed: 0,
coverage: 0
},
integrationTests: {
total: 0,
passed: 0,
failed: 0
},
e2eTests: {
total: 0,
passed: 0,
failed: 0
},
performanceTests: {
total: 0,
passed: 0,
failed: 0
},
securityTests: {
total: 0,
passed: 0,
failed: 0
},
codeQualityChecks: {
total: 0,
passed: 0,
failed: 0
}
},
recommendations: [
'Fix failing tests before deployment',
'Improve test coverage for critical paths',
'Add more integration tests',
'Implement automated security scanning',
'Set up continuous testing pipeline'
],
nextSteps: [
'Review test failures',
'Fix critical issues',
'Re-run failed tests',
'Update documentation',
'Prepare for deployment'
]
};
const reportPath = path.join(process.cwd(), 'test-report.json');
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
logSuccess(`Test report generated: ${reportPath}`);
return true;
}
function main() {
console.log(`${COLORS.bright}${COLORS.blue}
╔══════════════════════════════════════════════════════════╗
║ ║
║ CRAWLFUL HUB - COMPREHENSIVE TEST SUITE ║
║ ║
║ Complete Quality Assurance & Testing ║
║ ║
╚══════════════════════════════════════════════════════════╝
${COLORS.reset}`);
logStep('START', 'Starting comprehensive testing...\n');
const results = {
unitTests: runUnitTests(),
integrationTests: runIntegrationTests(),
e2eTests: runE2ETests(),
performanceTests: runPerformanceTests(),
securityTests: runSecurityTests(),
codeQualityChecks: runCodeQualityChecks(),
reportGeneration: generateTestReport()
};
console.log(`\n${COLORS.bright}${COLORS.blue}
══════════════════════════════════════════════════════════
TESTING 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 tests completed successfully!');
console.log(`\n${COLORS.yellow}Next Steps:${COLORS.reset}`);
console.log(' 1. Review test-report.json');
console.log(' 2. Check test coverage reports');
console.log(' 3. Review performance metrics');
console.log(' 4. Address any warnings');
console.log(' 5. Prepare for deployment');
} else {
logWarning('Some tests failed. Please review the errors above.');
console.log(`\n${COLORS.yellow}Action Items:${COLORS.reset}`);
console.log(' 1. Review test failures');
console.log(' 2. Fix critical issues');
console.log(' 3. Re-run failed tests');
console.log(' 4. Update documentation');
}
process.exit(successCount === totalCount ? 0 : 1);
}
if (require.main === module) {
main();
}
module.exports = {
runUnitTests,
runIntegrationTests,
runE2ETests,
runPerformanceTests,
runSecurityTests,
runCodeQualityChecks,
generateTestReport
};