···11+import { NextRequest, NextResponse } from 'next/server';
22+import { createClient } from '@supabase/supabase-js';
33+import { containsBannedWords, sanitizeText } from '@/lib/content-filter';
44+55+// Configure this route as dynamic to prevent any caching
66+export const dynamic = 'force-dynamic';
77+88+// Define type for our database entry
99+interface FlushingRecord {
1010+ id: string | number;
1111+ uri: string;
1212+ cid: string;
1313+ did: string;
1414+ text: string;
1515+ emoji: string;
1616+ created_at: string;
1717+ handle?: string;
1818+}
1919+2020+// Type for the processed entry for the client
2121+interface ProcessedEntry {
2222+ id: string | number;
2323+ uri: string;
2424+ cid: string;
2525+ authorDid: string;
2626+ authorHandle: string;
2727+ text: string;
2828+ emoji: string;
2929+ createdAt: string;
3030+}
3131+3232+// Constants
3333+const MAX_ENTRIES = 20;
3434+3535+// Supabase client - using environment variables
3636+const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL || '';
3737+const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY || '';
3838+3939+export async function GET(request: NextRequest) {
4040+ // Debug log the incoming request
4141+ console.log(`\n=== SIMPLE FEED REQUEST @ ${new Date().toISOString()} ===`);
4242+ console.log(`URL: ${request.url}`);
4343+4444+ try {
4545+ const url = new URL(request.url);
4646+ const beforeCursor = url.searchParams.get('before');
4747+4848+ console.log(`Request params: beforeCursor=${beforeCursor || 'none'}`);
4949+5050+ // If we don't have Supabase credentials, return an error
5151+ if (!supabaseUrl || !supabaseKey) {
5252+ console.error('Missing Supabase credentials');
5353+ return NextResponse.json(
5454+ { error: 'Server configuration error' },
5555+ { status: 500 }
5656+ );
5757+ }
5858+5959+ // Create Supabase client
6060+ const supabase = createClient(supabaseUrl, supabaseKey);
6161+6262+ // Show highest ID in database for debugging
6363+ const { data: maxIdResult } = await supabase
6464+ .from('flushing_records')
6565+ .select('id')
6666+ .order('id', { ascending: false })
6767+ .limit(1);
6868+6969+ console.log('Highest ID in database:', maxIdResult?.[0]?.id || 'unknown');
7070+7171+ let entries: FlushingRecord[] = [];
7272+7373+ if (beforeCursor) {
7474+ // Pagination query: get entries older than the cursor
7575+ console.log(`Fetching entries older than ID ${beforeCursor}`);
7676+7777+ const { data, error } = await supabase
7878+ .from('flushing_records')
7979+ .select('*')
8080+ .lt('id', beforeCursor)
8181+ .order('id', { ascending: false })
8282+ .limit(MAX_ENTRIES);
8383+8484+ if (error) {
8585+ throw new Error(`Database query error: ${error.message}`);
8686+ }
8787+8888+ entries = data || [];
8989+ } else {
9090+ // Main query: get the most recent entries
9191+ console.log('Fetching latest entries');
9292+9393+ // Standard query approach - simplest and most reliable
9494+ const { data, error } = await supabase
9595+ .from('flushing_records')
9696+ .select('*')
9797+ .order('id', { ascending: false })
9898+ .limit(MAX_ENTRIES);
9999+100100+ if (error) {
101101+ throw new Error(`Database query error: ${error.message}`);
102102+ }
103103+104104+ entries = data || [];
105105+ }
106106+107107+ console.log(`Query returned ${entries.length} entries`);
108108+109109+ if (entries.length > 0) {
110110+ console.log('Top 5 entries:');
111111+ for (let i = 0; i < Math.min(5, entries.length); i++) {
112112+ const entry = entries[i];
113113+ console.log(` ${i+1}. ID: ${entry.id}, Handle: ${entry.handle || 'unknown'}, Text: "${entry.text.substring(0, 20)}..."`);
114114+ }
115115+ } else {
116116+ console.warn('No entries found - this may indicate a database problem');
117117+ }
118118+119119+ // Process entries for the client
120120+ const processedEntries = entries.map((entry) => {
121121+ // Skip entries with banned content
122122+ if (containsBannedWords(entry.text)) {
123123+ return null;
124124+ }
125125+126126+ // Use the handle from the database, or extract from DID as fallback
127127+ const authorHandle = entry.handle ||
128128+ (entry.did.startsWith('did:plc:') ?
129129+ `${entry.did.substring(8, 16)}...` :
130130+ `${entry.did.substring(0, 8)}...`);
131131+132132+ // Return processed entry
133133+ return {
134134+ id: entry.id,
135135+ uri: entry.uri,
136136+ cid: entry.cid,
137137+ authorDid: entry.did,
138138+ authorHandle: authorHandle,
139139+ text: sanitizeText(entry.text),
140140+ emoji: entry.emoji,
141141+ createdAt: entry.created_at
142142+ } as ProcessedEntry;
143143+ });
144144+145145+ // Filter out null entries (those with banned content)
146146+ const filteredEntries = processedEntries.filter((entry): entry is ProcessedEntry => entry !== null);
147147+148148+ // Return the processed entries
149149+ return NextResponse.json({
150150+ entries: filteredEntries,
151151+ source: 'simple-query'
152152+ });
153153+ } catch (error: any) {
154154+ console.error('Error in simple feed API:', error);
155155+ return NextResponse.json(
156156+ { error: 'Failed to fetch feed', message: error.message },
157157+ { status: 500 }
158158+ );
159159+ }
160160+}
+8-9
app/src/app/page.tsx
···189189 createdAt: new Date().toISOString()
190190 };
191191192192- console.log('Adding temporary entry to feed:', tempEntry);
192192+ console.log('Adding temporary entry to feed for immediate display:', tempEntry);
193193194194 // Add the temporary entry to the top of the feed
195195 setEntries(prevEntries => [tempEntry, ...prevEntries]);
···221221 setLoading(true);
222222 setError(null);
223223224224- // Use the new direct API endpoint that bypasses all caching
225224 // Add a timestamp to ensure we bypass browser caching
226225 const timestamp = Date.now();
227226228228- // Use our new direct API endpoint which has a more reliable implementation
229229- const url = `/api/bluesky/feed-direct?_t=${timestamp}`;
227227+ // Use our simple API endpoint for reliability
228228+ const url = `/api/bluesky/feed-simple?_t=${timestamp}`;
230229231230 console.log(`Fetching feed from ${url} at ${new Date().toISOString()}`);
232231···304303 console.log(`Loading older entries before ID ${oldestEntry.id}`);
305304306305 // Use the oldest entry's ID as the cursor, plus add a unique timestamp
307307- // Use our direct API for more reliable pagination
308308- const url = `/api/bluesky/feed-direct?before=${oldestEntry.id}&_t=${Date.now()}`;
306306+ // Use our simple API for reliable pagination
307307+ const url = `/api/bluesky/feed-simple?before=${oldestEntry.id}&_t=${Date.now()}`;
309308310309 const response = await fetch(url, {
311310 cache: 'no-store',
···486485 setLoading(true);
487486 setError(null);
488487489489- // Use the direct API endpoint with a timestamp
488488+ // Use the simple API endpoint with a timestamp
490489 const timestamp = Date.now();
491491- const url = `/api/bluesky/feed-direct?_t=${timestamp}`;
490490+ const url = `/api/bluesky/feed-simple?_t=${timestamp}`;
492491 console.log(`🔄 MANUAL REFRESH @ ${new Date().toISOString()}`);
493493- console.log(`Using direct API URL: ${url}`);
492492+ console.log(`Using simple API URL: ${url}`);
494493495494 // Use strong no-cache headers to ensure browsers don't use cached responses
496495 const response = await fetch(url, {