export const dynamic = 'force-dynamic' export const runtime = 'nodejs' import { NextResponse } from 'next/server' import * as https from 'node:https' const ORGP_HOST = 'transport.orgp.spb.ru' // ORGP TLS chain fails default verification — accept like curl -k for this one host. const insecureAgent = new https.Agent({ rejectUnauthorized: false }) interface UpstreamResult { status: number body: string } function postOrgp(path: string, form: string): Promise { return new Promise((resolve, reject) => { const req = https.request( { host: ORGP_HOST, path, method: 'POST', agent: insecureAgent, timeout: 8000, headers: { 'X-Requested-With': 'XMLHttpRequest', 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'Accept': 'application/json, text/javascript, */*; q=0.01', 'Content-Length': Buffer.byteLength(form).toString(), }, }, (res) => { let data = '' res.setEncoding('utf8') res.on('data', (chunk) => { data += chunk }) res.on('end', () => resolve({ status: res.statusCode || 0, body: data })) } ) req.on('timeout', () => { req.destroy(new Error('upstream timeout')) }) req.on('error', reject) req.write(form) req.end() }) } export async function GET(req: Request) { const { searchParams } = new URL(req.url) const stopId = searchParams.get('stopId') if (!stopId || !/^\d+$/.test(stopId)) { return NextResponse.json({ error: 'stopId required (digits)' }, { status: 400 }) } const form = new URLSearchParams({ sEcho: '1', iColumns: '5', sColumns: 'index,routeNumber,timeToArrive,parkNumber,wheelchair', iDisplayStart: '0', iDisplayLength: '-1', sNames: 'index,routeNumber,timeToArrive,parkNumber,wheelchair', }).toString() try { const { status, body } = await postOrgp( `/Portal/transport/stop/${encodeURIComponent(stopId)}/arriving`, form ) if (status !== 200) { return NextResponse.json( { error: `upstream_${status}`, arrivals: [] }, { status: 502 } ) } const data = JSON.parse(body) const arrivals = Array.isArray(data?.aaData) ? data.aaData.map((row: any[]) => ({ route: String(row[1] ?? ''), minutes: Number(row[2] ?? 0), park: String(row[3] ?? ''), wheelchair: Boolean(row[4]), })) : [] return NextResponse.json({ stopId, arrivals, fetchedAt: new Date().toISOString(), }) } catch (e: any) { return NextResponse.json( { error: e?.message || 'fetch_failed', arrivals: [] }, { status: 502 } ) } }