Newer
Older
client_dwatch / lib / ds-agent / agent-request.ts
/**
 * 制御盤のログの API のルートが共有する前処理と応答(指示書AY 段階7-3。M12)。
 *
 * ★ルートは薄く保つ★。★ファイルシステム/OS のモジュールを import しない★(D3。段階7-4 の静的な検査の対象)。
 */
import { NextResponse } from "next/server";
import { parsePathCommandId } from "@/lib/ds-logs/validate";
import { requireAgent } from "@/lib/ds-agent/require-agent";
import type { ApiError, ErrorCode } from "@/lib/types";

export function errorBody(
  code: ErrorCode,
  message: string,
  details: Record<string, string[]> | null = null
): ApiError {
  return { error: { code, message, details } };
}

export function jsonError(
  status: number,
  code: ErrorCode,
  message: string,
  details: Record<string, string[]> | null = null
): Response {
  return NextResponse.json(errorBody(code, message, details), { status });
}

export function noContent(): Response {
  return new Response(null, { status: 204 });
}

/**
 * `[command_id]` を持つ制御盤向けの5本の前処理: ★`requireAgent()`(再検証・最終接続日時の更新)→ `command_id` の検証★。
 * 本体はこの後にルートが `request.text()` で読む(JSON でない・オブジェクトでないは 400 `result_invalid`。R43・R44)。
 */
export async function agentCommandPreamble(
  request: Request,
  commandIdRaw: string
): Promise<{ ok: true; serial: string; commandId: string } | { ok: false; response: Response }> {
  const auth = await requireAgent(request);
  if (!auth.ok) return auth;
  const parsed = parsePathCommandId(commandIdRaw);
  if (!parsed.ok) return { ok: false, response: NextResponse.json(parsed.error, { status: parsed.status }) };
  return { ok: true, serial: auth.serial, commandId: parsed.query };
}

/** 制御盤向け: 指示が無い・別の制御盤の指示・種類の違う指示(403 にしない。R35) */
export function commandNotFound(): Response {
  return jsonError(404, "not_found", "指示が見つかりません");
}