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 });
}
}