1import fs from 'fs/promises' 10 mfsRoot: string | null 15const META_FILE = '_meta.json' 17export const backupIpfsContent = async ({rootCids, mfsParamsMap}: { 18 rootCids: {label: string, cid: string}[], 19 mfsParamsMap?: Record<string, {mfsRoot: string, kvKey: string}>, 22 const summary = {total: 0, new: 0, skipped: 0, failed: 0} 24 for (const {label, cid} of rootCids) { 28 const mfsParam = mfsParamsMap?.[label] 29 const meta: IpfsBackupMeta = {mfsRoot: mfsParam?.mfsRoot ?? null, kvKey: label, rootCid: cid} 30 await fs.writeFile(path.join(dirPath, META_FILE), JSON.stringify(meta, null, 2)) 32 let entries: {cid: any, name: string, type: string}[] = [] 34 if (mfsParam?.mfsRoot) { 35 for await (const entry of client.files.ls(mfsParam.mfsRoot)) { 36 entries.push(entry as any) 39 for await (const entry of client.ls(cid)) { 40 entries.push(entry as any) 44 betLog('ipfsBackupLsFail', {label, cid, errMsg: (err as Error).message}) 49 const fileEntries = entries.filter(e => e.type === 'file') 50 summary.total += fileEntries.length 52 for (const entry of fileEntries) { 53 const entryCid = entry.cid.toString() 54 const lastDot = entry.name.lastIndexOf('.') 55 const ext = lastDot >= 0 ? entry.name.slice(lastDot + 1) : 'bin' 56 const fileName = `${entryCid}.${ext}` 57 const filePath = path.join(dirPath, fileName) 59 try { await fs.access(filePath); summary.skipped++; continue } catch {} 63 if (!resp.ok) throw new Error(`HTTP ${resp.status}`) 66 const contentType = resp.headers.get('content-type') 69 if (derived) finalExt = derived 72 const finalFileName = `${entryCid}.${finalExt}` 73 const finalFilePath = path.join(dirPath, finalFileName) 74 const buffer = Buffer.from(await resp.arrayBuffer()) 75 await fs.writeFile(finalFilePath, buffer) 78 betLog('ipfsBackupDownloadFail', {label, cid: entryCid, errMsg: (err as Error).message})