/** * 下载官方 EasyTier Windows x64 发行包并解压到 resources/easytier/ * 须复制 exe 同目录下全部 DLL 等依赖,否则 easytier-core 会以 0xC0000135(3221225781)退出。 * 设置 SKIP_EASYTIER=1 可跳过(CI 无网环境) */ import fs from 'fs' import path from 'path' import https from 'https' import { fileURLToPath } from 'url' import AdmZip from 'adm-zip' const __dirname = path.dirname(fileURLToPath(import.meta.url)) const root = path.resolve(__dirname, '..') const resDir = path.join(root, 'resources', 'easytier') const DEFAULT_URL = process.env.EASYTIER_DOWNLOAD_URL || 'https://github.com/EasyTier/EasyTier/releases/download/v2.4.5/easytier-windows-x86_64-v2.4.5.zip' const exeName = 'easytier-core.exe' const bundleMarker = '.easytier_bundle_ok' if (process.env.SKIP_EASYTIER === '1') { console.log('[easytier] SKIP_EASYTIER=1,跳过下载') process.exit(0) } function download(url) { return new Promise((resolve, reject) => { https .get(url, (res) => { if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) { download(res.headers.location).then(resolve).catch(reject) return } if (res.statusCode !== 200) { reject(new Error(`HTTP ${res.statusCode} ${url}`)) return } const chunks = [] res.on('data', (c) => chunks.push(c)) res.on('end', () => resolve(Buffer.concat(chunks))) res.on('error', reject) }) .on('error', reject) }) } function findExe(dir, name) { if (!fs.existsSync(dir)) return null for (const f of fs.readdirSync(dir, { withFileTypes: true })) { const p = path.join(dir, f.name) if (f.isDirectory()) { const r = findExe(p, name) if (r) return r } else if (f.name === name) { return p } } return null } fs.mkdirSync(resDir, { recursive: true }) const outExe = path.join(resDir, exeName) const markerPath = path.join(resDir, bundleMarker) if (fs.existsSync(outExe) && fs.existsSync(markerPath)) { console.log('[easytier] 已存在完整包,跳过:', outExe) process.exit(0) } if (fs.existsSync(outExe) && !fs.existsSync(markerPath)) { console.log('[easytier] 检测到旧版仅复制了 exe,将重新下载并解压全部依赖文件…') } console.log('[easytier] 下载:', DEFAULT_URL) const buf = await download(DEFAULT_URL) const zip = new AdmZip(buf) const tmp = path.join(resDir, '_extract_tmp') if (fs.existsSync(tmp)) fs.rmSync(tmp, { recursive: true }) fs.mkdirSync(tmp, { recursive: true }) zip.extractAllTo(tmp, true) const found = findExe(tmp, exeName) if (!found) { console.error('[easytier] zip 内未找到', exeName) process.exit(1) } const srcDir = path.dirname(found) for (const name of fs.readdirSync(srcDir)) { const from = path.join(srcDir, name) const to = path.join(resDir, name) if (!fs.statSync(from).isFile()) continue fs.copyFileSync(from, to) } fs.rmSync(tmp, { recursive: true }) fs.writeFileSync( path.join(resDir, 'NOTICE-EasyTier.txt'), [ 'EasyTier 以 LGPL-3.0 授权发布。', '源码: https://github.com/EasyTier/EasyTier', '本程序随官方 GitHub Release 附带 easytier-core.exe 及同目录依赖,未修改二进制。', '', ].join('\n'), 'utf-8', ) fs.writeFileSync(markerPath, `${new Date().toISOString()}\n`, 'utf-8') console.log('[easytier] 完成(已复制 zip 内同目录全部文件):', outExe)