Initial TrueGrowth source import

This commit is contained in:
2026-07-07 09:36:36 +08:00
commit 3b6781d695
2283 changed files with 691996 additions and 0 deletions

View File

@@ -0,0 +1,321 @@
#!/usr/bin/env node
/**
* CDN 静态资源发布脚本
*
* 只发布静态资源到 npm不包含 HTML 文件
*
* 安全特性:
* - 不上传 HTML 文件(避免用户配置泄露)
* - 不上传 source maps
* - 不上传敏感配置文件
*
* 用法:
* node scripts/publish-cdn-assets.js [--dry-run] [--skip-build] [--otp=123456]
*/
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
// 配置
const CONFIG = {
packageName: 'aitu-app',
distDir: path.resolve(__dirname, '../dist/apps/web'),
cdnDir: path.resolve(__dirname, '../dist/deploy/cdn'),
rootPackageJson: path.resolve(__dirname, '../package.json'),
// 不上传到 CDN 的文件
excludePatterns: [
/\.html$/, // HTML 文件
/\.map$/, // Source maps
/^init\.json$/, // 初始化配置
/^stats\.html$/, // 构建分析
/deploy-info\.json/, // 部署信息
],
};
// 解析命令行参数
const args = process.argv.slice(2);
const isDryRun = args.includes('--dry-run');
const skipBuild = args.includes('--skip-build');
const otpArg = args.find(arg => arg.startsWith('--otp='));
const otp = otpArg ? otpArg.split('=')[1] : null;
// 颜色输出
const colors = {
reset: '\x1b[0m',
green: '\x1b[32m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
red: '\x1b[31m',
cyan: '\x1b[36m',
};
function log(message, color = 'reset') {
console.log(`${colors[color]}${message}${colors.reset}`);
}
function logStep(step, message) {
log(`\n[${step}] ${message}`, 'blue');
}
function logSuccess(message) {
log(`${message}`, 'green');
}
function logError(message) {
log(`${message}`, 'red');
}
// 执行命令
function exec(command, options = {}) {
log(` 执行: ${command}`, 'yellow');
try {
execSync(command, { stdio: 'inherit', ...options });
return true;
} catch (error) {
logError(`命令执行失败: ${command}`);
return false;
}
}
// 读取版本号
function getVersion() {
const pkg = JSON.parse(fs.readFileSync(CONFIG.rootPackageJson, 'utf-8'));
return pkg.version;
}
// 确保目录存在
function ensureDir(dir) {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
}
// 检查是否应该排除
function shouldExclude(filename) {
return CONFIG.excludePatterns.some(pattern => {
if (pattern instanceof RegExp) {
return pattern.test(filename);
}
return filename === pattern;
});
}
// 复制文件(排除敏感文件)
function copyFilteredFiles(src, dest) {
ensureDir(dest);
let copied = 0;
let excluded = 0;
function processDir(srcDir, destDir, relativePath = '') {
const entries = fs.readdirSync(srcDir, { withFileTypes: true });
for (const entry of entries) {
const srcPath = path.join(srcDir, entry.name);
const destPath = path.join(destDir, entry.name);
const relPath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
if (entry.isDirectory()) {
ensureDir(destPath);
processDir(srcPath, destPath, relPath);
} else {
if (shouldExclude(entry.name)) {
excluded++;
// log(` 排除: ${relPath}`);
} else {
fs.copyFileSync(srcPath, destPath);
copied++;
}
}
}
}
processDir(src, dest);
return { copied, excluded };
}
// 生成 npm package.json
function generateNpmPackageJson(version) {
return {
name: CONFIG.packageName,
version: version,
description: 'Opentu static assets for CDN deployment (HTML files not included for security)',
keywords: [
'aitu',
'whiteboard',
'assets',
'cdn',
],
homepage: 'https://opentu.ai',
repository: {
type: 'git',
url: 'https://github.com/ljquan/aitu.git'
},
license: 'MIT',
author: 'ljquan',
files: ['**/*'],
publishConfig: {
access: 'public'
},
// 标记这是纯静态资源包
aituAssets: {
type: 'cdn-assets',
htmlIncluded: false,
note: 'This package contains only static assets (JS, CSS, images, fonts). HTML files are served from your own server for security.'
}
};
}
// 生成 README
function generateReadme(version) {
return `# Opentu CDN Assets
> ⚠️ **安全说明**: 此包仅包含静态资源JS、CSS、图片、字体不包含 HTML 文件。HTML 文件应从你自己的服务器提供。
## 版本
当前版本: **${version}**
## CDN 访问
### unpkg
\`\`\`
https://unpkg.com/${CONFIG.packageName}@${version}/assets/index-xxx.js
https://unpkg.com/${CONFIG.packageName}@${version}/assets/index-xxx.css
\`\`\`
### jsDelivr
\`\`\`
https://cdn.jsdelivr.net/npm/${CONFIG.packageName}@${version}/assets/index-xxx.js
https://cdn.jsdelivr.net/npm/${CONFIG.packageName}@${version}/assets/index-xxx.css
\`\`\`
## 使用方式
1. 从你的服务器提供 HTML 文件
2. 修改 HTML 中的资源路径指向 CDN
3. 用户访问你的域名,静态资源从 CDN 加载
## 包含的文件
- \`/assets/\` - JS 和 CSS 文件
- \`/icons/\` - 图标文件
- \`/manifest.json\` - PWA 配置
## 不包含的文件
- \`*.html\` - HTML 文件(安全考虑)
- \`*.map\` - Source maps
## 许可证
MIT License
`;
}
// 主流程
async function main() {
log('\n🚀 CDN 静态资源发布脚本\n', 'cyan');
if (isDryRun) {
log('⚠️ DRY RUN 模式 - 不会实际发布\n', 'yellow');
}
const version = getVersion();
log(`📦 版本: ${version}`, 'cyan');
// 步骤 1: 构建
if (!skipBuild) {
logStep('1/4', '构建项目');
if (!exec('npm run build:web', { cwd: path.resolve(__dirname, '..') })) {
logError('构建失败');
process.exit(1);
}
logSuccess('构建完成');
} else {
logStep('1/4', '跳过构建');
}
// 检查构建产物
if (!fs.existsSync(CONFIG.distDir)) {
logError(`构建目录不存在: ${CONFIG.distDir}`);
process.exit(1);
}
// 步骤 2: 准备 CDN 目录
logStep('2/4', '准备 CDN 发布目录');
if (fs.existsSync(CONFIG.cdnDir)) {
fs.rmSync(CONFIG.cdnDir, { recursive: true });
}
ensureDir(CONFIG.cdnDir);
// 复制文件(排除敏感文件)
const { copied, excluded } = copyFilteredFiles(CONFIG.distDir, CONFIG.cdnDir);
logSuccess(`复制 ${copied} 个文件,排除 ${excluded} 个敏感文件`);
// 步骤 3: 生成 package.json 和 README
logStep('3/4', '生成 npm 发布文件');
const npmPackage = generateNpmPackageJson(version);
fs.writeFileSync(
path.join(CONFIG.cdnDir, 'package.json'),
JSON.stringify(npmPackage, null, 2)
);
logSuccess('生成 package.json');
const readme = generateReadme(version);
fs.writeFileSync(path.join(CONFIG.cdnDir, 'README.md'), readme);
logSuccess('生成 README.md');
// 列出排除的文件类型
log('\n📋 排除的文件类型:', 'cyan');
log(' - *.html (HTML 文件)');
log(' - *.map (Source maps)');
// 步骤 4: 发布
logStep('4/4', '发布到 npm');
if (isDryRun) {
log('\n📦 DRY RUN - 将要发布的内容:', 'yellow');
exec(`ls -la "${CONFIG.cdnDir}"`);
log('\n📄 package.json:', 'yellow');
console.log(JSON.stringify(npmPackage, null, 2));
} else {
let publishCmd = `cd "${CONFIG.cdnDir}" && npm publish --access public --registry https://registry.npmjs.org`;
if (otp) {
publishCmd += ` --otp=${otp}`;
log(` 使用 OTP: ${otp.slice(0, 2)}****`);
}
if (!exec(publishCmd)) {
logError('发布失败');
if (!otp) {
log('\n💡 提示:如果启用了 2FA请使用 --otp=123456 参数', 'yellow');
}
process.exit(1);
}
logSuccess('发布成功!');
}
// 输出访问链接
log('\n' + '═'.repeat(50), 'green');
log('🎉 完成!', 'green');
log('═'.repeat(50), 'green');
log('\n📌 CDN 访问地址:', 'cyan');
log(` unpkg: https://unpkg.com/${CONFIG.packageName}@${version}/`);
log(` jsdelivr: https://cdn.jsdelivr.net/npm/${CONFIG.packageName}@${version}/`);
log('\n📝 下一步:', 'yellow');
log(' 1. 在你的服务器上部署 HTML 文件');
log(' 2. 修改 HTML 中的资源路径指向 CDN');
log(' 3. 或使用 build-hybrid.js 自动处理\n');
}
main().catch(error => {
logError(`脚本执行失败: ${error.message}`);
console.error(error);
process.exit(1);
});