An API you can curl, or open in a browser, to receive Bluesky data as markdown!
10
fork

Configure Feed

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

at main 71 lines 2.6 kB view raw
1import { NextRequest, NextResponse } from 'next/server' 2 3// --------------------------------------------------------------------------- 4// Rate limiting — edge-side, in-memory sliding window 5// Each edge instance tracks its own state. Not globally coordinated, but 6// sufficient to cap runaway single-IP abuse and protect Bluesky's API. 7// --------------------------------------------------------------------------- 8const WINDOW_MS = parseInt(process.env.RATE_LIMIT_WINDOW_MS ?? '60000', 10) 9const MAX_REQUESTS = parseInt(process.env.RATE_LIMIT_MAX ?? '10', 10) 10 11const ipWindows = new Map<string, number[]>() 12 13function isRateLimited(ip: string): boolean { 14 const now = Date.now() 15 const hits = (ipWindows.get(ip) ?? []).filter(t => now - t < WINDOW_MS) 16 if (hits.length >= MAX_REQUESTS) return true 17 hits.push(now) 18 ipWindows.set(ip, hits) 19 return false 20} 21 22// --------------------------------------------------------------------------- 23// Terminal-client detection (curl, wget, etc.) 24// --------------------------------------------------------------------------- 25function isTerminalClient(req: NextRequest): boolean { 26 const ua = req.headers.get('user-agent') ?? '' 27 const accept = req.headers.get('accept') ?? '' 28 29 if (/^(curl|Wget|HTTPie|httpie|xh\/|python-httpx|python-requests|Go-http-client|nushell|Nu\/|httpx\/)/i.test(ua)) { 30 return true 31 } 32 if (/^text\/(markdown|plain)/.test(accept)) return true 33 if (ua && !ua.includes('Mozilla') && !accept.includes('text/html')) { 34 return true 35 } 36 return false 37} 38 39// --------------------------------------------------------------------------- 40// Middleware 41// --------------------------------------------------------------------------- 42export function middleware(req: NextRequest) { 43 const { pathname } = req.nextUrl 44 45 // Terminal rewrite on homepage 46 if (pathname === '/' && isTerminalClient(req)) { 47 const url = req.nextUrl.clone() 48 url.pathname = '/cli' 49 return NextResponse.rewrite(url) 50 } 51 52 // Rate-limit API routes 53 if (pathname.startsWith('/profile') || pathname === '/search' || pathname === '/trending') { 54 const ip = req.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ?? 'unknown' 55 if (isRateLimited(ip)) { 56 return new NextResponse('Rate limit exceeded. Please slow down.\n', { 57 status: 429, 58 headers: { 59 'Content-Type': 'text/plain; charset=utf-8', 60 'Retry-After': '60', 61 }, 62 }) 63 } 64 } 65 66 return NextResponse.next() 67} 68 69export const config = { 70 matcher: ['/', '/profile/:path*', '/search', '/trending'], 71}