diff --git a/lib/fs-utils.ts b/lib/fs-utils.ts new file mode 100644 index 0000000..e35dfa9 --- /dev/null +++ b/lib/fs-utils.ts @@ -0,0 +1,106 @@ +// lib/fs-utils.ts +import fs from 'fs/promises'; +import path from 'path'; +import { RESOURCE_PATH } from './constants'; + +/** + * 获取所有项目目录 + */ +export async function getProjects(): Promise { + const dirs = await fs.readdir(RESOURCE_PATH); + const projectDirs = dirs.filter(dir => { + const dirPath = path.join(RESOURCE_PATH, dir); + // 检查是否是目录 + return fs.stat(dirPath).then(stats => stats.isDirectory()).catch(() => false); + }); + return projectDirs; +} + +/** + * 获取指定项目的所有版本目录 + */ +export async function getVersions(project: string): Promise { + const projectPath = path.join(RESOURCE_PATH, project); + const dirs = await fs.readdir(projectPath); + const versionDirs = dirs.filter(dir => { + const dirPath = path.join(projectPath, dir); + return fs.stat(dirPath).then(stats => stats.isDirectory()).catch(() => false); + }); + return versionDirs.sort(); +} + +/** + * 获取指定项目版本的 APK 文件列表 + */ +export async function getApks(project: string, version: string, commitId?: string): Promise<{ + name: string; + commit: string | null; + size: number; + modifiedAt: Date; +}[]> { + const apksPath = path.join(RESOURCE_PATH, project, version, 'apks'); + + try { + const files = await fs.readdir(apksPath); + const apkFiles = files.filter(file => file.endsWith('.apk')); + + const apks = await Promise.all( + apkFiles.map(async (file) => { + const filePath = path.join(apksPath, file); + const stats = await fs.stat(filePath); + + // 提取 commit id (假设文件名格式为 xxx-{commit}-xxx.apk) + const commitMatch = file.match(/([a-f0-9]{7,})/i); + const commit = commitMatch ? commitMatch[1] : null; + + return { + name: file, + commit, + size: stats.size, + modifiedAt: stats.mtime, + }; + }) + ); + + // 如果指定了 commit id,进行过滤 + if (commitId) { + return apks.filter(apk => + apk.commit && apk.commit.toLowerCase().includes(commitId.toLowerCase()) + ); + } + + return apks.sort((a, b) => b.modifiedAt.getTime() - a.modifiedAt.getTime()); + } catch (error) { + // 如果 apks 目录不存在,返回空数组 + return []; + } +} + +/** + * 格式化版本号显示 (0_42 -> 0.42) + */ +export function formatVersion(version: string): string { + return version.replace('_', '.'); +} + +/** + * 格式化文件大小 + */ +export function formatFileSize(bytes: number): string { + if (bytes < 1024) return bytes + ' B'; + if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + ' KB'; + if (bytes < 1024 * 1024 * 1024) return (bytes / (1024 * 1024)).toFixed(2) + ' MB'; + return (bytes / (1024 * 1024 * 1024)).toFixed(2) + ' GB'; +} + +/** + * 格式化日期时间 + */ +export function formatDateTime(date: Date): string { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + const hours = String(date.getHours()).padStart(2, '0'); + const minutes = String(date.getMinutes()).padStart(2, '0'); + return `${year}-${month}-${day} ${hours}:${minutes}`; +}