mirror of
https://github.com/joinmarket-webui/jam.git
synced 2026-08-19 13:18:30 +02:00
fix(logs): add robust log endpoint fallback and html guard
This commit is contained in:
parent
2f01019e60
commit
caa8983dfc
1 changed files with 65 additions and 4 deletions
|
|
@ -3,6 +3,24 @@ export interface AuthApiRequestContext {
|
|||
signal?: AbortSignal
|
||||
}
|
||||
|
||||
export class LogsApiError extends Error {
|
||||
status?: number
|
||||
code?: 'not_supported' | 'request_failed'
|
||||
|
||||
constructor(
|
||||
message: string,
|
||||
options: {
|
||||
status?: number
|
||||
code?: 'not_supported' | 'request_failed'
|
||||
} = {},
|
||||
) {
|
||||
super(message)
|
||||
this.name = 'LogsApiError'
|
||||
this.status = options.status
|
||||
this.code = options.code
|
||||
}
|
||||
}
|
||||
|
||||
const buildAuthHeader = (token: string) => {
|
||||
return { 'x-jm-authorization': `Bearer ${token}` }
|
||||
}
|
||||
|
|
@ -23,6 +41,9 @@ const withExpectedContentTypeOrThrow = (response: Response, expectedContentType:
|
|||
return response
|
||||
}
|
||||
|
||||
const LOG_ENDPOINT_PATHS = ['/api/v0/log', '/jam/api/v0/log'] as const
|
||||
const LOG_FALLBACK_STATUSES = new Set([404, 405, 501])
|
||||
|
||||
/**
|
||||
* Fetch features configuration from the server
|
||||
* @param token - Authentication token
|
||||
|
|
@ -50,8 +71,48 @@ export const fetchLog = async ({
|
|||
}: AuthApiRequestContext & {
|
||||
fileName: string
|
||||
}) => {
|
||||
return await fetch(`/jam/api/v0/log/${fileName}`, {
|
||||
headers: { ...buildAuthHeader(token) },
|
||||
signal,
|
||||
}).then((response) => withExpectedContentTypeOrThrow(response, 'text/plain'))
|
||||
const encodedFileName = encodeURIComponent(fileName)
|
||||
let fallbackError: LogsApiError | undefined
|
||||
|
||||
for (const path of LOG_ENDPOINT_PATHS) {
|
||||
const response = await fetch(`${path}/${encodedFileName}`, {
|
||||
headers: { ...buildAuthHeader(token) },
|
||||
signal,
|
||||
})
|
||||
|
||||
if (response.ok) {
|
||||
const contentType = response.headers.get('content-type')?.toLowerCase() || ''
|
||||
const body = await response.text()
|
||||
const bodyStart = body.trimStart().slice(0, 64).toLowerCase()
|
||||
const isHtmlResponse =
|
||||
contentType.includes('text/html') || bodyStart.startsWith('<!doctype html') || bodyStart.startsWith('<html')
|
||||
|
||||
if (isHtmlResponse) {
|
||||
fallbackError = new LogsApiError('Unexpected HTML response while fetching logs.', {
|
||||
status: response.status,
|
||||
code: 'not_supported',
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
return new Response(body, {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
headers: response.headers,
|
||||
})
|
||||
}
|
||||
|
||||
const requestError = new LogsApiError(`Request failed with status ${response.status}`, {
|
||||
status: response.status,
|
||||
code: LOG_FALLBACK_STATUSES.has(response.status) ? 'not_supported' : 'request_failed',
|
||||
})
|
||||
if (LOG_FALLBACK_STATUSES.has(response.status)) {
|
||||
fallbackError = requestError
|
||||
continue
|
||||
}
|
||||
|
||||
throw requestError
|
||||
}
|
||||
|
||||
throw fallbackError ?? new LogsApiError('Failed to load log file.', { code: 'request_failed' })
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue