7const gamesFixmfsCmd = 'ptnode pt0/gamesapp/binF/epDbSync.mjs fixmfs' 8const donateFixmfsCmd = 'ptnode pt0/donateapp/binF/epDbSync.mjs fixmfs' 10const getMfsCids = async (mfsRootPath: string): Promise<Set<string>> => { 12 const cids = new Set<string>() 14 for await (const entry of client.files.ls(mfsRootPath)) { 15 if ((entry as any).type === 'file') { 16 cids.add((entry as any).cid.toString()) 23const collectGamesAppDbCids = async ({db}: {db: any}): Promise<Map<string, string[]>> => { 24 const rows = await db('dbgames').select('game_id', 'game_state') 25 const dbCids = new Map<string, string[]>() 26 const addCid = (cid: string, gameId: string) => { 27 const arr = dbCids.get(cid) ?? [] 28 if (!arr.includes(gameId)) arr.push(gameId) 31 for (const row of rows) { 32 const state = typeof row.game_state === 'string' ? JSON.parse(row.game_state) : row.game_state 33 const stacks = state?.hiddenState?.paperStacks ?? {} 34 for (const stack of Object.values(stacks) as any[]) { 35 for (const r of stack?.responses ?? []) { 36 if (r.lastIpfsCid) addCid(r.lastIpfsCid, row.game_id) 39 for (const action of state?.gameActionHistA ?? []) { 40 if (action.ipfsCid) addCid(action.ipfsCid, row.game_id) 46const collectDonateAppDbCids = async ({db}: {db: any}): Promise<Map<string, string[]>> => { 47 const rows = await db('dona_entries').whereNotNull('ipfs_cid').select('ipfs_cid', 'id') 48 const dbCids = new Map<string, string[]>() 49 for (const row of rows) { 50 const arr = dbCids.get(row.ipfs_cid) ?? [] 52 dbCids.set(row.ipfs_cid, arr) 57export const checkGamesAppIpfsPins = async ({db, qsName}: {db: any, qsName: string}) => { 59 const mfsEntry = await db('generic_kvs').where({key: kvKey}).first() 60 if (!mfsEntry?.value) { betLog('checkGamesIpfsPinsSkip', {kvKey}); return } 62 const dbCids = await collectGamesAppDbCids({db}) 63 const mfsCids = await getMfsCids(mfsRoot) 64 const orphanedCids = [...dbCids.keys()].filter(cid => !mfsCids.has(cid)) 65 const orphanCount = orphanedCids.length 67 betLog('checkGamesIpfsPins', {total: dbCids.size, orphaned: orphanCount}) 69 orphanedCids: orphanedCids.map(cid => ({cid: cid.slice(0, 8) + '...', gameIds: dbCids.get(cid)})), 70 fixmfsCmd: gamesFixmfsCmd 74export const checkDonateAppIpfsPins = async ({db, qsName}: {db: any, qsName: string}) => { 76 const mfsEntry = await db('generic_kvs').where({key: kvKey}).first() 77 if (!mfsEntry?.value) { betLog('checkDonateIpfsPinsSkip', {kvKey}); return } 79 const dbCids = await collectDonateAppDbCids({db}) 80 const mfsCids = await getMfsCids(mfsRoot) 81 const orphanedCids = [...dbCids.keys()].filter(cid => !mfsCids.has(cid)) 82 const orphanCount = orphanedCids.length 84 betLog('checkDonateIpfsPins', {total: dbCids.size, orphaned: orphanCount}) 86 orphanedCids: orphanedCids.map(cid => ({cid, entryIds: dbCids.get(cid)})), 87 fixmfsCmd: donateFixmfsCmd 91export const findAndFixOrphanedCids = async ({db, qsName}: {db: any, qsName: string}) => { 93 const mfsEntry = await db('generic_kvs').where({key: kvKey}).first() 94 if (!mfsEntry?.value) throw new Error(`No MFS root for ${kvKey}`) 96 const mfsCids = await getMfsCids(mfsRoot) 97 const orphanedWithExt: Array<{cid: string, ext: string}> = [] 100 const rows = await db('dbgames').select('game_state') 101 for (const row of rows) { 102 const state = typeof row.game_state === 'string' ? JSON.parse(row.game_state) : row.game_state 103 const stacks = state?.hiddenState?.paperStacks ?? {} 104 for (const stack of Object.values(stacks) as any[]) { 105 for (const r of stack?.responses ?? []) { 106 if (r.lastIpfsCid && !mfsCids.has(r.lastIpfsCid) && !orphanedWithExt.some(o => o.cid === r.lastIpfsCid)) { 107 orphanedWithExt.push({cid: r.lastIpfsCid, ext: 'png'}) 111 for (const action of state?.gameActionHistA ?? []) { 112 if (action.ipfsCid && !mfsCids.has(action.ipfsCid) && !orphanedWithExt.some(o => o.cid === action.ipfsCid)) { 113 orphanedWithExt.push({cid: action.ipfsCid, ext: 'png'}) 120 const rows = await db('dona_entries').whereNotNull('ipfs_cid').select('ipfs_cid', 'upload_mime_type') 121 for (const row of rows) { 122 if (row.ipfs_cid && !mfsCids.has(row.ipfs_cid) && !orphanedWithExt.some(o => o.cid === row.ipfs_cid)) { 123 const ext = row.upload_mime_type ? (getExtFromMime(row.upload_mime_type) || 'bin') : 'bin' 124 orphanedWithExt.push({cid: row.ipfs_cid, ext}) 129 if (orphanedWithExt.length === 0) { 131 return {fixed: 0, failed: 0} 134 const summary = {fixed: 0, failed: 0} 135 for (const {cid, ext} of orphanedWithExt) { 143 betLog('fixmfsFail', {cid, errMsg: (err as Error).message})