You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

80 lines
2.6 KiB

import { ASCIIDOCTOR_SERVER_URL } from '@/constants'
export type AsciiDoctorExportFormat = 'epub3' | 'epub' | 'pdf' | 'html5' | 'html'
const FORMATS: Record<
AsciiDoctorExportFormat,
{ endpoint: string; mimeType: string; extension: string }
> = {
epub3: { endpoint: 'epub', mimeType: 'application/epub+zip', extension: 'epub' },
epub: { endpoint: 'epub', mimeType: 'application/epub+zip', extension: 'epub' },
pdf: { endpoint: 'pdf', mimeType: 'application/pdf', extension: 'pdf' },
html5: { endpoint: 'html5', mimeType: 'text/html; charset=utf-8', extension: 'html' },
html: { endpoint: 'html5', mimeType: 'text/html; charset=utf-8', extension: 'html' }
}
const CONVERT_TIMEOUT_MS = 120_000
export function isAsciiDoctorServerConfigured(): boolean {
return ASCIIDOCTOR_SERVER_URL.length > 0
}
export function normalizeAsciiDoctorFormat(format: string): AsciiDoctorExportFormat {
const key = format.trim().toLowerCase()
if (key === '' || key === 'epub3') return 'epub3'
if (key in FORMATS) return key as AsciiDoctorExportFormat
throw new Error(`Unsupported export format: ${format}`)
}
export async function convertAsciiDocViaServer(
format: AsciiDoctorExportFormat,
content: string,
title: string,
author: string,
image?: string | null
): Promise<{ blob: Blob; mimeType: string; extension: string }> {
if (!isAsciiDoctorServerConfigured()) {
throw new Error('AsciiDoctor server URL is not configured.')
}
const normalized = normalizeAsciiDoctorFormat(format)
const info = FORMATS[normalized]
const url = `${ASCIIDOCTOR_SERVER_URL.replace(/\/$/, '')}/convert/${info.endpoint}`
const payload: Record<string, string> = {
content,
title: title.trim() || 'Publication',
author: author.trim()
}
const cover = image?.trim()
if (cover) payload.image = cover
const controller = new AbortController()
const timeoutId = window.setTimeout(() => controller.abort(), CONVERT_TIMEOUT_MS)
let response: Response
try {
response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Accept: '*/*' },
body: JSON.stringify(payload),
signal: controller.signal
})
} finally {
window.clearTimeout(timeoutId)
}
if (!response.ok) {
const detail = (await response.text()).slice(0, 400)
throw new Error(
detail ? `AsciiDoctor server returned HTTP ${response.status}: ${detail}` : `AsciiDoctor server returned HTTP ${response.status}`
)
}
const blob = await response.blob()
if (blob.size === 0) {
throw new Error('AsciiDoctor server returned an empty file.')
}
return { blob, mimeType: info.mimeType, extension: info.extension }
}