← GPT-AI-LP
CODE · 2.8 KB

api/generate-page.js

Workspace snapshot · 08/31 23:51

import {
  buildLpPrompt,
  parseBriefResult,
  parseHeadlineResult,
  parseLpResult,
  parseQualityGate,
  parseToneResult,
} from "../src/runtime-prompt.js";
import { environment, rpc, SERVICE_ID } from "./runtime.js";
import { authorizeRequest, publicError } from "./security.js";
export const config = { maxDuration: 30 };
export default async function handler(req, res) {
  const auth = authorizeRequest(req, process.env, Date.now(), { rateLimit: req.method === "POST" });
  if (!auth.ok) return res.status(auth.status).json({ error: auth.error });
  const runtime = environment();
  if (!runtime.connected)
    return res.status(503).json({ error: "VPS GPT基盤が未接続です" });
  try {
    if (req.method === "POST") {
      const kind = ["brief", "structure", "wireframe", "tone"].includes(
        req.body?.kind,
      )
        ? req.body.kind
        : "structure";
      const jobId = await rpc("avs_llm_public_enqueue_strategy_job", {
        p_service_id: SERVICE_ID,
        p_token: runtime.token,
        p_request_id: /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu.test(String(req.body?.requestId || ""))
          ? req.body.requestId
          : crypto.randomUUID(),
        p_prompt: buildLpPrompt({ ...req.body, kind }),
      });
      return res.status(202).json({ jobId, kind });
    }
    if (req.method === "GET") {
      const jobId = String(req.query?.jobId || ""),
        kind = ["brief", "tone"].includes(req.query?.kind)
          ? req.query.kind
          : "structure";
      const parserOptions = { allowPartial: req.query?.partial === "1" };
      if (!/^[0-9a-f-]{36}$/iu.test(jobId))
        return res.status(400).json({ error: "jobIdが不正です" });
      const job = await rpc("avs_llm_public_get_job", {
        p_service_id: SERVICE_ID,
        p_token: runtime.token,
        p_job_id: jobId,
      });
      if (job.status === "COMPLETED")
        return res.status(200).json(
          kind === "brief"
            ? { ...job, brief: parseBriefResult(job.output?.text) }
            : kind === "tone"
              ? { ...job, directions: parseToneResult(job.output?.text, parserOptions) }
              : {
                  ...job,
                  sections: parseLpResult(job.output?.text, parserOptions),
                  headlines: parseHeadlineResult(job.output?.text, parserOptions),
                  qualityGate: parseQualityGate(job.output?.text),
                },
        );
      return res.status(200).json(job);
    }
    return res.status(405).json({ error: "GETまたはPOSTのみ利用できます" });
  } catch (error) {
    console.error("[generate-page]", {
      status: error?.status,
      code: String(error?.message || "GENERATION_FAILED").slice(0, 180),
    });
    const mapped = publicError(error);
    return res.status(mapped.status).json({ error: mapped.error });
  }
}