mirror of
https://github.com/Silentely/eSIM-Tools.git
synced 2026-09-03 06:24:20 +08:00
feat: 重构Netlify Functions架构并增强代码质量
- 新增统一的中间件模块,提供鉴权、CORS和错误处理功能 - 重构所有Functions使用withAuth中间件简化代码结构 - 添加安全存储模块替代localStorage,防御XSS攻击 - 引入HTML清理工具,自动转义特殊字符和验证URL安全性 - 创建代码质量检查脚本,验证语法、环境变量和依赖完整性 - 添加构建日志工具和重构脚本,统一替换console.log为Logger - 引入ESLint配置,提升代码质量和一致性 - 重构Giffgaff相关API,统一错误处理和验证逻辑 - 优化构建脚本,添加压缩和图片优化功能 - 新增健康检查端点,用于服务监控和状态报告
This commit is contained in:
61
scripts/apply-middleware.sh
Normal file
61
scripts/apply-middleware.sh
Normal file
@@ -0,0 +1,61 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 批量应用中间件到剩余Functions的脚本
|
||||
# 使用方法: bash scripts/apply-middleware.sh
|
||||
|
||||
set -e
|
||||
|
||||
FUNCTIONS_DIR="netlify/functions"
|
||||
|
||||
echo "🔧 开始批量重构Functions..."
|
||||
echo ""
|
||||
|
||||
# 定义需要重构的函数列表(排除已重构的)
|
||||
FUNCTIONS=(
|
||||
"giffgaff-graphql"
|
||||
"giffgaff-mfa-challenge"
|
||||
"giffgaff-mfa-validation"
|
||||
"giffgaff-sms-activate"
|
||||
"auto-activate-esim"
|
||||
)
|
||||
|
||||
for func in "${FUNCTIONS[@]}"; do
|
||||
FILE="${FUNCTIONS_DIR}/${func}.js"
|
||||
|
||||
if [ ! -f "$FILE" ]; then
|
||||
echo "⚠️ 跳过不存在的文件: $FILE"
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "📝 处理: $func.js"
|
||||
|
||||
# 备份原文件
|
||||
cp "$FILE" "${FILE}.backup"
|
||||
|
||||
# 检查是否已经使用中间件
|
||||
if grep -q "withAuth" "$FILE"; then
|
||||
echo " ✅ 已使用中间件,跳过"
|
||||
rm "${FILE}.backup"
|
||||
continue
|
||||
fi
|
||||
|
||||
echo " ⏳ 添加中间件导入..."
|
||||
# 在第一个require之后添加中间件导入
|
||||
if ! grep -q "_shared/middleware" "$FILE"; then
|
||||
sed -i.tmp "1,/const.*require/s/\(const.*require.*\);/\1;\nconst { withAuth, validateInput, AuthError } = require('.\/\_shared\/middleware');/" "$FILE"
|
||||
rm "${FILE}.tmp" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
echo " ✅ 完成"
|
||||
echo ""
|
||||
done
|
||||
|
||||
echo "✨ 批量重构完成!"
|
||||
echo ""
|
||||
echo "📋 请手动完成以下步骤:"
|
||||
echo " 1. 检查每个函数的备份文件(.backup)"
|
||||
echo " 2. 完成exports.handler重构为withAuth包装"
|
||||
echo " 3. 添加输入验证schema"
|
||||
echo " 4. 测试功能是否正常"
|
||||
echo ""
|
||||
echo "💡 参考示例: netlify/functions/giffgaff-token-exchange.js"
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env node
|
||||
const BuildLogger = require('./logger.js');
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
@@ -61,16 +63,16 @@ async function copyDirectory(source, destination) {
|
||||
}
|
||||
|
||||
(async () => {
|
||||
console.log('🧹 清理 dist 目录...');
|
||||
BuildLogger.log('🧹 清理 dist 目录...');
|
||||
await removeDist();
|
||||
await fs.promises.mkdir(distDir, { recursive: true });
|
||||
|
||||
for (const entry of entries) {
|
||||
console.log(`📦 复制 ${entry} -> dist/${entry}`);
|
||||
BuildLogger.log(`📦 复制 ${entry} -> dist/${entry}`);
|
||||
await copyEntry(entry);
|
||||
}
|
||||
|
||||
console.log('✅ 静态资源构建完成,输出目录 dist/');
|
||||
BuildLogger.success(' 静态资源构建完成,输出目录 dist/');
|
||||
})().catch(err => {
|
||||
console.error('构建静态资源失败:', err);
|
||||
process.exitCode = 1;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const zlib = require('zlib');
|
||||
const BuildLogger = require('./logger.js');
|
||||
|
||||
const { promisify } = require('util');
|
||||
|
||||
const gzip = promisify(zlib.gzip);
|
||||
@@ -83,7 +85,7 @@ async function compressFile(filePath) {
|
||||
brotliSize = brotlied.length;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`Brotli压缩失败 ${fileName}:`, error.message);
|
||||
BuildLogger.log(`Brotli压缩失败 ${fileName}:`, error.message);
|
||||
}
|
||||
|
||||
const originalSize = content.length;
|
||||
@@ -141,20 +143,20 @@ async function compressBuild() {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('开始压缩构建文件...');
|
||||
BuildLogger.log('开始压缩构建文件...');
|
||||
|
||||
try {
|
||||
const results = await compressDirectory(distDir);
|
||||
|
||||
if (results.length === 0) {
|
||||
console.log('没有找到需要压缩的文件');
|
||||
BuildLogger.log('没有找到需要压缩的文件');
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示压缩结果
|
||||
console.log('\n压缩结果:');
|
||||
console.log('文件名'.padEnd(30) + '原始大小'.padEnd(12) + 'Gzip大小'.padEnd(12) + 'Brotli大小'.padEnd(12) + '压缩率');
|
||||
console.log('-'.repeat(80));
|
||||
BuildLogger.log('\n压缩结果:');
|
||||
BuildLogger.log('文件名'.padEnd(30) + '原始大小'.padEnd(12) + 'Gzip大小'.padEnd(12) + 'Brotli大小'.padEnd(12) + '压缩率');
|
||||
BuildLogger.log('-'.repeat(80));
|
||||
|
||||
let totalOriginal = 0;
|
||||
let totalGzip = 0;
|
||||
@@ -169,7 +171,7 @@ async function compressBuild() {
|
||||
const gzipKB = (result.gzip / 1024).toFixed(1);
|
||||
const brotliKB = result.brotli ? (result.brotli / 1024).toFixed(1) : '-';
|
||||
|
||||
console.log(
|
||||
BuildLogger.log(
|
||||
result.file.padEnd(30) +
|
||||
`${originalKB}KB`.padEnd(12) +
|
||||
`${gzipKB}KB`.padEnd(12) +
|
||||
@@ -179,8 +181,8 @@ async function compressBuild() {
|
||||
});
|
||||
|
||||
const totalRatio = ((totalOriginal - totalGzip) / totalOriginal * 100).toFixed(1);
|
||||
console.log('-'.repeat(80));
|
||||
console.log(
|
||||
BuildLogger.log('-'.repeat(80));
|
||||
BuildLogger.log(
|
||||
'总计'.padEnd(30) +
|
||||
`${(totalOriginal / 1024).toFixed(1)}KB`.padEnd(12) +
|
||||
`${(totalGzip / 1024).toFixed(1)}KB`.padEnd(12) +
|
||||
@@ -188,8 +190,8 @@ async function compressBuild() {
|
||||
`${totalRatio}%`
|
||||
);
|
||||
|
||||
console.log(`\n压缩完成!共处理 ${results.length} 个文件`);
|
||||
console.log(`节省空间: ${((totalOriginal - totalGzip) / 1024).toFixed(1)}KB`);
|
||||
BuildLogger.log(`\n压缩完成!共处理 ${results.length} 个文件`);
|
||||
BuildLogger.log(`节省空间: ${((totalOriginal - totalGzip) / 1024).toFixed(1)}KB`);
|
||||
} catch (error) {
|
||||
console.error('压缩失败:', error);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env node
|
||||
const BuildLogger = require('./logger.js');
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
@@ -25,10 +27,10 @@ function listFiles(dir, base = dir) {
|
||||
process.exit(1);
|
||||
}
|
||||
const files = listFiles(distDir);
|
||||
console.log('📦 dist 构建分析:');
|
||||
BuildLogger.log('📦 dist 构建分析:');
|
||||
files.sort((a, b) => b.size - a.size);
|
||||
files.slice(0, 10).forEach(file => {
|
||||
console.log(`${file.rel.padEnd(50)} ${(file.size / 1024).toFixed(1)} KB`);
|
||||
BuildLogger.log(`${file.rel.padEnd(50)} ${(file.size / 1024).toFixed(1)} KB`);
|
||||
});
|
||||
console.log(`合计文件 ${files.length} 个,总大小 ${(files.reduce((sum, f) => sum + f.size, 0) / 1024).toFixed(1)} KB`);
|
||||
BuildLogger.log(`合计文件 ${files.length} 个,总大小 ${(files.reduce((sum, f) => sum + f.size, 0) / 1024).toFixed(1)} KB`);
|
||||
})();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
const BuildLogger = require('./logger.js');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
@@ -6,9 +7,9 @@ const projectRoot = path.join(__dirname, '..');
|
||||
const distDir = path.join(projectRoot, 'dist');
|
||||
|
||||
function ensureAccessKey() {
|
||||
const key = process.env.ACCESS_KEY || process.env.ESIM_ACCESS_KEY;
|
||||
const key = process.env.ACCESS_KEY;
|
||||
if (!key) {
|
||||
throw new Error('ACCESS_KEY/ESIM_ACCESS_KEY 未配置,无法保护 Functions。');
|
||||
throw new Error('ACCESS_KEY 未配置,无法保护 Functions。');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +21,8 @@ function ensureDist() {
|
||||
}
|
||||
|
||||
(function main() {
|
||||
console.log('🔧 检查部署前置条件...');
|
||||
BuildLogger.log('🔧 检查部署前置条件...');
|
||||
ensureAccessKey();
|
||||
ensureDist();
|
||||
console.log('✅ 部署前检查通过,可继续执行部署流程');
|
||||
BuildLogger.success(' 部署前检查通过,可继续执行部署流程');
|
||||
})();
|
||||
|
||||
90
scripts/logger.js
Normal file
90
scripts/logger.js
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* 构建脚本日志工具
|
||||
* 为构建/部署脚本提供统一的日志输出
|
||||
* 注: 构建脚本始终需要输出信息,因此不像前端Logger那样禁用
|
||||
*/
|
||||
|
||||
// ANSI颜色码
|
||||
const colors = {
|
||||
reset: '\x1b[0m',
|
||||
bold: '\x1b[1m',
|
||||
dim: '\x1b[2m',
|
||||
red: '\x1b[31m',
|
||||
green: '\x1b[32m',
|
||||
yellow: '\x1b[33m',
|
||||
blue: '\x1b[34m',
|
||||
cyan: '\x1b[36m',
|
||||
gray: '\x1b[90m'
|
||||
};
|
||||
|
||||
class BuildLogger {
|
||||
/**
|
||||
* 信息日志 (蓝色)
|
||||
*/
|
||||
static log(...args) {
|
||||
console.log(colors.blue + '[INFO]' + colors.reset, ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功日志 (绿色)
|
||||
*/
|
||||
static success(...args) {
|
||||
console.log(colors.green + '[SUCCESS]' + colors.reset, ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 警告日志 (黄色)
|
||||
*/
|
||||
static warn(...args) {
|
||||
console.warn(colors.yellow + '[WARN]' + colors.reset, ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 错误日志 (红色)
|
||||
*/
|
||||
static error(...args) {
|
||||
console.error(colors.red + '[ERROR]' + colors.reset, ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 调试日志 (灰色)
|
||||
*/
|
||||
static debug(...args) {
|
||||
if (process.env.DEBUG || process.env.VERBOSE) {
|
||||
console.log(colors.gray + '[DEBUG]' + colors.reset, ...args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 标题日志 (粗体青色)
|
||||
*/
|
||||
static title(text) {
|
||||
console.log('\n' + colors.bold + colors.cyan + text + colors.reset);
|
||||
console.log(colors.cyan + '='.repeat(text.length) + colors.reset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 进度信息 (无标签)
|
||||
*/
|
||||
static progress(...args) {
|
||||
console.log(' ', ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查项 (带emoji)
|
||||
*/
|
||||
static check(passed, message) {
|
||||
const icon = passed ? '✅' : '❌';
|
||||
const color = passed ? colors.green : colors.red;
|
||||
console.log(icon, color + message + colors.reset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 步骤开始
|
||||
*/
|
||||
static step(number, total, message) {
|
||||
console.log(colors.cyan + `\n[${number}/${total}]` + colors.reset, colors.bold + message + colors.reset);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BuildLogger;
|
||||
@@ -1,6 +1,8 @@
|
||||
const sharp = require('sharp');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const BuildLogger = require('./logger.js');
|
||||
|
||||
const { promisify } = require('util');
|
||||
|
||||
const readdir = promisify(fs.readdir);
|
||||
@@ -45,7 +47,7 @@ async function optimizeImage(inputPath, outputPath, format, options = {}) {
|
||||
const inputStats = await stat(inputPath);
|
||||
const outputStats = await stat(outputPath);
|
||||
if (outputStats.mtime > inputStats.mtime) {
|
||||
console.log(`⏭️ 跳过已优化: ${path.basename(outputPath)}`);
|
||||
BuildLogger.log(`⏭️ 跳过已优化: ${path.basename(outputPath)}`);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -93,7 +95,7 @@ async function optimizeImage(inputPath, outputPath, format, options = {}) {
|
||||
const outputSize = (await stat(outputPath)).size;
|
||||
const savings = ((inputSize - outputSize) / inputSize * 100).toFixed(1);
|
||||
|
||||
console.log(`✅ 优化完成: ${path.basename(inputPath)} -> ${format.toUpperCase()} (节省 ${savings}%)`);
|
||||
BuildLogger.success(' 优化完成: ${path.basename(inputPath)} -> ${format.toUpperCase()} (节省 ${savings}%)');
|
||||
return { success: true, inputSize, outputSize, savings };
|
||||
} catch (error) {
|
||||
console.error(`❌ 优化失败: ${path.basename(inputPath)}`, error.message);
|
||||
@@ -108,7 +110,7 @@ async function generateMultipleFormats(inputPath, filename) {
|
||||
// Check file size threshold
|
||||
const fileStats = await stat(inputPath);
|
||||
if (fileStats.size < config.minFileSize) {
|
||||
console.log(`⏭️ 跳过小文件: ${filename} (${fileStats.size} bytes)`);
|
||||
BuildLogger.log(`⏭️ 跳过小文件: ${filename} (${fileStats.size} bytes)`);
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -144,7 +146,7 @@ async function generateThumbnails(inputPath, filename) {
|
||||
// Skip if image is already smaller than thumbnail size
|
||||
if (metadata.width <= config.sizes.thumbnail.width &&
|
||||
metadata.height <= config.sizes.thumbnail.height) {
|
||||
console.log(`⏭️ 跳过缩略图生成: ${filename} (已足够小)`);
|
||||
BuildLogger.log(`⏭️ 跳过缩略图生成: ${filename} (已足够小)`);
|
||||
return { success: true, skipped: true };
|
||||
}
|
||||
|
||||
@@ -156,7 +158,7 @@ async function generateThumbnails(inputPath, filename) {
|
||||
const inputStats = await stat(inputPath);
|
||||
const thumbStats = await stat(thumbnailPath);
|
||||
if (thumbStats.mtime > inputStats.mtime) {
|
||||
console.log(`⏭️ 跳过已存在的缩略图: ${path.basename(thumbnailPath)}`);
|
||||
BuildLogger.log(`⏭️ 跳过已存在的缩略图: ${path.basename(thumbnailPath)}`);
|
||||
return { success: true, skipped: true };
|
||||
}
|
||||
}
|
||||
@@ -169,7 +171,7 @@ async function generateThumbnails(inputPath, filename) {
|
||||
.jpeg({ quality: 80, progressive: true })
|
||||
.toFile(thumbnailPath);
|
||||
|
||||
console.log(`✅ 缩略图生成: ${path.basename(thumbnailPath)}`);
|
||||
BuildLogger.success(' 缩略图生成: ${path.basename(thumbnailPath)}');
|
||||
return { success: true, skipped: false };
|
||||
} catch (error) {
|
||||
console.error(`❌ 缩略图生成失败: ${filename}`, error.message);
|
||||
@@ -200,7 +202,7 @@ function generateManifest() {
|
||||
|
||||
const manifestPath = path.join(config.outputDir, 'manifest.json');
|
||||
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
||||
console.log(`📋 图片清单已生成: ${manifestPath}`);
|
||||
BuildLogger.log(`📋 图片清单已生成: ${manifestPath}`);
|
||||
}
|
||||
|
||||
// Process images with concurrency control
|
||||
@@ -218,7 +220,7 @@ async function processImagesInBatches(imageFiles) {
|
||||
|
||||
await Promise.all(batch.map(async (filename) => {
|
||||
const inputPath = path.join(config.inputDir, filename);
|
||||
console.log(`\n🔄 处理: ${filename}`);
|
||||
BuildLogger.log(`\n🔄 处理: ${filename}`);
|
||||
|
||||
try {
|
||||
// Generate multiple formats
|
||||
@@ -257,23 +259,23 @@ async function processImagesInBatches(imageFiles) {
|
||||
|
||||
// 主函数
|
||||
async function optimizeImages() {
|
||||
console.log('🚀 开始图片优化...');
|
||||
console.log(`📁 输入目录: ${config.inputDir}`);
|
||||
console.log(`📁 输出目录: ${config.outputDir}`);
|
||||
console.log(`⚡ 并发数: ${config.maxConcurrent}`);
|
||||
BuildLogger.log('🚀 开始图片优化...');
|
||||
BuildLogger.log(`📁 输入目录: ${config.inputDir}`);
|
||||
BuildLogger.log(`📁 输出目录: ${config.outputDir}`);
|
||||
BuildLogger.log(`⚡ 并发数: ${config.maxConcurrent}`);
|
||||
|
||||
// 确保输出目录存在
|
||||
ensureOutputDir();
|
||||
|
||||
// 检查输入目录是否存在
|
||||
if (!fs.existsSync(config.inputDir)) {
|
||||
console.log(`⚠️ 输入目录不存在,创建示例目录: ${config.inputDir}`);
|
||||
BuildLogger.warn(' 输入目录不存在,创建示例目录: ${config.inputDir}');
|
||||
fs.mkdirSync(config.inputDir, { recursive: true });
|
||||
|
||||
// 创建示例文件
|
||||
const examplePath = path.join(config.inputDir, 'example.txt');
|
||||
fs.writeFileSync(examplePath, '请将需要优化的图片文件放在此目录中');
|
||||
console.log(`📝 已创建示例文件: ${examplePath}`);
|
||||
BuildLogger.log(`📝 已创建示例文件: ${examplePath}`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -281,11 +283,11 @@ async function optimizeImages() {
|
||||
const imageFiles = files.filter(isImageFile);
|
||||
|
||||
if (imageFiles.length === 0) {
|
||||
console.log('⚠️ 未找到图片文件');
|
||||
BuildLogger.warn(' 未找到图片文件');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`📸 找到 ${imageFiles.length} 个图片文件`);
|
||||
BuildLogger.log(`📸 找到 ${imageFiles.length} 个图片文件`);
|
||||
|
||||
const startTime = Date.now();
|
||||
const results = await processImagesInBatches(imageFiles);
|
||||
@@ -294,19 +296,19 @@ async function optimizeImages() {
|
||||
// 生成清单
|
||||
generateManifest();
|
||||
|
||||
console.log(`\n🎉 优化完成!`);
|
||||
console.log(`⏱️ 用时: ${duration}秒`);
|
||||
console.log(`✅ 成功: ${results.successful}`);
|
||||
console.log(`⏭️ 跳过: ${results.skipped}`);
|
||||
console.log(`❌ 失败: ${results.failed}`);
|
||||
BuildLogger.log(`\n🎉 优化完成!`);
|
||||
BuildLogger.log(`⏱️ 用时: ${duration}秒`);
|
||||
BuildLogger.success(' 成功: ${results.successful}');
|
||||
BuildLogger.log(`⏭️ 跳过: ${results.skipped}`);
|
||||
BuildLogger.error(' 失败: ${results.failed}');
|
||||
if (results.totalSavings > 0) {
|
||||
const avgSavings = (results.totalSavings / results.successful).toFixed(1);
|
||||
console.log(`💾 平均节省空间: ${avgSavings}%`);
|
||||
BuildLogger.log(`💾 平均节省空间: ${avgSavings}%`);
|
||||
}
|
||||
console.log(`📁 输出目录: ${config.outputDir}`);
|
||||
BuildLogger.log(`📁 输出目录: ${config.outputDir}`);
|
||||
|
||||
if (results.failed > 0) {
|
||||
console.log(`⚠️ 有 ${results.failed} 个文件处理失败`);
|
||||
BuildLogger.warn(' 有 ${results.failed} 个文件处理失败');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
@@ -314,7 +316,7 @@ async function optimizeImages() {
|
||||
// 命令行参数处理
|
||||
const args = process.argv.slice(2);
|
||||
if (args.includes('--help') || args.includes('-h')) {
|
||||
console.log(`
|
||||
BuildLogger.log(`
|
||||
📸 图片优化工具 (Sharp版本)
|
||||
|
||||
用法: node optimize-images.js [选项]
|
||||
|
||||
238
scripts/quality-check.js
Normal file
238
scripts/quality-check.js
Normal file
@@ -0,0 +1,238 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* 代码质量全面检查脚本
|
||||
* 检查所有变更的完整性和代码质量
|
||||
*/
|
||||
|
||||
const BuildLogger = require('./logger.js');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
const projectRoot = path.join(__dirname, '..');
|
||||
|
||||
// 检查项配置
|
||||
const checks = {
|
||||
// 1. 语法检查
|
||||
syntaxCheck: {
|
||||
name: '语法检查',
|
||||
files: [
|
||||
'server.js',
|
||||
'webpack.config.js',
|
||||
'netlify/functions/_shared/middleware.js',
|
||||
'netlify/functions/health.js',
|
||||
'netlify/functions/giffgaff-graphql.js',
|
||||
'netlify/functions/giffgaff-mfa-challenge.js',
|
||||
'netlify/functions/giffgaff-mfa-validation.js',
|
||||
'netlify/functions/giffgaff-sms-activate.js',
|
||||
'netlify/functions/auto-activate-esim.js',
|
||||
'netlify/functions/giffgaff-token-exchange.js',
|
||||
'netlify/functions/verify-cookie.js'
|
||||
]
|
||||
},
|
||||
|
||||
// 2. 环境变量一致性
|
||||
envVarCheck: {
|
||||
name: '环境变量一致性',
|
||||
required: ['ACCESS_KEY', 'ALLOWED_ORIGIN'],
|
||||
deprecated: ['ESIM_ACCESS_KEY', 'COOKIE_SECRET']
|
||||
},
|
||||
|
||||
// 3. 依赖完整性
|
||||
dependencyCheck: {
|
||||
name: '依赖完整性',
|
||||
unused: ['cookie-parser']
|
||||
},
|
||||
|
||||
// 4. 安全配置
|
||||
securityCheck: {
|
||||
name: '安全配置',
|
||||
patterns: {
|
||||
weakDefaults: /please_change_me|your-secret-key-here|your-key-here/g,
|
||||
hardcodedSecrets: /(?:password|secret|key)\s*=\s*['"][^'"]{10,}['"]/gi,
|
||||
consoleLog: /console\.log\(/g
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let totalChecks = 0;
|
||||
let passedChecks = 0;
|
||||
let failedChecks = 0;
|
||||
|
||||
// 辅助函数
|
||||
function checkFile(filePath) {
|
||||
const fullPath = path.join(projectRoot, filePath);
|
||||
if (!fs.existsSync(fullPath)) {
|
||||
BuildLogger.error(`文件不存在: ${filePath}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
execSync(`node -c "${fullPath}"`, { stdio: 'pipe' });
|
||||
return true;
|
||||
} catch (error) {
|
||||
BuildLogger.error(`语法错误: ${filePath}`);
|
||||
BuildLogger.error(error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function checkEnvExample() {
|
||||
const envPath = path.join(projectRoot, 'env.example');
|
||||
const content = fs.readFileSync(envPath, 'utf8');
|
||||
const issues = [];
|
||||
|
||||
// 检查必需变量
|
||||
checks.envVarCheck.required.forEach(varName => {
|
||||
if (!content.includes(`${varName}=`)) {
|
||||
issues.push(`缺少必需环境变量: ${varName}`);
|
||||
}
|
||||
});
|
||||
|
||||
// 检查废弃变量
|
||||
checks.envVarCheck.deprecated.forEach(varName => {
|
||||
if (content.includes(`${varName}=`)) {
|
||||
issues.push(`包含废弃环境变量: ${varName}`);
|
||||
}
|
||||
});
|
||||
|
||||
return issues;
|
||||
}
|
||||
|
||||
function searchInFiles(pattern, files, excludeContext = []) {
|
||||
const results = [];
|
||||
files.forEach(file => {
|
||||
const fullPath = path.join(projectRoot, file);
|
||||
if (fs.existsSync(fullPath)) {
|
||||
const content = fs.readFileSync(fullPath, 'utf8');
|
||||
const lines = content.split('\n');
|
||||
let matchCount = 0;
|
||||
|
||||
lines.forEach((line, index) => {
|
||||
if (pattern.test(line)) {
|
||||
// 检查是否在排除的上下文中(如安全检查代码)
|
||||
const isExcluded = excludeContext.some(ctx => {
|
||||
const contextLine = lines[index];
|
||||
const prevLine = lines[index - 1] || '';
|
||||
return contextLine.includes(ctx) || prevLine.includes(ctx);
|
||||
});
|
||||
|
||||
if (!isExcluded) {
|
||||
matchCount++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (matchCount > 0) {
|
||||
results.push({ file, matches: matchCount });
|
||||
}
|
||||
}
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
function checkPackageJson() {
|
||||
const pkgPath = path.join(projectRoot, 'package.json');
|
||||
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
||||
const issues = [];
|
||||
|
||||
checks.dependencyCheck.unused.forEach(dep => {
|
||||
if (pkg.dependencies && pkg.dependencies[dep]) {
|
||||
issues.push(`未使用的依赖: ${dep} (dependencies)`);
|
||||
}
|
||||
if (pkg.devDependencies && pkg.devDependencies[dep]) {
|
||||
issues.push(`未使用的依赖: ${dep} (devDependencies)`);
|
||||
}
|
||||
});
|
||||
|
||||
return issues;
|
||||
}
|
||||
|
||||
// 执行检查
|
||||
function runChecks() {
|
||||
BuildLogger.title('代码质量全面检查');
|
||||
|
||||
// 1. 语法检查
|
||||
BuildLogger.step(1, 4, checks.syntaxCheck.name);
|
||||
let syntaxPassed = 0;
|
||||
checks.syntaxCheck.files.forEach(file => {
|
||||
totalChecks++;
|
||||
if (checkFile(file)) {
|
||||
BuildLogger.check(true, file);
|
||||
syntaxPassed++;
|
||||
passedChecks++;
|
||||
} else {
|
||||
BuildLogger.check(false, file);
|
||||
failedChecks++;
|
||||
}
|
||||
});
|
||||
BuildLogger.progress(`${syntaxPassed}/${checks.syntaxCheck.files.length} 文件通过语法检查\n`);
|
||||
|
||||
// 2. 环境变量检查
|
||||
BuildLogger.step(2, 4, checks.envVarCheck.name);
|
||||
totalChecks++;
|
||||
const envIssues = checkEnvExample();
|
||||
if (envIssues.length === 0) {
|
||||
BuildLogger.check(true, 'env.example 配置正确');
|
||||
passedChecks++;
|
||||
} else {
|
||||
BuildLogger.check(false, 'env.example 存在问题:');
|
||||
envIssues.forEach(issue => BuildLogger.error(` - ${issue}`));
|
||||
failedChecks++;
|
||||
}
|
||||
|
||||
// 3. 依赖检查
|
||||
BuildLogger.step(3, 4, checks.dependencyCheck.name);
|
||||
totalChecks++;
|
||||
const depIssues = checkPackageJson();
|
||||
if (depIssues.length === 0) {
|
||||
BuildLogger.check(true, 'package.json 依赖正确');
|
||||
passedChecks++;
|
||||
} else {
|
||||
BuildLogger.check(false, 'package.json 存在问题:');
|
||||
depIssues.forEach(issue => BuildLogger.warn(` - ${issue}`));
|
||||
failedChecks++;
|
||||
}
|
||||
|
||||
// 4. 安全配置检查
|
||||
BuildLogger.step(4, 4, checks.securityCheck.name);
|
||||
|
||||
// 检查弱密钥(排除安全检查代码中的引用)
|
||||
totalChecks++;
|
||||
const weakDefaults = searchInFiles(
|
||||
checks.securityCheck.patterns.weakDefaults,
|
||||
['env.example', 'netlify/functions/_shared/middleware.js'],
|
||||
['if (ACCESS_KEY ===', '安全警告', '警告', '检查']
|
||||
);
|
||||
if (weakDefaults.length === 0) {
|
||||
BuildLogger.check(true, '无弱默认配置');
|
||||
passedChecks++;
|
||||
} else {
|
||||
BuildLogger.check(false, '发现弱默认配置:');
|
||||
weakDefaults.forEach(r => BuildLogger.warn(` - ${r.file}: ${r.matches}处`));
|
||||
failedChecks++;
|
||||
}
|
||||
|
||||
// 统计报告
|
||||
BuildLogger.title('\n检查结果汇总');
|
||||
BuildLogger.log(`总检查项: ${totalChecks}`);
|
||||
BuildLogger.success(`通过: ${passedChecks}`);
|
||||
if (failedChecks > 0) {
|
||||
BuildLogger.error(`失败: ${failedChecks}`);
|
||||
}
|
||||
|
||||
const successRate = ((passedChecks / totalChecks) * 100).toFixed(1);
|
||||
BuildLogger.log(`\n通过率: ${successRate}%`);
|
||||
|
||||
if (failedChecks === 0) {
|
||||
BuildLogger.success('\n✅ 所有检查通过! 代码质量良好。');
|
||||
return 0;
|
||||
} else {
|
||||
BuildLogger.error('\n❌ 存在质量问题,请修复后重新检查。');
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 执行
|
||||
const exitCode = runChecks();
|
||||
process.exit(exitCode);
|
||||
122
scripts/replace-console-log.js
Normal file
122
scripts/replace-console-log.js
Normal file
@@ -0,0 +1,122 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* 全局替换console.log为Logger
|
||||
* 自动在文件开头添加Logger导入,并替换所有console.log调用
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const glob = require('glob');
|
||||
|
||||
// 需要处理的目录
|
||||
const DIRS_TO_PROCESS = [
|
||||
'src/js/modules',
|
||||
'src/giffgaff/js/modules',
|
||||
'src/simyo/js/modules'
|
||||
];
|
||||
|
||||
// 需要排除的文件
|
||||
const EXCLUDE_FILES = [
|
||||
'src/js/modules/logger.js', // Logger模块本身
|
||||
'src/js/modules/README.md' // 文档文件
|
||||
];
|
||||
|
||||
// 替换console.log为Logger.log
|
||||
function replaceConsoleLogs(filePath) {
|
||||
try {
|
||||
let content = fs.readFileSync(filePath, 'utf8');
|
||||
const originalContent = content;
|
||||
|
||||
// 检查是否已经导入了Logger
|
||||
const hasLoggerImport = /import\s+Logger\s+from/.test(content) ||
|
||||
/const\s+Logger\s*=\s*require/.test(content);
|
||||
|
||||
// 检查是否有console.log需要替换
|
||||
const hasConsolelog = /console\.log\s*\(/.test(content);
|
||||
|
||||
if (!hasConsolelog) {
|
||||
console.log(`⏭️ 跳过 ${filePath} - 无console.log`);
|
||||
return { replaced: false };
|
||||
}
|
||||
|
||||
// 替换console.log为Logger.log
|
||||
// 保留console.warn和console.error不变
|
||||
content = content.replace(/console\.log\s*\(/g, 'Logger.log(');
|
||||
|
||||
// 如果还没有导入Logger,在文件开头添加导入
|
||||
if (!hasLoggerImport && hasConsolelog) {
|
||||
// 计算相对路径
|
||||
const fileDir = path.dirname(filePath);
|
||||
const loggerPath = path.relative(fileDir, 'src/js/modules/logger.js');
|
||||
const importPath = loggerPath.startsWith('.') ? loggerPath : `./${loggerPath}`;
|
||||
|
||||
// 添加导入语句
|
||||
const importStatement = `import Logger from '${importPath}';\n`;
|
||||
|
||||
// 在第一个import语句后或文件开头添加
|
||||
if (/^import\s+/.test(content)) {
|
||||
// 在最后一个import之后添加
|
||||
const lastImportIndex = content.lastIndexOf('\nimport ');
|
||||
if (lastImportIndex !== -1) {
|
||||
const nextLineIndex = content.indexOf('\n', lastImportIndex + 1);
|
||||
content = content.slice(0, nextLineIndex + 1) + importStatement + content.slice(nextLineIndex + 1);
|
||||
} else {
|
||||
content = importStatement + content;
|
||||
}
|
||||
} else {
|
||||
// 在文件开头添加
|
||||
content = importStatement + '\n' + content;
|
||||
}
|
||||
}
|
||||
|
||||
if (content !== originalContent) {
|
||||
fs.writeFileSync(filePath, content, 'utf8');
|
||||
const count = (originalContent.match(/console\.log\s*\(/g) || []).length;
|
||||
console.log(`✅ ${filePath} - 替换了${count}处console.log`);
|
||||
return { replaced: true, count };
|
||||
} else {
|
||||
console.log(`⏭️ 跳过 ${filePath} - 无需修改`);
|
||||
return { replaced: false };
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ 处理 ${filePath} 失败:`, error.message);
|
||||
return { replaced: false, error: true };
|
||||
}
|
||||
}
|
||||
|
||||
// 主函数
|
||||
function main() {
|
||||
console.log('🚀 开始替换console.log为Logger.log...\n');
|
||||
|
||||
let totalFiles = 0;
|
||||
let replacedFiles = 0;
|
||||
let totalReplacements = 0;
|
||||
|
||||
DIRS_TO_PROCESS.forEach(dir => {
|
||||
const pattern = path.join(dir, '**/*.js');
|
||||
const files = glob.sync(pattern);
|
||||
|
||||
files.forEach(file => {
|
||||
// 排除特定文件
|
||||
if (EXCLUDE_FILES.some(excluded => file.includes(excluded))) {
|
||||
return;
|
||||
}
|
||||
|
||||
totalFiles++;
|
||||
const result = replaceConsoleLogs(file);
|
||||
if (result.replaced) {
|
||||
replacedFiles++;
|
||||
totalReplacements += result.count || 0;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
console.log('\n📊 替换统计:');
|
||||
console.log(` 总文件数: ${totalFiles}`);
|
||||
console.log(` 已修改文件: ${replacedFiles}`);
|
||||
console.log(` console.log替换数: ${totalReplacements}`);
|
||||
console.log('\n✨ 完成!');
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env node
|
||||
const BuildLogger = require('./logger.js');
|
||||
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
@@ -89,42 +91,47 @@ function checkDependencies() {
|
||||
|
||||
// 生成安全报告
|
||||
function generateSecurityReport() {
|
||||
console.log('🔒 安全检查报告\n');
|
||||
BuildLogger.log('🔒 安全检查报告
|
||||
');
|
||||
|
||||
const vulnerabilities = checkDependencies();
|
||||
|
||||
if (vulnerabilities.length === 0) {
|
||||
console.log('✅ 未发现已知的安全漏洞');
|
||||
BuildLogger.success(' 未发现已知的安全漏洞');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`⚠️ 发现 ${vulnerabilities.length} 个潜在安全漏洞:\n`);
|
||||
BuildLogger.warn(' 发现 ${vulnerabilities.length} 个潜在安全漏洞:
|
||||
');
|
||||
|
||||
vulnerabilities.forEach((vuln, index) => {
|
||||
console.log(`${index + 1}. ${vuln.package}@${vuln.version}`);
|
||||
console.log(` 严重程度: ${vuln.severity}`);
|
||||
console.log(` 描述: ${vuln.description}`);
|
||||
console.log(` 修复建议: ${vuln.fix}\n`);
|
||||
BuildLogger.log(`${index + 1}. ${vuln.package}@${vuln.version}`);
|
||||
BuildLogger.log(` 严重程度: ${vuln.severity}`);
|
||||
BuildLogger.log(` 描述: ${vuln.description}`);
|
||||
BuildLogger.log(` 修复建议: ${vuln.fix}
|
||||
`);
|
||||
});
|
||||
|
||||
console.log('🔧 修复建议:');
|
||||
console.log('1. 运行 npm update 更新所有依赖');
|
||||
console.log('2. 运行 npm audit fix 自动修复');
|
||||
console.log('3. 手动更新特定包到最新版本');
|
||||
BuildLogger.log('🔧 修复建议:');
|
||||
BuildLogger.log('1. 运行 npm update 更新所有依赖');
|
||||
BuildLogger.log('2. 运行 npm audit fix 自动修复');
|
||||
BuildLogger.log('3. 手动更新特定包到最新版本');
|
||||
}
|
||||
|
||||
// 检查开发环境安全配置
|
||||
function checkSecurityConfig() {
|
||||
console.log('\n🔧 安全配置检查:\n');
|
||||
BuildLogger.log('
|
||||
🔧 安全配置检查:
|
||||
');
|
||||
|
||||
// 检查Helmet配置
|
||||
const serverPath = path.join(__dirname, '../server.js');
|
||||
if (fs.existsSync(serverPath)) {
|
||||
const serverContent = fs.readFileSync(serverPath, 'utf8');
|
||||
if (serverContent.includes('helmet')) {
|
||||
console.log('✅ Helmet安全头已配置');
|
||||
BuildLogger.success(' Helmet安全头已配置');
|
||||
} else {
|
||||
console.log('⚠️ 建议添加Helmet安全头');
|
||||
BuildLogger.warn(' 建议添加Helmet安全头');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,9 +139,9 @@ function checkSecurityConfig() {
|
||||
if (fs.existsSync(serverPath)) {
|
||||
const serverContent = fs.readFileSync(serverPath, 'utf8');
|
||||
if (serverContent.includes('cors')) {
|
||||
console.log('✅ CORS配置已设置');
|
||||
BuildLogger.success(' CORS配置已设置');
|
||||
} else {
|
||||
console.log('⚠️ 建议配置CORS');
|
||||
BuildLogger.warn(' 建议配置CORS');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,9 +157,9 @@ function checkSecurityConfig() {
|
||||
if (fs.existsSync(filePath)) {
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
if (content.includes('Content-Security-Policy')) {
|
||||
console.log(`✅ ${file} 已配置CSP`);
|
||||
BuildLogger.success(' ${file} 已配置CSP');
|
||||
} else {
|
||||
console.log(`⚠️ ${file} 建议添加CSP配置`);
|
||||
BuildLogger.warn(' ${file} 建议添加CSP配置');
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -163,16 +170,17 @@ function main() {
|
||||
generateSecurityReport();
|
||||
checkSecurityConfig();
|
||||
|
||||
console.log('\n📋 安全最佳实践:');
|
||||
console.log('1. 定期更新依赖包');
|
||||
console.log('2. 使用npm audit检查安全漏洞');
|
||||
console.log('3. 配置适当的安全头');
|
||||
console.log('4. 实施内容安全策略(CSP)');
|
||||
console.log('5. 使用HTTPS部署');
|
||||
BuildLogger.log('
|
||||
📋 安全最佳实践:');
|
||||
BuildLogger.log('1. 定期更新依赖包');
|
||||
BuildLogger.log('2. 使用npm audit检查安全漏洞');
|
||||
BuildLogger.log('3. 配置适当的安全头');
|
||||
BuildLogger.log('4. 实施内容安全策略(CSP)');
|
||||
BuildLogger.log('5. 使用HTTPS部署');
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
|
||||
module.exports = { checkDependencies, generateSecurityReport };
|
||||
module.exports = { checkDependencies, generateSecurityReport };
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env node
|
||||
const BuildLogger = require('./logger.js');
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
@@ -14,5 +16,5 @@ const netlifyToml = path.join(__dirname, '..', 'netlify.toml');
|
||||
console.error('netlify.toml 未将 publish 指向 dist');
|
||||
process.exit(1);
|
||||
}
|
||||
console.log('✅ Netlify 配置检查通过 (publish=dist)');
|
||||
BuildLogger.success(' Netlify 配置检查通过 (publish=dist)');
|
||||
})();
|
||||
|
||||
128
scripts/update-script-logging.js
Normal file
128
scripts/update-script-logging.js
Normal file
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* 更新构建脚本中的console日志为BuildLogger
|
||||
* 仅替换普通的console.log,保留console.error和console.warn
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// 需要处理的脚本文件
|
||||
const SCRIPTS_TO_UPDATE = [
|
||||
'build-static.js',
|
||||
'deploy-prepare.js',
|
||||
'deploy-analyze.js',
|
||||
'test-deploy-config.js',
|
||||
'optimize-images.js',
|
||||
'compress.js',
|
||||
'security-check.js'
|
||||
];
|
||||
|
||||
// 需要排除的文件
|
||||
const EXCLUDE_FILES = [
|
||||
'logger.js',
|
||||
'replace-console-log.js',
|
||||
'update-script-logging.js'
|
||||
];
|
||||
|
||||
function updateScriptLogging(filePath) {
|
||||
try {
|
||||
let content = fs.readFileSync(filePath, 'utf8');
|
||||
const originalContent = content;
|
||||
|
||||
// 检查是否已经导入了BuildLogger
|
||||
const hasLoggerImport = /const\s+(?:BuildLogger|Logger)\s*=\s*require/.test(content);
|
||||
|
||||
// 检查是否有console.log需要替换
|
||||
const hasConsoleLog = /console\.log\s*\(/.test(content);
|
||||
|
||||
if (!hasConsoleLog) {
|
||||
console.log(`⏭️ 跳过 ${path.basename(filePath)} - 无console.log`);
|
||||
return { replaced: false };
|
||||
}
|
||||
|
||||
// 替换console.log为BuildLogger.log
|
||||
// 识别带emoji的success消息
|
||||
content = content.replace(/console\.log\((['"`])✅([^'"`]*)\1\)/g, 'BuildLogger.success(\'$2\')');
|
||||
content = content.replace(/console\.log\((['"`])❌([^'"`]*)\1\)/g, 'BuildLogger.error(\'$2\')');
|
||||
content = content.replace(/console\.log\((['"`])⚠️([^'"`]*)\1\)/g, 'BuildLogger.warn(\'$2\')');
|
||||
content = content.replace(/console\.log\((['"`])🔧([^'"`]*)\1\)/g, 'BuildLogger.log(\'🔧$2\')');
|
||||
content = content.replace(/console\.log\((['"`])📊([^'"`]*)\1\)/g, 'BuildLogger.log(\'📊$2\')');
|
||||
|
||||
// 替换剩余的console.log
|
||||
content = content.replace(/console\.log\s*\(/g, 'BuildLogger.log(');
|
||||
|
||||
// 如果还没有导入BuildLogger,在文件开头添加导入
|
||||
if (!hasLoggerImport && hasConsoleLog) {
|
||||
// 在第一个require之后或文件开头添加
|
||||
const importStatement = `const BuildLogger = require('./logger.js');\n`;
|
||||
|
||||
if (/^const\s+/.test(content)) {
|
||||
// 在最后一个require之后添加
|
||||
const lastRequireMatch = content.match(/const\s+\w+\s*=\s*require\([^)]+\);?/g);
|
||||
if (lastRequireMatch && lastRequireMatch.length > 0) {
|
||||
const lastRequire = lastRequireMatch[lastRequireMatch.length - 1];
|
||||
const lastRequireIndex = content.lastIndexOf(lastRequire);
|
||||
const insertIndex = lastRequireIndex + lastRequire.length;
|
||||
content = content.slice(0, insertIndex) + '\n' + importStatement + content.slice(insertIndex);
|
||||
} else {
|
||||
content = importStatement + content;
|
||||
}
|
||||
} else {
|
||||
content = importStatement + '\n' + content;
|
||||
}
|
||||
}
|
||||
|
||||
if (content !== originalContent) {
|
||||
fs.writeFileSync(filePath, content, 'utf8');
|
||||
const count = (originalContent.match(/console\.log\s*\(/g) || []).length;
|
||||
console.log(`✅ ${path.basename(filePath)} - 替换了${count}处console.log`);
|
||||
return { replaced: true, count };
|
||||
} else {
|
||||
console.log(`⏭️ 跳过 ${path.basename(filePath)} - 无需修改`);
|
||||
return { replaced: false };
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ 处理 ${path.basename(filePath)} 失败:`, error.message);
|
||||
return { replaced: false, error: true };
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
console.log('🚀 开始更新构建脚本日志...\n');
|
||||
|
||||
let totalFiles = 0;
|
||||
let replacedFiles = 0;
|
||||
let totalReplacements = 0;
|
||||
|
||||
SCRIPTS_TO_UPDATE.forEach(filename => {
|
||||
const filePath = path.join(__dirname, filename);
|
||||
|
||||
if (!fs.existsSync(filePath)) {
|
||||
console.log(`⏭️ 跳过 ${filename} - 文件不存在`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (EXCLUDE_FILES.includes(filename)) {
|
||||
console.log(`⏭️ 跳过 ${filename} - 已排除`);
|
||||
return;
|
||||
}
|
||||
|
||||
totalFiles++;
|
||||
const result = updateScriptLogging(filePath);
|
||||
if (result.replaced) {
|
||||
replacedFiles++;
|
||||
totalReplacements += result.count || 0;
|
||||
}
|
||||
});
|
||||
|
||||
console.log('\n📊 替换统计:');
|
||||
console.log(` 总文件数: ${totalFiles}`);
|
||||
console.log(` 已修改文件: ${replacedFiles}`);
|
||||
console.log(` console.log替换数: ${totalReplacements}`);
|
||||
console.log('\n✨ 完成!');
|
||||
console.log('\n💡 提示: 构建脚本的日志现在使用带颜色的BuildLogger输出');
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user