import type { NextApiResponse } from 'next' import type { ProxyUploadResponse } from './types' export function handleProxyResponse(params: { res: NextApiResponse; response: ProxyUploadResponse; targetEndpoint: string }): void { if (params.response.statusCode < 200 || params.response.statusCode >= 300) { respondNonOk({ res: params.res, response: params.response, targetEndpoint: params.targetEndpoint }) return } if (isHtmlResponse(params.response.body)) { respondHtml({ res: params.res, response: params.response, targetEndpoint: params.targetEndpoint }) return } const parseResult = parseJsonSafe(params.response.body) if (!parseResult.ok) { console.error('NIP-95 proxy JSON parse error:', { targetEndpoint: params.targetEndpoint, bodyPreview: params.response.body.substring(0, 100) }) params.res.status(500).json({ error: 'Invalid upload response: Invalid JSON response. The endpoint may not be a valid NIP-95 upload endpoint.' }) return } params.res.status(200).json(parseResult.value) } function respondNonOk(params: { res: NextApiResponse; response: ProxyUploadResponse; targetEndpoint: string }): void { const errorText = params.response.body.substring(0, 200) console.error('NIP-95 proxy response error:', { targetEndpoint: params.targetEndpoint, finalUrl: params.response.finalUrl, status: params.response.statusCode, statusText: params.response.statusMessage, errorText, }) params.res.status(params.response.statusCode).json({ error: buildUserFriendlyHttpError(params.response.statusCode, params.response.statusMessage, errorText) }) } function buildUserFriendlyHttpError(statusCode: number, statusMessage: string, errorText: string): string { if (statusCode === 401) { return 'Authentication required. This endpoint requires authorization headers.' } if (statusCode === 403) { return 'Access forbidden. This endpoint may require authentication or have restrictions.' } if (statusCode === 405) { return 'Method not allowed. This endpoint may not support POST requests or the URL may be incorrect.' } if (statusCode === 413) { return 'File too large. The file exceeds the maximum size allowed by this endpoint.' } if (statusCode >= 500) { return `Server error (${statusCode}). The endpoint server encountered an error.` } return errorText || `Upload failed: ${statusCode} ${statusMessage}` } export function isHtmlResponse(body: string): boolean { const trimmedBody = body.trim() return trimmedBody.startsWith(']*>([^<]+)<\/title>/i)?.[1] } function readHtmlH1(body: string): string | undefined { return body.match(/]*>([^<]+)<\/h1>/i)?.[1] } function detectHtmlErrorFlags(params: { body: string; title: string | undefined }): { is404: boolean; is403: boolean; is500: boolean } { const is404 = isHtmlErrorForCode({ body: params.body, title: params.title, code: '404', marker: 'Not Found' }) const is403 = isHtmlErrorForCode({ body: params.body, title: params.title, code: '403', marker: 'Forbidden' }) const is500 = isHtmlErrorForCode({ body: params.body, title: params.title, code: '500', marker: 'Internal Server Error' }) return { is404, is403, is500 } } function isHtmlErrorForCode(params: { body: string; title: string | undefined; code: string; marker: string }): boolean { const titleHasCode = params.title?.includes(params.code) === true return params.body.includes(params.code) || params.body.includes(params.marker) || titleHasCode } function buildHtmlErrorSuggestion(params: { is404: boolean; is403: boolean; is500: boolean }): string { if (params.is404) { return 'The endpoint URL may be incorrect or the endpoint does not exist' } if (params.is403) { return 'The endpoint may require authentication or have restrictions' } if (params.is500) { return 'The endpoint server encountered an internal error' } return 'The endpoint returned HTML instead of JSON; verify URL and required headers' }