165 lines
4.3 KiB
JavaScript
165 lines
4.3 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* 混淆构建脚本
|
||
* 在 Next.js 构建后对敏感代码进行混淆
|
||
*
|
||
* 使用方法:
|
||
* 1. npm run build
|
||
* 2. node scripts/build-obfuscated.js
|
||
*
|
||
* 或者使用组合命令:
|
||
* npm run build:prod
|
||
*/
|
||
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
const { glob } = require('glob');
|
||
|
||
// 检查是否安装了 javascript-obfuscator
|
||
let JavaScriptObfuscator;
|
||
try {
|
||
JavaScriptObfuscator = require('javascript-obfuscator');
|
||
} catch {
|
||
console.error('❌ javascript-obfuscator 未安装,生产混淆不能跳过');
|
||
console.error(' 请先运行: npm install');
|
||
process.exit(1);
|
||
}
|
||
|
||
const { obfuscatorOptions } = require('./obfuscate-config');
|
||
|
||
// 构建输出目录
|
||
const BUILD_DIR = path.join(process.cwd(), '.next');
|
||
const STANDALONE_DIR = path.join(BUILD_DIR, 'standalone');
|
||
|
||
// 需要混淆的文件模式(在构建输出中)
|
||
const PATTERNS = [
|
||
// 服务端 chunks(包含许可证相关代码)
|
||
'server/chunks/*.js',
|
||
// API 路由
|
||
'server/app/api/license/**/*.js',
|
||
// standalone 目录
|
||
'standalone/.next/server/chunks/*.js',
|
||
'standalone/.next/server/app/api/license/**/*.js',
|
||
];
|
||
|
||
// 排除的文件
|
||
const EXCLUDE = [
|
||
'**/node_modules/**',
|
||
'**/*.map',
|
||
];
|
||
|
||
async function main() {
|
||
console.log('🔒 开始代码混淆...\n');
|
||
|
||
// 检查构建目录是否存在
|
||
if (!fs.existsSync(BUILD_DIR)) {
|
||
console.error('❌ 构建目录不存在,请先运行 npm run build');
|
||
process.exit(1);
|
||
}
|
||
|
||
let totalFiles = 0;
|
||
let targetFiles = 0;
|
||
let obfuscatedFiles = 0;
|
||
const failedFiles = [];
|
||
|
||
// 查找需要混淆的文件
|
||
for (const pattern of PATTERNS) {
|
||
const files = await glob(pattern, {
|
||
cwd: BUILD_DIR,
|
||
ignore: EXCLUDE,
|
||
absolute: false,
|
||
});
|
||
|
||
for (const file of files) {
|
||
const filePath = path.join(BUILD_DIR, file);
|
||
|
||
// 跳过不存在的文件
|
||
if (!fs.existsSync(filePath)) continue;
|
||
|
||
totalFiles++;
|
||
|
||
try {
|
||
const code = fs.readFileSync(filePath, 'utf-8');
|
||
|
||
// 跳过太小的文件(可能是空文件或只有导出)
|
||
if (code.length < 100) continue;
|
||
targetFiles++;
|
||
|
||
console.log(` 混淆: ${file}`);
|
||
|
||
const result = JavaScriptObfuscator.obfuscate(code, {
|
||
...obfuscatorOptions,
|
||
sourceMap: false, // 生产环境不生成 source map
|
||
});
|
||
|
||
fs.writeFileSync(filePath, result.getObfuscatedCode());
|
||
obfuscatedFiles++;
|
||
} catch (error) {
|
||
failedFiles.push(`${file}: ${error.message}`);
|
||
console.warn(` ⚠️ 混淆失败 ${file}: ${error.message}`);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 同样处理 standalone 目录(如果存在)
|
||
if (fs.existsSync(STANDALONE_DIR)) {
|
||
for (const pattern of PATTERNS) {
|
||
const files = await glob(pattern, {
|
||
cwd: STANDALONE_DIR,
|
||
ignore: EXCLUDE,
|
||
absolute: false,
|
||
});
|
||
|
||
for (const file of files) {
|
||
const filePath = path.join(STANDALONE_DIR, file);
|
||
|
||
if (!fs.existsSync(filePath)) continue;
|
||
|
||
totalFiles++;
|
||
|
||
try {
|
||
const code = fs.readFileSync(filePath, 'utf-8');
|
||
if (code.length < 100) continue;
|
||
targetFiles++;
|
||
|
||
console.log(` 混淆 (standalone): ${file}`);
|
||
|
||
const result = JavaScriptObfuscator.obfuscate(code, {
|
||
...obfuscatorOptions,
|
||
sourceMap: false,
|
||
});
|
||
|
||
fs.writeFileSync(filePath, result.getObfuscatedCode());
|
||
obfuscatedFiles++;
|
||
} catch (error) {
|
||
failedFiles.push(`${file}: ${error.message}`);
|
||
console.warn(` ⚠️ 混淆失败 ${file}: ${error.message}`);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (targetFiles === 0) {
|
||
console.error('\n❌ 没有找到需要混淆的目标文件,生产镜像不能继续出包');
|
||
process.exit(1);
|
||
}
|
||
|
||
if (failedFiles.length > 0) {
|
||
console.error('\n❌ 混淆失败的目标文件:');
|
||
for (const failedFile of failedFiles) {
|
||
console.error(` - ${failedFile}`);
|
||
}
|
||
process.exit(1);
|
||
}
|
||
|
||
console.log(`\n✅ 混淆完成!`);
|
||
console.log(` 扫描文件: ${totalFiles}`);
|
||
console.log(` 目标文件: ${targetFiles}`);
|
||
console.log(` 混淆文件: ${obfuscatedFiles}`);
|
||
}
|
||
|
||
main().catch((error) => {
|
||
console.error('❌ 混淆失败:', error);
|
||
process.exit(1);
|
||
});
|