feat: add cloudflare workers deployment

This commit is contained in:
Codex
2026-06-21 23:43:02 +08:00
parent 2fc34979f4
commit 44e9e04533
35 changed files with 8343 additions and 121 deletions
@@ -1,11 +1,7 @@
import { NextResponse } from "next/server";
import {
createBrandTemplate,
getArticleJob,
saveFactCard,
updateArticleJob,
} from "../../../../../lib/db/repositories";
import { requireApiAccess } from "../../../../../lib/api/auth";
import { getRepositoryFromRuntime } from "../../../../../lib/db/repository";
import { confirmedFactCardSchema } from "../../../../../lib/domain/validation";
interface RouteContext {
@@ -13,16 +9,25 @@ interface RouteContext {
}
export async function POST(request: Request, context: RouteContext) {
const access = requireApiAccess(request);
if (!access.ok) {
return access.response;
}
const { jobId } = await context.params;
const job = getArticleJob(undefined, jobId);
const repository = getRepositoryFromRuntime();
const job = await repository.getArticleJob(jobId);
if (!job) {
return NextResponse.json({ error: "Job not found" }, { status: 404 });
}
try {
const factCard = confirmedFactCardSchema.parse(await request.json());
const brandTemplate = createBrandTemplate(undefined, {
brand_name: factCard.brand_names[0] ?? factCard.company_short_names[0] ?? factCard.company_full_name,
const brandTemplate = await repository.createBrandTemplate({
brand_name:
factCard.brand_names[0] ??
factCard.company_short_names[0] ??
factCard.company_full_name,
company_full_name: factCard.company_full_name,
company_short_names: factCard.company_short_names,
product_names: factCard.product_names,
@@ -35,8 +40,8 @@ export async function POST(request: Request, context: RouteContext) {
media_article: "objective third-party voice",
},
});
const savedFactCard = saveFactCard(undefined, jobId, factCard);
updateArticleJob(undefined, jobId, {
const savedFactCard = await repository.saveFactCard(jobId, factCard);
await repository.updateArticleJob(jobId, {
brand_template_id: brandTemplate.id,
status: "fact_confirmed",
});
@@ -1,37 +1,24 @@
import { existsSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { NextResponse } from "next/server";
import { getAppDataDir } from "../../../../../../lib/db/connection";
const CONTENT_TYPES: Record<string, string> = {
"optimized.md": "text/markdown; charset=utf-8",
"optimized.docx":
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"qa_report.json": "application/json; charset=utf-8",
};
import { requireApiAccess } from "../../../../../../lib/api/auth";
import { getExportStoreFromRuntime } from "../../../../../../lib/workflow/export-store";
interface RouteContext {
params: Promise<{ jobId: string; fileName: string }>;
}
export async function GET(_request: Request, context: RouteContext) {
export async function GET(request: Request, context: RouteContext) {
const access = requireApiAccess(request);
if (!access.ok) {
return access.response;
}
const { jobId, fileName } = await context.params;
const contentType = CONTENT_TYPES[fileName];
if (!contentType) {
const exportStore = getExportStoreFromRuntime();
const response = await exportStore.readJobExport(jobId, fileName);
if (!response) {
return NextResponse.json({ error: "Export file not found" }, { status: 404 });
}
const path = join(getAppDataDir(), "exports", jobId, fileName);
if (!existsSync(path)) {
return NextResponse.json({ error: "Export file not found" }, { status: 404 });
}
return new Response(readFileSync(path), {
headers: {
"content-type": contentType,
"content-disposition": `attachment; filename="${fileName}"`,
},
});
return response;
}
+17 -16
View File
@@ -1,27 +1,28 @@
import { NextResponse } from "next/server";
import {
getArticleJob,
getFactCard,
saveOptimizedArticle,
saveQaReport,
updateArticleJob,
} from "../../../../../lib/db/repositories";
import { writeJobExports } from "../../../../../lib/workflow/exporter";
import { requireApiAccess } from "../../../../../lib/api/auth";
import { getRepositoryFromRuntime } from "../../../../../lib/db/repository";
import { getExportStoreFromRuntime } from "../../../../../lib/workflow/export-store";
import { runOptimizationWorkflow } from "../../../../../lib/workflow/orchestrator";
interface RouteContext {
params: Promise<{ jobId: string }>;
}
export async function POST(_request: Request, context: RouteContext) {
export async function POST(request: Request, context: RouteContext) {
const access = requireApiAccess(request);
if (!access.ok) {
return access.response;
}
const { jobId } = await context.params;
const job = getArticleJob(undefined, jobId);
const repository = getRepositoryFromRuntime();
const job = await repository.getArticleJob(jobId);
if (!job) {
return NextResponse.json({ error: "Job not found" }, { status: 404 });
}
const factCardRecord = getFactCard(undefined, jobId);
const factCardRecord = await repository.getFactCard(jobId);
if (!factCardRecord) {
return NextResponse.json(
{ error: "Confirm the fact card before optimizing" },
@@ -39,22 +40,22 @@ export async function POST(_request: Request, context: RouteContext) {
},
factCard: factCardRecord,
});
const optimizedArticle = saveOptimizedArticle(undefined, jobId, result.article);
const qaReport = saveQaReport(
undefined,
const optimizedArticle = await repository.saveOptimizedArticle(jobId, result.article);
const qaReport = await repository.saveQaReport(
jobId,
optimizedArticle.revision ?? 1,
result.qaReport,
);
const exportStore = getExportStoreFromRuntime();
const exportPaths =
qaReport.overall_status === "fail"
? {}
: await writeJobExports({
: await exportStore.writeJobExports({
jobId,
article: optimizedArticle,
qaReport,
});
updateArticleJob(undefined, jobId, {
await repository.updateArticleJob(jobId, {
status: qaReport.overall_status === "fail" ? "qa_failed" : "optimized",
export_paths: exportPaths,
});
+11 -4
View File
@@ -1,14 +1,21 @@
import { NextResponse } from "next/server";
import { createArticleJob } from "../../../lib/db/repositories";
import { requireApiAccess } from "../../../lib/api/auth";
import { getRepositoryFromRuntime } from "../../../lib/db/repository";
import { extractCandidateFactCard } from "../../../lib/workflow/fact-extractor";
import { normalizeInput } from "../../../lib/workflow/input-normalizer";
import { normalizeInput, type RawArticleInput } from "../../../lib/workflow/input-normalizer";
export async function POST(request: Request) {
const access = requireApiAccess(request);
if (!access.ok) {
return access.response;
}
try {
const payload = await request.json();
const payload = (await request.json()) as RawArticleInput;
const normalized = normalizeInput(payload);
const job = createArticleJob(undefined, {
const repository = getRepositoryFromRuntime();
const job = await repository.createArticleJob({
source_title: normalized.articleInput.title,
source_body: normalized.articleInput.body,
image_inputs: normalized.articleInput.images,