6type MfsEntry = {cid: any, name: string, type: number | string} 8const listMfsEntries = async (client: ReturnType<typeof getIpfsClient>, mfsRoot: string): Promise<MfsEntry[]> => { 9 const entries: MfsEntry[] = [] 10 for await (const entry of client.files.ls(mfsRoot)) { 11 entries.push(entry as any) 16export const verifyMfsReplication = async ({rootCids, mfsParamsMap}: { 18 mfsParamsMap: Record<string, {mfsRoot: string, kvKey: string}> 27 perRoot: {} as Record<string, {primaryCid: string, synced: string[], healed: string[], mismatches: string[]}>, 30 for (const {label, cid} of rootCids) { 31 const bareLabel = label.split('/').pop()! 32 const mfsParam = mfsParamsMap[bareLabel] 33 if (!mfsParam) continue 36 const entry = {primaryCid: cid, synced: [] as string[], healed: [] as string[], mismatches: [] as string[]} 37 result.perRoot[label] = entry 39 let primaryEntries: MfsEntry[] 41 primaryEntries = await listMfsEntries(primaryClient, mfsParam.mfsRoot) 43 betLog('mfsVerifyPrimaryLsFail', {label, mfsRoot: mfsParam.mfsRoot, errMsg: (err as Error).message}) 47 for (const t of targets) { 48 if (t.client === primaryClient) continue 50 let secondaryEntries: MfsEntry[] 52 secondaryEntries = await listMfsEntries(t.client, mfsParam.mfsRoot) 57 const secondaryNames = new Set(secondaryEntries.filter(e => e.type === 'file').map(e => e.name)) 58 const missingEntries = primaryEntries.filter(e => e.type === 'file' && !secondaryNames.has(e.name)) 60 if (missingEntries.length > 0) { 62 await t.client.files.mkdir(mfsParam.mfsRoot, {parents: true}).catch(() => {}) 63 for (const me of missingEntries) { 64 const mfsPath = `${mfsParam.mfsRoot}/${me.name}` 65 const meCid = me.cid.toString() 66 await t.client.files.cp(`/ipfs/${meCid}`, mfsPath).catch((err: Error) => betLog('mfsVerifyCpFail', {label, host: t.host, mfsPath, errMsg: err.message})) 68 entry.healed.push(t.host) 70 betLog('mfsVerifyHealed', {label, host: t.host, copiedCount: missingEntries.length}) 72 betLog('mfsVerifyHealFail', {label, host: t.host, errMsg: (err as Error).message}) 77 const stat = await t.client.files.stat(mfsParam.mfsRoot) 78 const secondaryRootCid = stat.cid.toString() 79 if (secondaryRootCid === cid) { 80 entry.synced.push(t.host) 82 betLog('mfsVerifyRebuildRoot', {label, host: t.host, primaryCid: cid, oldCid: secondaryRootCid}) 83 await t.client.files.rm(mfsParam.mfsRoot, {recursive: true}).catch((err: Error) => betLog('mfsVerifyRmFail', {label, host: t.host, errMsg: err.message})) 84 await t.client.files.cp(`/ipfs/${cid}`, mfsParam.mfsRoot).catch((err: Error) => betLog('mfsVerifyRebuildCpFail', {label, host: t.host, errMsg: err.message})) 85 entry.synced.push(t.host) 88 betLog('mfsVerifyStatFail', {label, host: t.host, errMsg: (err as Error).message}) 92 if (entry.mismatches.length === 0 && entry.healed.length === 0) result.synced++ 95 if (result.mismatches > 0) await noThrowNotifErr('mfsReplicationMismatch', result)