import { getRuntimeEnv } from "../../../lib/runtime-env"; import { ensureSchema, getRawDb, getUploadBucket, } from "../../../lib/mvp-db"; import { downloadFeishuMedia, type FeishuBindings, } from "../../../lib/feishu-client"; import { partnerOptions, withPartnerCors, } from "../../../lib/partner-cors"; import { parseResultScreenshotKeys } from "../../../lib/result-screenshots"; const env = getRuntimeEnv(); type StoredAsset = { index: number; key: string; fileToken?: string; }; function textValue(value: string | null, maxLength = 100) { return String(value ?? "").trim().slice(0, maxLength); } function findAsset(value: string, imageIndex: number) { try { const assets = JSON.parse(value) as StoredAsset[]; return Array.isArray(assets) ? assets.find( (asset) => asset.index === imageIndex && typeof asset.key === "string" && (asset.key.startsWith("content-assets/") || asset.key.startsWith("task-assets/")) && (asset.fileToken === undefined || typeof asset.fileToken === "string"), ) : undefined; } catch { return undefined; } } async function handleGet(request: Request) { try { await ensureSchema(); const url = new URL(request.url); const taskToken = textValue(url.searchParams.get("task")); const claimToken = textValue(url.searchParams.get("claim")); const delegationToken = textValue(url.searchParams.get("share")); const distributionId = textValue(url.searchParams.get("distribution")); const imageIndex = Number(url.searchParams.get("index")); const imageKind = textValue(url.searchParams.get("kind"), 20); if ( (!delegationToken && (!taskToken || !claimToken)) || !distributionId || !Number.isInteger(imageIndex) || imageIndex < 1 ) { return Response.json({ error: "图片链接不完整" }, { status: 400 }); } const row = delegationToken ? await getRawDb() .prepare( `SELECT c.image_assets, d.result_screenshot_key, d.publish_screenshot_key, d.screenshot_key FROM distributions d JOIN contents c ON c.id = d.content_id JOIN delegation_bundles b ON b.id = d.delegation_bundle_id WHERE d.id = ? AND b.share_token = ? AND b.status = 'active'`, ) .bind(distributionId, delegationToken) .first<{ image_assets: string; result_screenshot_key: string | null; publish_screenshot_key: string | null; screenshot_key: string | null; }>() : await getRawDb() .prepare( `SELECT c.image_assets, d.result_screenshot_key, d.publish_screenshot_key, d.screenshot_key FROM distributions d JOIN contents c ON c.id = d.content_id JOIN claims cl ON cl.id = d.claim_id JOIN tasks t ON t.id = d.task_id WHERE d.id = ? AND cl.claim_token = ? AND t.share_token = ? AND cl.task_id = t.id`, ) .bind(distributionId, claimToken, taskToken) .first<{ image_assets: string; result_screenshot_key: string | null; publish_screenshot_key: string | null; screenshot_key: string | null; }>(); const asset = imageKind === "result" ? row ? { index: imageIndex, key: parseResultScreenshotKeys(row.result_screenshot_key)[ imageIndex - 1 ], } : undefined : imageKind === "publish" ? row?.publish_screenshot_key?.startsWith("publish-evidence/") ? { index: 1, key: row.publish_screenshot_key } : undefined : imageKind === "creator" ? row?.screenshot_key?.startsWith("creator-center/") ? { index: 1, key: row.screenshot_key } : undefined : row ? findAsset(row.image_assets, imageIndex) : undefined; if (!asset?.key) { return Response.json({ error: "没有找到这张图片" }, { status: 404 }); } const bucket = getUploadBucket(); let object = await bucket.get(asset.key); if (!object && asset.fileToken) { const media = await downloadFeishuMedia( asset.fileToken, env as unknown as FeishuBindings, ); await bucket.put(asset.key, media.bytes, { httpMetadata: { contentType: media.contentType }, customMetadata: { source: "feishu-api" }, }); object = await bucket.get(asset.key); } if (!object) { return Response.json({ error: "图片同步失败,请稍后重试" }, { status: 404 }); } const headers = new Headers(); object.writeHttpMetadata(headers); headers.set("Cache-Control", "private, max-age=3600"); headers.set("Content-Disposition", `inline; filename="note-${imageIndex}"`); return new Response(await object.arrayBuffer(), { headers }); } catch (error) { return Response.json( { error: error instanceof Error ? error.message : "图片读取失败" }, { status: 500 }, ); } } export async function GET(request: Request) { return withPartnerCors(request, await handleGet(request)); } export async function OPTIONS(request: Request) { return partnerOptions(request); }