File size: 2,470 Bytes
8059bf0 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | 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<string, any>
const err = parsed?.error as Record<string, any> | 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<OpsErrorDetail, 'upstream_error_detail' | 'upstream_errors' | 'upstream_error_message'> | 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
}
|