Blogging platform with advanced tools for arts and sciences.
6

Configure Feed

Select the types of activity you want to include in your feed.

Provide more info to the user during old post migration process

lemma (May 22, 2026, 2:32 PM -0500) b3f55cce be23f814

+30 -12
+15 -7
app/lib/api-client.ts
··· 1043 1043 export async function migratePipupEntries( 1044 1044 session: OAuthSession, 1045 1045 publicationAtUri: string, 1046 - ): Promise<{ migrated: number; skipped: number; errors: number }> { 1046 + ): Promise<{ migrated: number; skipped: number; errors: Array<{ title: string; reason: string }> }> { 1047 1047 const did = session.sub 1048 1048 const pdsUrl = await resolvePdsUrl(did) 1049 1049 ··· 1066 1066 }> 1067 1067 } 1068 1068 1069 - if (records.length === 0) return { migrated: 0, skipped: 0, errors: 0 } 1069 + if (records.length === 0) return { migrated: 0, skipped: 0, errors: [] } 1070 1070 1071 1071 const token = await getServiceToken(session, pdsUrl) 1072 - let migrated = 0, skipped = 0, errors = 0 1072 + let migrated = 0, skipped = 0 1073 + const errors: Array<{ title: string; reason: string }> = [] 1073 1074 1074 1075 for (const record of records) { 1075 1076 const rkey = record.uri.split('/').pop()! ··· 1103 1104 }), 1104 1105 }, 1105 1106 ) 1106 - if (!pdsRes.ok) { errors++; continue } 1107 + if (!pdsRes.ok) { 1108 + const body = await pdsRes.text().catch(() => '') 1109 + errors.push({ title, reason: `PDS write failed (${pdsRes.status})${body ? `: ${body.slice(0, 120)}` : ''}` }) 1110 + continue 1111 + } 1107 1112 1108 1113 const lambdaRes = await fetch(`${apiUrl()}/posts`, { 1109 1114 method: 'POST', ··· 1121 1126 }), 1122 1127 }) 1123 1128 if (lambdaRes.ok) migrated++ 1124 - else errors++ 1125 - } catch { 1126 - errors++ 1129 + else { 1130 + const body = await lambdaRes.text().catch(() => '') 1131 + errors.push({ title, reason: `Index failed (${lambdaRes.status})${body ? `: ${body.slice(0, 120)}` : ''}` }) 1132 + } 1133 + } catch (err) { 1134 + errors.push({ title, reason: err instanceof Error ? err.message : 'Unknown error' }) 1127 1135 } 1128 1136 } 1129 1137
+15 -5
app/routes/settings.tsx
··· 42 42 function MigrateSection({ session }: { session: NonNullable<ReturnType<typeof useAuth>['session']> }) { 43 43 const [publications, setPublications] = useState<Publication[] | null>(null) 44 44 const [status, setStatus] = useState<'idle' | 'running' | 'done' | 'error'>('idle') 45 - const [result, setResult] = useState<{ migrated: number; skipped: number; errors: number } | null>(null) 45 + const [result, setResult] = useState<{ migrated: number; skipped: number; errors: Array<{ title: string; reason: string }> } | null>(null) 46 46 const [error, setError] = useState<string | null>(null) 47 47 48 48 useEffect(() => { ··· 116 116 )} 117 117 118 118 {status === 'done' && result && ( 119 - <div className="space-y-2"> 119 + <div className="space-y-3"> 120 120 <p className="text-sm text-stone-700 dark:text-stone-300"> 121 121 Migration complete —{' '} 122 122 <span className="font-medium">{result.migrated} migrated</span> 123 123 {result.skipped > 0 && `, ${result.skipped} skipped (no title)`} 124 - {result.errors > 0 && ( 125 - <span className="text-red-500">, {result.errors} errors</span> 124 + {result.errors.length > 0 && ( 125 + <span className="text-red-500">, {result.errors.length} errors</span> 126 126 )} 127 127 </p> 128 - {result.migrated === 0 && result.skipped === 0 && result.errors === 0 && ( 128 + {result.migrated === 0 && result.skipped === 0 && result.errors.length === 0 && ( 129 129 <p className="text-sm text-stone-500 dark:text-stone-400">No old entries found.</p> 130 + )} 131 + {result.errors.length > 0 && ( 132 + <ul className="space-y-1.5"> 133 + {result.errors.map((e, i) => ( 134 + <li key={i} className="text-sm"> 135 + <span className="font-medium text-stone-700 dark:text-stone-300">{e.title}</span> 136 + <span className="text-red-500 ml-2">{e.reason}</span> 137 + </li> 138 + ))} 139 + </ul> 130 140 )} 131 141 <button 132 142 onClick={() => { setStatus('idle'); setResult(null) }}