SSH 协议层: - 新增 Curve25519 密钥交换算法支持 - 新增 AES-CTR 加密模式 (aes256-ctr, aes192-ctr, aes128-ctr) - 新增 HMAC 完整性校验 (hmac-sha2-256, hmac-sha2-512, hmac-sha1) - 优化算法协商和密钥派生流程 前端: - 文件传输从 Zmodem 迁移到 trzsz,支持拖拽上传和 tmux 兼容 - 新增多标签页 SSH 支持(登录用户可在新标签页打开独立终端) - 修复 WebSocket 二进制数据处理问题 后端: - 恢复 Hibernation API 支持长连接 - 兼容 Dropbear 等不发送 shell 确认的 SSH 服务器 - 关闭调试日志 构建: - 迁移到 pnpm 包管理器 - 添加 GitHub Actions 自动部署
76 lines
2.9 KiB
JavaScript
76 lines
2.9 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
const { execSync } = require('child_process');
|
|
|
|
const rootDir = path.resolve(__dirname, '..');
|
|
const frontendDir = path.join(rootDir, 'frontend');
|
|
const distDir = path.join(frontendDir, 'dist');
|
|
const workerDir = path.join(rootDir, 'src', 'worker');
|
|
const htmlTsFile = path.join(workerDir, 'html.ts');
|
|
|
|
console.log('Building frontend...');
|
|
try {
|
|
// 1. Install and build frontend
|
|
execSync('pnpm install', { cwd: frontendDir, stdio: 'inherit' });
|
|
execSync('pnpm run build', { cwd: frontendDir, stdio: 'inherit' });
|
|
|
|
console.log('Inlining assets...');
|
|
// 2. Read dist/index.html
|
|
let html = fs.readFileSync(path.join(distDir, 'index.html'), 'utf8');
|
|
|
|
// 3. Find assets in dist/assets
|
|
const assetsDir = path.join(distDir, 'assets');
|
|
const files = fs.readdirSync(assetsDir);
|
|
|
|
let jsContent = '';
|
|
let cssContent = '';
|
|
|
|
for (const file of files) {
|
|
const filePath = path.join(assetsDir, file);
|
|
if (file.endsWith('.js')) {
|
|
jsContent = fs.readFileSync(filePath, 'utf8');
|
|
} else if (file.endsWith('.css')) {
|
|
cssContent = fs.readFileSync(filePath, 'utf8');
|
|
}
|
|
}
|
|
|
|
// 4. Inline favicon.svg as base64 data URL
|
|
const faviconPath = path.join(frontendDir, 'public', 'favicon.svg');
|
|
if (fs.existsSync(faviconPath)) {
|
|
const faviconContent = fs.readFileSync(faviconPath, 'utf8');
|
|
const faviconBase64 = Buffer.from(faviconContent).toString('base64');
|
|
html = html.replace(
|
|
/href=["']\/favicon\.svg["']/i,
|
|
`href="data:image/svg+xml;base64,${faviconBase64}"`
|
|
);
|
|
}
|
|
|
|
// 5. Inline CSS (replace <link rel="stylesheet" ...>)
|
|
html = html.replace(/<link[^>]*rel=["']stylesheet["'][^>]*href=["']\/assets\/[^"']+["'][^>]*>/i, () => `<style>${cssContent}</style>`);
|
|
html = html.replace(/<link[^>]*rel=["']stylesheet["'][^>]*href=["']\/src\/[^"']+["'][^>]*>/i, () => `<style>${cssContent}</style>`);
|
|
|
|
// 6. Inline JS (replace <script type="module" src="/src/main.ts"></script> or similar)
|
|
html = html.replace(/<script[^>]*type=["']module["'][^>]*src=["']\/assets\/[^"']+["'][^>]*><\/script>/i, () => `<script type="module">${jsContent}</script>`);
|
|
html = html.replace(/<script[^>]*type=["']module["'][^>]*src=["']\/src\/[^"']+["'][^>]*><\/script>/i, () => `<script type="module">${jsContent}</script>`);
|
|
|
|
// 7. Write to src/worker/html.ts
|
|
const escapedHtml = html
|
|
.replace(/\\/g, '\\\\')
|
|
.replace(/`/g, '\\`')
|
|
.replace(/\${/g, '\\${');
|
|
|
|
const htmlTsContent = `// Automatically generated by scripts/build-html.js. Do not edit directly.
|
|
export const HTML = \`${escapedHtml}\`;
|
|
`;
|
|
|
|
if (!fs.existsSync(workerDir)) {
|
|
fs.mkdirSync(workerDir, { recursive: true });
|
|
}
|
|
|
|
fs.writeFileSync(htmlTsFile, htmlTsContent, 'utf8');
|
|
console.log('Frontend built and inlined successfully to src/worker/html.ts!');
|
|
} catch (error) {
|
|
console.error('Failed to build and inline frontend:', error);
|
|
process.exit(1);
|
|
}
|