/**
* POST /ds-agent/commands/{command_id}/chunks/{seq} — zip のチャンク(指示書AY 段階7-3。D1・D20・D25 ①・D-179)
*
* 仕様の正は `d-watch-api.yaml` の同パス。★URL の command_id は export の指示の id★。
* ★1チャンク 4MB 以下★(proxy が本体を10MBまでしかバッファせず、超えた分を黙って切り捨てるため)。長さとハッシュを必ず検証する。
* ★204 はブラウザ側に空きができてから(流量制御。最大45秒)★。チャンクはメモリで中継するだけで、サーバーには保存しない。
*/
import { NextResponse } from "next/server";
import { agentCommandPreamble, commandNotFound, jsonError, noContent } from "@/lib/ds-agent/agent-request";
import { getRelay } from "@/lib/ds-agent/relay";
import { ensureDsAgentLogging } from "@/lib/ds-agent/relay-log";
import { parsePathSeq } from "@/lib/ds-logs/validate";
import { withRequestId } from "@/lib/logger";
const SHA256_HEX = /^[0-9a-f]{64}$/;
export async function POST(
request: Request,
{ params }: { params: Promise<{ command_id: string; seq: string }> }
) {
return withRequestId(request, async () => {
ensureDsAgentLogging();
const { command_id, seq: seqRaw } = await params;
const pre = await agentCommandPreamble(request, command_id);
if (!pre.ok) return pre.response;
const seq = parsePathSeq(seqRaw);
if (!seq.ok) return NextResponse.json(seq.error, { status: seq.status });
const sha256 = request.headers.get("x-chunk-sha256");
if (sha256 === null || !SHA256_HEX.test(sha256)) {
return jsonError(400, "chunk_invalid", "X-Chunk-Sha256 の形式が不正です");
}
const bytes = new Uint8Array(await request.arrayBuffer());
switch (await getRelay().pushChunk(pre.serial, pre.commandId, seq.query, bytes, sha256)) {
case "ok":
return noContent();
case "invalid":
return jsonError(400, "chunk_invalid", "チャンクの長さかハッシュが不正です");
case "out_of_order":
return jsonError(409, "chunk_out_of_order", "チャンクの順番が正しくありません");
case "cancelled":
case "gone":
return jsonError(410, "export_cancelled", "エクスポートは取り消されました");
case "not_found":
return commandNotFound();
}
});
}