import os from "node:os"; import fs from "node:fs"; import path from "node:path"; import https from "node:https"; import { pipeline } from "node:stream/promises"; import { createWriteStream, createReadStream } from "node:fs"; import { createGunzip } from "node:zlib"; import { extract } from "tar"; const base = "https://api.github.com/repos/nocodb/nocodb/releases/latest"; const program = 'program'; const proxy = 'https://gh-proxy.org/'; const directory = program + '/' + 'nocodb'; // 获取平台和架构对应的文件名 function getPlatformFileName(): string { const platform = os.platform(); const arch = os.arch(); if (platform === 'darwin') { return arch === 'arm64' ? 'Noco-macos-arm64' : 'Noco-macos-x64'; } else if (platform === 'linux') { return arch === 'arm64' ? 'Noco-linux-arm64' : 'Noco-linux-x64'; } else if (platform === 'win32') { return arch === 'arm64' ? 'Noco-win-arm64.exe' : 'Noco-win-x64.exe'; } throw new Error(`Unsupported platform: ${platform}-${arch}`); } // 下载文件 async function downloadFile(url: string, dest: string): Promise { return new Promise((resolve, reject) => { https.get(url, { headers: { 'User-Agent': 'nocodb-installer' } }, (response) => { if (response.statusCode === 302 || response.statusCode === 301) { // 处理重定向 downloadFile(response.headers.location!, dest).then(resolve).catch(reject); return; } if (response.statusCode !== 200) { reject(new Error(`Failed to download: ${response.statusCode}`)); return; } const fileStream = createWriteStream(dest); response.pipe(fileStream); fileStream.on('finish', () => { fileStream.close(); resolve(); }); fileStream.on('error', reject); }).on('error', reject); }); } // 解压 tar.gz 文件 async function extractTarGz(filePath: string, destDir: string): Promise { await pipeline( createReadStream(filePath), createGunzip(), extract({ cwd: destDir }) ); } // 主函数 export async function download() { try { console.log('正在获取最新版本信息...'); // 获取 latest release 信息 const releaseData = await new Promise((resolve, reject) => { https.get(proxy+base, { headers: { 'User-Agent': 'nocodb-installer' } }, (response) => { let data = ''; response.on('data', chunk => data += chunk); response.on('end', () => resolve(JSON.parse(data))); response.on('error', reject); }).on('error', reject); }); const version = releaseData.tag_name; console.log(`最新版本: ${version}`); const fileName = getPlatformFileName(); console.log(`平台文件: ${fileName}`); // 查找对应的 asset const asset = releaseData.assets.find((a: any) => a.name === fileName); if (!asset) { throw new Error(`找不到对应的文件: ${fileName}`); } // 创建目录 if (!fs.existsSync(directory)) { fs.mkdirSync(directory, { recursive: true }); } const downloadPath = path.join(directory, fileName); // 下载文件 console.log('正在下载...'); await downloadFile(asset.browser_download_url, downloadPath); console.log(`下载完成: ${downloadPath}`); // 确定最终文件名 const finalFileName = os.platform() === 'win32' ? 'nocodb.exe' : 'nocodb'; const finalPath = path.join(directory, finalFileName); // 如果是 tar.gz 文件,进行解压 if (fileName === 'nocodb.tar.gz') { console.log('正在解压...'); await extractTarGz(downloadPath, directory); console.log('解压完成'); // 删除压缩包 fs.unlinkSync(downloadPath); } else { // 重命名为统一的可执行文件名 if (downloadPath !== finalPath) { fs.renameSync(downloadPath, finalPath); console.log(`重命名为: ${finalFileName}`); } // 给可执行文件添加执行权限(非 Windows) if (os.platform() !== 'win32') { fs.chmodSync(finalPath, 0o755); console.log('已添加执行权限'); } } console.log('安装完成!'); } catch (error) { console.error('安装失败:', error); process.exit(1); } } // 如果直接运行此文件 if (require.main === module) { download(); }