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/pocketbase/pocketbase/releases/latest"; const proxy = 'https://gh-proxy.org/'; const program = 'program'; const directory = program + '/' + 'pocketbase'; // 获取系统平台和架构 function getPlatformInfo() { const platform = os.platform(); // 'darwin', 'linux', 'win32' const arch = os.arch(); // 'x64', 'arm64', etc. let platformName = ''; let archName = ''; // 映射平台名称 switch (platform) { case 'darwin': platformName = 'darwin'; break; case 'linux': platformName = 'linux'; break; case 'win32': platformName = 'windows'; break; default: throw new Error(`Unsupported platform: ${platform}`); } // 映射架构名称 switch (arch) { case 'x64': archName = 'amd64'; break; case 'arm64': archName = 'arm64'; break; default: throw new Error(`Unsupported architecture: ${arch}`); } return { platformName, archName }; } // 下载文件 async function downloadFile(url: string, destPath: string): Promise { return new Promise((resolve, reject) => { https.get(url, (response) => { // 处理重定向 if (response.statusCode === 302 || response.statusCode === 301) { const redirectUrl = response.headers.location; if (redirectUrl) { downloadFile(redirectUrl, destPath).then(resolve).catch(reject); return; } } if (response.statusCode !== 200) { reject(new Error(`Failed to download: ${response.statusCode}`)); return; } const fileStream = createWriteStream(destPath); response.pipe(fileStream); fileStream.on('finish', () => { fileStream.close(); resolve(); }); fileStream.on('error', reject); }).on('error', reject); }); } // 解压 zip 文件 (Windows) async function extractZip(zipPath: string, destDir: string): Promise { const AdmZip = require('adm-zip'); const zip = new AdmZip(zipPath); zip.extractAllTo(destDir, true); } // 提取 tar.gz 文件 (Linux/macOS) export async function extractTarGz(tarGzPath: string, destDir: string): Promise { await fs.promises.mkdir(destDir, { recursive: true }); await pipeline( createReadStream(tarGzPath), createGunzip(), extract({ cwd: destDir }) ); } // 主函数:下载并解压 PocketBase export async function download(): Promise { try { const { platformName, archName } = getPlatformInfo(); console.log(`检测到系统: ${platformName} ${archName}`); // 获取最新版本信息 const response = await fetch(base); if (!response.ok) { throw new Error(`Failed to fetch release info: ${response.status}`); } const releaseData = await response.json(); const tagName = releaseData.tag_name.replace(/^v/, ''); const assets = releaseData.assets; // 查找匹配的资源文件 const fileExtension = '.zip'; const assetPattern = `pocketbase_${tagName}_${platformName}_${archName}${fileExtension}`; const asset = assets.find((a: any) => a.name.includes(assetPattern)); if (!asset) { throw new Error(`No matching asset found for ${platformName} ${archName}`); } console.log(`找到资源: ${asset.name}`); console.log(`下载链接: ${asset.browser_download_url}`); // 创建目录 await fs.promises.mkdir(directory, { recursive: true }); const downloadPath = path.join(directory, asset.name); // 下载文件 console.log('开始下载...'); await downloadFile(asset.browser_download_url, downloadPath); console.log('下载完成'); // 解压文件 console.log('开始解压...'); await extractZip(downloadPath, directory); console.log('解压完成'); // 给可执行文件添加执行权限(非 Windows) if (os.platform() !== 'win32') { const finalPath = path.join(directory, 'pocketbase'); fs.chmodSync(finalPath, 0o755); console.log('已添加执行权限'); } // 删除压缩包 await fs.promises.unlink(downloadPath); console.log('清理完成'); console.log(`PocketBase 已安装到: ${path.resolve(directory)}`); } catch (error) { console.error('下载或解压失败:', error); throw error; } } // 如果直接运行此文件 if (require.main === module) { download(); }