This repository has no description
0

Configure Feed

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

fix 22

+151 -71
+151 -71
app/src/app/api/bluesky/profile/route.ts
··· 114 114 115 115 // Step 2: Get the PDS service endpoint from PLC directory 116 116 let serviceEndpoint = 'https://bsky.social'; // Start with bsky.social as fallback 117 + let servicePds = null; // Store the actual PDS domain for logging 117 118 try { 119 + console.log(`Looking up PDS endpoint for DID: ${did}`); 118 120 const plcResponse = await fetch(`https://plc.directory/${did}/data`); 119 121 120 122 if (plcResponse.ok) { 121 123 const plcData = await plcResponse.json(); 124 + console.log(`Got PLC directory data for ${did}`); 122 125 123 126 // Extract service endpoint from PLC data 124 127 if (plcData && plcData.service) { ··· 129 132 130 133 if (pdsService && pdsService.endpoint) { 131 134 serviceEndpoint = pdsService.endpoint; 135 + 136 + // Extract just the domain for reference 137 + try { 138 + const serviceUrl = new URL(pdsService.endpoint); 139 + servicePds = serviceUrl.hostname; 140 + console.log(`Found PDS service for ${handle} at ${serviceEndpoint} (${servicePds})`); 141 + } catch (e) { 142 + console.warn(`Could not parse service URL: ${pdsService.endpoint}`); 143 + } 132 144 } 133 145 } 146 + } else { 147 + console.warn(`PLC directory lookup failed for ${did}: ${plcResponse.status} ${plcResponse.statusText}`); 134 148 } 135 149 } catch (error: any) { 136 150 console.warn(`Failed to get service endpoint from PLC directory: ${error.message}`); ··· 149 163 }); 150 164 151 165 if (!recordsResponse.ok) { 152 - // Special case for handling third-party PDS 153 - if (handle.includes('.') && !handle.endsWith('bsky.social') && !handle.endsWith('flushes.app') && !handle.endsWith('flushing.im')) { 154 - const domain = handle.split('.').slice(1).join('.'); 155 - 156 - console.log(`Trying direct domain access before falling back: ${domain}`); 157 - try { 158 - const directUrl = `https://${domain}/xrpc/com.atproto.repo.listRecords?repo=${encodeURIComponent(did)}&collection=${encodeURIComponent(FLUSHING_STATUS_NSID)}&limit=${MAX_ENTRIES}`; 159 - 160 - const directResponse = await fetch(directUrl, { 161 - headers: { 'Accept': 'application/json' } 162 - }); 163 - 164 - if (directResponse.ok) { 165 - console.log(`Successfully accessed records directly from ${domain}`); 166 - const directData = await directResponse.json(); 166 + // Log complete error information - this helps diagnose third-party PDS issues 167 + console.warn(`Failed to get records from ${serviceEndpoint}`); 168 + try { 169 + const errorText = await recordsResponse.text(); 170 + console.error(`Error response from ${serviceEndpoint}: ${errorText}`); 171 + } catch (e) { 172 + console.error(`Could not read error response: ${e}`); 173 + } 174 + 175 + // Try appropriate fallback, with different strategies for different PDS hosts 176 + if (serviceEndpoint !== 'https://public.api.bsky.app') { 177 + // 1. If we have a servicePds from the PLC directory, try using it directly 178 + if (servicePds) { 179 + try { 180 + console.log(`Trying direct PDS domain: https://${servicePds}`); 181 + const pdsDirectUrl = `https://${servicePds}/xrpc/com.atproto.repo.listRecords?repo=${encodeURIComponent(did)}&collection=${encodeURIComponent(FLUSHING_STATUS_NSID)}&limit=${MAX_ENTRIES}`; 167 182 168 - // Process the direct response... 169 - // Process the direct response... 170 - const directEntries = directData.records 171 - .map((record: any) => { 172 - const text = record.value.text || ''; 173 - if (containsBannedWords(text)) return null; 174 - 175 - return { 176 - id: record.uri, 177 - uri: record.uri, 178 - cid: record.cid, 179 - did: did, 180 - text: sanitizeText(text), 181 - emoji: record.value.emoji || '🚽', 182 - created_at: record.value.createdAt 183 - }; 184 - }) 185 - .filter((entry: ProfileEntry | null): entry is ProfileEntry => entry !== null); 186 - 187 - // Calculate emoji stats 188 - const directEmojiCounts = new Map<string, number>(); 189 - directEntries.forEach((entry: ProfileEntry) => { 190 - const emoji = entry.emoji?.trim() || '🚽'; 191 - if (APPROVED_EMOJIS.includes(emoji)) { 192 - directEmojiCounts.set(emoji, (directEmojiCounts.get(emoji) || 0) + 1); 193 - } else { 194 - directEmojiCounts.set('🚽', (directEmojiCounts.get('🚽') || 0) + 1); 195 - } 183 + const pdsDirectResponse = await fetch(pdsDirectUrl, { 184 + headers: { 'Accept': 'application/json' } 196 185 }); 197 186 198 - const directEmojiStats = Array.from(directEmojiCounts.entries()) 199 - .map(([emoji, count]): EmojiStat => ({ emoji, count })) 200 - .sort((a, b) => b.count - a.count); 187 + if (pdsDirectResponse.ok) { 188 + console.log(`Successfully accessed records directly from PDS domain: ${servicePds}`); 189 + const directData = await pdsDirectResponse.json(); 190 + 191 + // Process the direct response 192 + const directEntries = directData.records 193 + .map((record: any) => { 194 + const text = record.value.text || ''; 195 + if (containsBannedWords(text)) return null; 196 + 197 + return { 198 + id: record.uri, 199 + uri: record.uri, 200 + cid: record.cid, 201 + did: did, 202 + text: sanitizeText(text), 203 + emoji: record.value.emoji || '🚽', 204 + created_at: record.value.createdAt 205 + }; 206 + }) 207 + .filter((entry: ProfileEntry | null): entry is ProfileEntry => entry !== null); 208 + 209 + // Calculate emoji stats 210 + const directEmojiCounts = new Map<string, number>(); 211 + directEntries.forEach((entry: ProfileEntry) => { 212 + const emoji = entry.emoji?.trim() || '🚽'; 213 + if (APPROVED_EMOJIS.includes(emoji)) { 214 + directEmojiCounts.set(emoji, (directEmojiCounts.get(emoji) || 0) + 1); 215 + } else { 216 + directEmojiCounts.set('🚽', (directEmojiCounts.get('🚽') || 0) + 1); 217 + } 218 + }); 219 + 220 + const directEmojiStats = Array.from(directEmojiCounts.entries()) 221 + .map(([emoji, count]): EmojiStat => ({ emoji, count })) 222 + .sort((a, b) => b.count - a.count); 223 + 224 + return NextResponse.json({ 225 + entries: directEntries, 226 + count: directEntries.length, 227 + cursor: directData.cursor, 228 + profile: userProfile, 229 + emojiStats: directEmojiStats, 230 + serviceEndpoint: `https://${servicePds}`, 231 + directPds: true 232 + }); 233 + } else { 234 + console.warn(`PDS direct access failed: ${await pdsDirectResponse.text()}`); 235 + } 236 + } catch (pdsErr) { 237 + console.error(`Error with direct PDS domain access: ${pdsErr}`); 238 + } 239 + } 240 + 241 + // 2. For third-party domain handles, try using the handle's domain 242 + if (handle.includes('.') && !handle.endsWith('bsky.social') && !handle.endsWith('flushes.app') && !handle.endsWith('flushing.im')) { 243 + const domain = handle.split('.').slice(1).join('.'); 244 + try { 245 + console.log(`Trying handle domain access: https://${domain}`); 246 + const domainUrl = `https://${domain}/xrpc/com.atproto.repo.listRecords?repo=${encodeURIComponent(did)}&collection=${encodeURIComponent(FLUSHING_STATUS_NSID)}&limit=${MAX_ENTRIES}`; 201 247 202 - return NextResponse.json({ 203 - entries: directEntries, 204 - count: directEntries.length, 205 - cursor: directData.cursor, 206 - profile: userProfile, 207 - emojiStats: directEmojiStats, 208 - serviceEndpoint: `https://${domain}`, 209 - directPds: true 248 + const domainResponse = await fetch(domainUrl, { 249 + headers: { 'Accept': 'application/json' } 210 250 }); 211 - } else { 212 - console.warn(`Direct access failed: ${await directResponse.text()}`); 251 + 252 + if (domainResponse.ok) { 253 + console.log(`Successfully accessed records from handle domain: ${domain}`); 254 + const domainData = await domainResponse.json(); 255 + 256 + // Process the domain response 257 + const domainEntries = domainData.records 258 + .map((record: any) => { 259 + const text = record.value.text || ''; 260 + if (containsBannedWords(text)) return null; 261 + 262 + return { 263 + id: record.uri, 264 + uri: record.uri, 265 + cid: record.cid, 266 + did: did, 267 + text: sanitizeText(text), 268 + emoji: record.value.emoji || '🚽', 269 + created_at: record.value.createdAt 270 + }; 271 + }) 272 + .filter((entry: ProfileEntry | null): entry is ProfileEntry => entry !== null); 273 + 274 + // Calculate emoji stats 275 + const domainEmojiCounts = new Map<string, number>(); 276 + domainEntries.forEach((entry: ProfileEntry) => { 277 + const emoji = entry.emoji?.trim() || '🚽'; 278 + if (APPROVED_EMOJIS.includes(emoji)) { 279 + domainEmojiCounts.set(emoji, (domainEmojiCounts.get(emoji) || 0) + 1); 280 + } else { 281 + domainEmojiCounts.set('🚽', (domainEmojiCounts.get('🚽') || 0) + 1); 282 + } 283 + }); 284 + 285 + const domainEmojiStats = Array.from(domainEmojiCounts.entries()) 286 + .map(([emoji, count]): EmojiStat => ({ emoji, count })) 287 + .sort((a, b) => b.count - a.count); 288 + 289 + return NextResponse.json({ 290 + entries: domainEntries, 291 + count: domainEntries.length, 292 + cursor: domainData.cursor, 293 + profile: userProfile, 294 + emojiStats: domainEmojiStats, 295 + serviceEndpoint: `https://${domain}`, 296 + handleDomain: true 297 + }); 298 + } else { 299 + console.warn(`Handle domain access failed: ${await domainResponse.text()}`); 300 + } 301 + } catch (domainErr) { 302 + console.error(`Error with handle domain access: ${domainErr}`); 213 303 } 214 - } catch (directErr) { 215 - console.error(`Error with direct domain access: ${directErr}`); 216 304 } 217 - } 218 - 219 - // If direct access failed or not applicable, try with public API endpoint 220 - if (serviceEndpoint !== 'https://public.api.bsky.app') { 221 - console.warn(`Failed to get records from ${serviceEndpoint}, trying public API endpoint`); 222 - // Log the error for debugging third-party PDS issues 223 - try { 224 - const errorText = await recordsResponse.text(); 225 - console.error(`Error response from ${serviceEndpoint}: ${errorText}`); 226 - } catch (e) { 227 - console.error(`Could not read error response: ${e}`); 228 - } 305 + 306 + // 3. Last resort: try public API 307 + console.warn(`All direct approaches failed, trying public API fallback`); 229 308 const fallbackUrl = `https://public.api.bsky.app/xrpc/com.atproto.repo.listRecords?repo=${encodeURIComponent(did)}&collection=${encodeURIComponent(FLUSHING_STATUS_NSID)}&limit=${MAX_ENTRIES}`; 230 309 231 310 const fallbackResponse = await fetch(fallbackUrl, { ··· 365 444 cursor: recordsData.cursor, 366 445 profile: userProfile, 367 446 emojiStats, 368 - serviceEndpoint // Include the endpoint we used for debugging 447 + serviceEndpoint, // Include the endpoint we used for debugging 448 + servicePds // Include the extracted PDS domain 369 449 }); 370 450 } catch (error: any) { 371 451 console.error('Error fetching records:', error);