# KOC LOOP KOC 内容分发与数据回收闭环,运行于 vinext、Cloudflare D1 和 R2。 ## Prerequisites - Node.js `>=22.13.0` ## Quick Start ```bash npm install npm run dev npm run build ``` 复制 `.dev.vars.example` 为 `.dev.vars` 并配置运行时变量。飞书动态导入需要: - `FEISHU_APP_ID` - `FEISHU_APP_SECRET` 飞书自建应用需开通电子表格读取、知识库节点读取和云文档素材下载权限, 并将应用添加到目标知识库或电子表格的文档应用中。 This starter does not use `wrangler.jsonc`. ## Included Shape - edit site code under `app/` - `.openai/hosting.json` declares optional Sites D1 and R2 bindings - `vite.config.ts` simulates declared bindings for local development - `db/schema.ts` starts intentionally empty - `examples/d1/` contains an optional D1 example surface - `drizzle.config.ts` supports local migration generation when needed ## Workspace Auth Headers OpenAI workspace sites can read the current user's email from `oai-authenticated-user-email`. SIWC-authenticated workspace sites may also receive `oai-authenticated-user-full-name` when the user's SIWC profile has a non-empty `name` claim. The full-name value is percent-encoded UTF-8 and is accompanied by `oai-authenticated-user-full-name-encoding: percent-encoded-utf-8`. Treat the full name as optional and fall back to email when it is absent: ```tsx import { headers } from "next/headers"; export default async function Home() { const requestHeaders = await headers(); const email = requestHeaders.get("oai-authenticated-user-email"); const encodedFullName = requestHeaders.get("oai-authenticated-user-full-name"); const fullName = encodedFullName && requestHeaders.get("oai-authenticated-user-full-name-encoding") === "percent-encoded-utf-8" ? decodeURIComponent(encodedFullName) : null; const displayName = fullName ?? email; // ... } ``` ## Optional Dispatch-Owned ChatGPT Sign-In Import the ready-to-use helpers from `app/chatgpt-auth.ts` when the site needs optional or required ChatGPT sign-in: - Use `getChatGPTUser()` for optional signed-in UI. - Use `requireChatGPTUser(returnTo)` for server-rendered pages that should send anonymous visitors through Sign in with ChatGPT. - Use `chatGPTSignInPath(returnTo)` and `chatGPTSignOutPath(returnTo)` for browser links or actions. - Pass a same-origin relative `returnTo` path for the destination after sign-in or sign-out. The helper validates and safely encodes it. - Mark protected pages with `export const dynamic = "force-dynamic"` because they depend on per-request identity headers. Dispatch owns `/signin-with-chatgpt`, `/signout-with-chatgpt`, `/callback`, the OAuth cookies, and identity header injection. Do not implement app routes for those reserved paths. Routes that do not import and call the helper remain anonymous-compatible. SIWC establishes identity only; it does not prove workspace membership. Use the Sites hosting platform's access policy controls for workspace-wide restrictions, or enforce explicit server-side membership or allowlist checks. Use SIWC for account pages, user-specific dashboards, saved records, and write actions tied to the current ChatGPT user. Leave public content anonymous. ## Useful Commands - `npm run dev`: start local development - `npm run build`: verify the vinext build output - `npm test`: build the starter and verify its rendered loading skeleton - `npm run db:generate`: generate Drizzle migrations after schema changes ## Learn More - [vinext Documentation](https://github.com/cloudflare/vinext) - [Drizzle D1 Guide](https://orm.drizzle.team/docs/get-started/d1-new)