Files
koc-loop/worker/index.ts
2026-07-30 12:06:41 +08:00

86 lines
2.8 KiB
TypeScript

/** Cloudflare Worker entry point for the vinext-starter template. */
import { handleImageOptimization, DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES } from "vinext/server/image-optimization";
import handler from "vinext/server/app-router-entry";
import { backfillAccountProfiles } from "../lib/account-enrichment-service";
import { ensureSchema } from "../lib/mvp-db";
import { runScheduledCollections } from "../lib/collection-service";
import {
resolveCollectionMcpConfig,
type CollectionMcpBindings,
} from "../lib/mcp-collection-client";
import type { FeishuBindings } from "../lib/feishu-client";
interface Env extends CollectionMcpBindings, FeishuBindings {
ASSETS: Fetcher;
DB: D1Database;
UPLOADS: R2Bucket;
IMAGES: {
input(stream: ReadableStream): {
transform(options: Record<string, unknown>): {
output(options: { format: string; quality: number }): Promise<{ response(): Response }>;
};
};
};
}
interface ExecutionContext {
waitUntil(promise: Promise<unknown>): void;
passThroughOnException(): void;
}
interface ScheduledController {
scheduledTime: number;
cron: string;
noRetry(): void;
}
// Image security config. SVG sources with .svg extension auto-skip the
// optimization endpoint on the client side (served directly, no proxy).
// To route SVGs through the optimizer (with security headers), set
// dangerouslyAllowSVG: true in next.config.js and uncomment below:
// const imageConfig: ImageConfig = { dangerouslyAllowSVG: true };
const worker = {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const url = new URL(request.url);
if (url.pathname === "/_vinext/image") {
const allowedWidths = [...DEFAULT_DEVICE_SIZES, ...DEFAULT_IMAGE_SIZES];
return handleImageOptimization(request, {
fetchAsset: (path) => env.ASSETS.fetch(new Request(new URL(path, request.url))),
transformImage: async (body, { width, format, quality }) => {
const result = await env.IMAGES.input(body).transform(width > 0 ? { width } : {}).output({ format, quality });
return result.response();
},
}, allowedWidths);
}
return handler.fetch(request, env, ctx);
},
async scheduled(
controller: ScheduledController,
env: Env,
ctx: ExecutionContext,
) {
ctx.waitUntil(
(async () => {
await ensureSchema(env.DB);
const mcpConfig = resolveCollectionMcpConfig(env);
await runScheduledCollections(
env.DB,
controller.scheduledTime,
mcpConfig,
);
const accountBackfill = await backfillAccountProfiles(
env.DB,
mcpConfig,
10,
);
console.info("KOC scheduled account backfill completed", accountBackfill);
})(),
);
},
};
export default worker;