fix(toutiao): fail publish when body images fail to upload
Route content/cover images through the desktop media-image asset pipeline and abort before submitting when any body image was not replaced with a Toutiao-hosted URL, so we don't publish articles that reference our own private asset endpoints. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -60,7 +60,7 @@ export interface ToutiaoPublishTransport {
|
||||
uploadHtmlImages(
|
||||
html: string,
|
||||
uploader: (sourceUrl: string) => Promise<string | null>,
|
||||
): Promise<{ html: string }>
|
||||
): Promise<{ html: string; uploaded?: Map<string, string> }>
|
||||
reportProgress?(stage: 'media_info' | 'upload_content_images' | 'upload_cover' | 'submit'): void
|
||||
}
|
||||
|
||||
@@ -77,10 +77,25 @@ export type ToutiaoPublishResult =
|
||||
| {
|
||||
success: false
|
||||
status: 'failed'
|
||||
code: 'toutiaohao_not_logged_in' | 'article_content_empty' | 'toutiaohao_publish_failed'
|
||||
code:
|
||||
| 'toutiaohao_not_logged_in'
|
||||
| 'article_content_empty'
|
||||
| 'toutiaohao_image_upload_failed'
|
||||
| 'toutiaohao_publish_failed'
|
||||
message: string
|
||||
}
|
||||
|
||||
function extractHtmlImageSources(html: string): string[] {
|
||||
const sources = new Set<string>()
|
||||
for (const match of html.matchAll(/<img\b[^>]*src=(['"])(.*?)\1[^>]*>/gi)) {
|
||||
const source = match[2]?.trim()
|
||||
if (source) {
|
||||
sources.add(source)
|
||||
}
|
||||
}
|
||||
return [...sources]
|
||||
}
|
||||
|
||||
export async function fetchToutiaoMediaSnapshot(
|
||||
fetchJson: <T>(input: string, init?: RequestInit) => Promise<T>,
|
||||
): Promise<ToutiaoMediaSnapshot | null> {
|
||||
@@ -246,6 +261,19 @@ export async function publishToutiaoArticle(
|
||||
const processed = await transport.uploadHtmlImages(normalizedHtml, async (sourceUrl) =>
|
||||
uploadContentImage(transport, sourceUrl),
|
||||
)
|
||||
const contentImageSources = extractHtmlImageSources(normalizedHtml)
|
||||
const uploadedSources = processed.uploaded ?? new Map<string, string>()
|
||||
const missingUpload = contentImageSources.find(
|
||||
(source) => !uploadedSources.has(source) && processed.html.includes(source),
|
||||
)
|
||||
if (missingUpload) {
|
||||
return {
|
||||
success: false,
|
||||
status: 'failed',
|
||||
code: 'toutiaohao_image_upload_failed',
|
||||
message: `toutiaohao_image_upload_failed: ${missingUpload}`,
|
||||
}
|
||||
}
|
||||
|
||||
transport.reportProgress?.('upload_cover')
|
||||
const cover = input.coverAssetUrl?.trim()
|
||||
|
||||
Reference in New Issue
Block a user