import type { OpsErrorDetail } from '@/api/admin/ops' const GENERIC_UPSTREAM_MESSAGES = new Set([ 'upstream request failed', 'upstream request failed after retries', 'upstream gateway error', 'upstream service temporarily unavailable' ]) type ParsedGatewayError = { type: string message: string } function parseGatewayErrorBody(raw: string): ParsedGatewayError | null { const text = String(raw || '').trim() if (!text) return null try { const parsed = JSON.parse(text) as Record const err = parsed?.error as Record | undefined if (!err || typeof err !== 'object') return null const type = typeof err.type === 'string' ? err.type.trim() : '' const message = typeof err.message === 'string' ? err.message.trim() : '' if (!type && !message) return null return { type, message } } catch { return null } } function isGenericGatewayUpstreamError(raw: string): boolean { const parsed = parseGatewayErrorBody(raw) if (!parsed) return false if (parsed.type !== 'upstream_error') return false return GENERIC_UPSTREAM_MESSAGES.has(parsed.message.toLowerCase()) } export function resolveUpstreamPayload( detail: Pick | null | undefined ): string { if (!detail) return '' const candidates = [ detail.upstream_error_detail, detail.upstream_errors, detail.upstream_error_message ] for (const candidate of candidates) { const payload = String(candidate || '').trim() if (!payload) continue // Normalize common "empty but present" JSON placeholders. if (payload === '[]' || payload === '{}' || payload.toLowerCase() === 'null') { continue } return payload } return '' } export function resolvePrimaryResponseBody( detail: OpsErrorDetail | null, errorType?: 'request' | 'upstream' ): string { if (!detail) return '' const upstreamPayload = resolveUpstreamPayload(detail) const errorBody = String(detail.error_body || '').trim() if (errorType === 'upstream') { return upstreamPayload || errorBody } if (!errorBody) { return upstreamPayload } // For request detail modal, keep client-visible body by default. // But if that body is a generic gateway wrapper, show upstream payload first. if (upstreamPayload && isGenericGatewayUpstreamError(errorBody)) { return upstreamPayload } return errorBody }