返回 CodeWhale
kv.ts
根目录 / web / lib / kv.ts
1 /**
2 * Cloudflare KV access via the OpenNext binding helper.
3 * Falls back to in-memory cache for `next dev` outside of `wrangler dev`.
4 */
5 import type { CuratedDispatch } from "./types";
6
7 const MEM = new Map<string, string>();
8
9 export interface KVNamespace {
10 get(key: string): Promise<string | null>;
11 put(key: string, value: string, opts?: { expirationTtl?: number }): Promise<void>;
12 list(opts?: { prefix?: string; limit?: number }): Promise<{ keys: { name: string }[] }>;
13 delete(key: string): Promise<void>;
14 }
15
16 interface CloudflareEnv {
17 CURATED_KV?: KVNamespace;
18 DEEPSEEK_API_KEY?: string;
19 DEEPSEEK_BASE_URL?: string;
20 DEEPSEEK_MODEL?: string;
21 GITHUB_TOKEN?: string;
22 CRON_SECRET?: string;
23 GITHUB_REPO?: string;
24 }
25
26 function envFromProcess(): CloudflareEnv {
27 return {
28 DEEPSEEK_API_KEY: process.env.DEEPSEEK_API_KEY,
29 DEEPSEEK_BASE_URL: process.env.DEEPSEEK_BASE_URL,
30 DEEPSEEK_MODEL: process.env.DEEPSEEK_MODEL,
31 GITHUB_TOKEN: process.env.GITHUB_TOKEN,
32 CRON_SECRET: process.env.CRON_SECRET,
33 GITHUB_REPO: process.env.GITHUB_REPO,
34 };
35 }
36
37 export async function getEnv(): Promise<CloudflareEnv> {
38 if (process.env.NEXT_PHASE === "phase-production-build") {
39 return envFromProcess();
40 }
41
42 try {
43 const mod = await import("@opennextjs/cloudflare");
44 const ctx = await mod.getCloudflareContext({ async: true });
45 return ctx.env as CloudflareEnv;
46 } catch {
47 return envFromProcess();
48 }
49 }
50
51 export async function getDispatch(): Promise<CuratedDispatch | null> {
52 const env = await getEnv();
53 const raw = env.CURATED_KV ? await env.CURATED_KV.get("dispatch:latest") : MEM.get("dispatch:latest") ?? null;
54 if (!raw) return null;
55 try {
56 return JSON.parse(raw) as CuratedDispatch;
57 } catch {
58 return null;
59 }
60 }
61
62 export async function putDispatch(d: CuratedDispatch): Promise<void> {
63 const env = await getEnv();
64 await putDispatchWithKv(env.CURATED_KV, d);
65 }
66
67 export async function putDispatchWithKv(kv: KVNamespace | undefined, d: CuratedDispatch): Promise<void> {
68 const value = JSON.stringify(d);
69 if (kv) {
70 await kv.put("dispatch:latest", value, { expirationTtl: 60 * 60 * 24 * 7 });
71 } else {
72 MEM.set("dispatch:latest", value);
73 }
74 }
75
75 lines TYPESCRIPT