Files
wehub-resource-sync d68f003000
CI / Change detection (push) Has been cancelled
CI / Version drift (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Workflow RLM cache (push) Has been cancelled
CI / Test (macos-latest) (push) Has been cancelled
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / npm wrapper smoke (push) Has been cancelled
CI / Mobile runtime smoke (push) Has been cancelled
CI / Workflow lint (push) Has been cancelled
CI / Documentation (push) Has been cancelled
Web Frontend / Lint & Type Check (push) Failing after 1s
Auto-close harvested PRs / close (push) Failing after 1s
cargo-deny / cargo-deny (bans licenses sources) (push) Failing after 1s
Security audit / cargo-audit (push) Failing after 1s
Sync to CNB / sync (push) Failing after 1s
cargo-deny / cargo-deny (advisories) (push) Failing after 3s
Web Frontend / Deploy to Cloudflare (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:08:23 +08:00

145 lines
8.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# codewhale-web
Community site for [CodeWhale](https://github.com/Hmbown/CodeWhale) — lives at **codewhale.net**.
Next.js 15 (App Router) + Tailwind, deployed to Cloudflare Workers via [`@opennextjs/cloudflare`](https://opennext.js.org/cloudflare). Curated "Today's Dispatch" content is regenerated every 6 hours by a Cloudflare Cron Trigger that calls `deepseek-v4-flash` to summarise recent repo activity, and stored in Workers KV.
## Local dev
```bash
cd web
npm install
cp .env.example .env.local # fill in the keys you have
npm run dev # http://localhost:3000
```
Env (mirrors `.env.example`):
| Variable | What | Required? |
| --------------------------- | ---------------------------------------------------------------- | -------------------- |
| `DEEPSEEK_API_KEY` | DeepSeek platform key (`sk-...`) | only for the `/api/cron` tasks (summarization + community agent) |
| `GITHUB_TOKEN` | Fine-grained PAT, public-repo read scope | optional (raises rate limit 60 → 5000 req/h) |
| `GITHUB_REPO` | Defaults to `Hmbown/CodeWhale` | optional |
| `CRON_SECRET` | Shared secret for manual `/api/cron` invocation | optional (Cloudflare cron triggers don't need it) |
| `DEEPSEEK_MODEL` | Defaults to `deepseek-v4-flash` | optional |
| `DEEPSEEK_BASE_URL` | Defaults to `https://api.deepseek.com` | optional |
| `MAINTAINER_TOKEN` | Admin panel auth; access `/admin?token=<value>` | only for `/admin` |
| `MAINTAINER_GITHUB_PAT` | PAT with `issues:write`, for posting comments via `/admin` | only for `/admin` posting |
| `NEXT_PUBLIC_GITEE_ENABLED` | Set to `1` once the Gitee mirror exists; blank hides Gitee links | optional |
The site renders fine without any of them — `Today's Dispatch` falls back to a static editorial; the GitHub feed shows "feed not yet loaded".
## Deploy to Cloudflare
You already own `codewhale.net` on Cloudflare and have a Workers Paid plan. The deploy is two steps:
1. **Provision KV namespaces once:**
```bash
npx wrangler kv namespace create CURATED_KV
npx wrangler kv namespace create NEXT_INC_CACHE_KV
```
Copy the printed `id` values into the matching `wrangler.jsonc` bindings
(replace each `REPLACE_WITH_KV_ID`).
2. **Set secrets and deploy:**
```bash
npx wrangler secret put DEEPSEEK_API_KEY
npx wrangler secret put GITHUB_TOKEN # optional
npx wrangler secret put CRON_SECRET # optional, for manual /api/cron?task=curate hits
npm run deploy # builds with OpenNext + uploads
```
3. **Point the domain:** in the Cloudflare dashboard, add a Worker route for `codewhale.net/*` → the deployed Worker, named `codewhale-web` (see `wrangler.jsonc`).
The first cron run happens within 6 hours; you can also kick it manually:
```bash
curl -H "x-cron-secret: $CRON_SECRET" "https://codewhale.net/api/cron?task=curate"
```
## What's where
Pages are bilingual: each `app/[locale]/` page renders both English and
Chinese from the same file, keyed by the `[locale]` segment (`en` / `zh`,
see `lib/i18n/config.ts`). Copy changes must update both locales.
```
web/
├── app/
│ ├── globals.css design system: paper grain, hairlines, type, seal
│ ├── [locale]/ en / zh — every page is bilingual
│ │ ├── layout.tsx root + locale layout: html shell, fonts, nav, footer
│ │ ├── page.tsx home — hero, dispatch, stats, how-it-works, join
│ │ ├── install/page.tsx per-OS install with auto-detection
│ │ ├── docs/page.tsx modes / tools / approval / config / mcp / providers
│ │ ├── faq/page.tsx frequently asked questions
│ │ ├── feed/page.tsx live mirror of issues + PRs
│ │ ├── roadmap/page.tsx shipped / underway / considered / ruled out
│ │ ├── contribute/page.tsx how to PR + house rules + dev loop
│ │ └── admin/ maintainer panel (page.tsx + admin-client.tsx)
│ └── api/
│ ├── cron/route.ts cron tasks: curate, triage, facts-drift, …
│ ├── github/feed/route.ts cached JSON endpoint
│ └── admin/ login, logout, post (MAINTAINER_TOKEN-gated)
├── components/
│ ├── nav.tsx sticky header w/ date strip + CJK accents
│ ├── footer.tsx dense 5-column footer
│ ├── seal.tsx red Chinese-seal mark used as section anchor
│ ├── ticker.tsx animated live activity strip
│ ├── stat-grid.tsx tabular repo stats row
│ ├── feed-card.tsx one issue/PR card
│ ├── locale-switcher.tsx EN ↔ ZH toggle
│ └── install-*.tsx install page blocks (binary, code block, tiles)
├── lib/
│ ├── types.ts shared types
│ ├── i18n/ locale config, en/zh dictionaries
│ ├── github.ts REST client + relative-time formatter
│ ├── deepseek.ts v4-flash chat client + curate() prompt
│ ├── facts.ts getFacts(): KV value, else build-time FACTS
│ ├── facts.generated.ts GENERATED — do not edit by hand
│ ├── facts-drift.ts runtime re-derivation for the drift cron
│ ├── community-agent.ts triage / pr-review / digest cron tasks
│ └── kv.ts Cloudflare KV access via OpenNext bindings
├── scripts/
│ ├── derive-facts.mjs prebuild: repo sources → lib/facts.generated.ts
│ └── check-kv-id.mjs predeploy guard for KV namespace ids
├── wrangler.jsonc CF Worker config + cron + KV binding
├── open-next.config.ts OpenNext adapter config
└── tailwind.config.ts design tokens
```
## Facts pipeline
Mechanical facts (version, provider list, sandbox backends, crate names,
default model, Node engines) are never hand-written into pages:
1. **Build time** — `scripts/derive-facts.mjs` runs as `prebuild` (and before
`npm run dev`), parses the parent repo (`Cargo.toml`, `crates/tui/src/config.rs`,
`crates/tui/src/sandbox/`, `npm/codewhale/package.json`) and writes
`lib/facts.generated.ts`. Never edit that file by hand.
2. **Runtime** — the `/api/cron?task=facts-drift` cron (`lib/facts-drift.ts`)
re-derives the same facts from `raw.githubusercontent.com` on a schedule and
writes changes to `CURATED_KV` under `facts:current`. Pages call
`getFacts()` (`lib/facts.ts`), which prefers the KV value over the
build-time constant — so a version bump or new provider self-corrects
within one cron tick, without a redeploy.
When a new `ApiProvider` variant lands in `crates/tui/src/config.rs`, it must
be added to the `labelMap` in **both** `scripts/derive-facts.mjs` and
`lib/facts-drift.ts` (or to the `EXCLUDED` set if deliberately hidden). Both
fail loudly on unmapped variants, so the build / cron will tell you.
## Aesthetic
"Yamen tech": Qing memorial document × WeChat news feed × Bloomberg terminal.
- **Palette**: white paper `#FFFFFF`, ink `#0E0E10`, indigo `#4D6BFE`, aged ochre, jade green, cobalt blue.
- **Type**: Fraunces (display), IBM Plex Sans (body), JetBrains Mono (UI/code), Noto Serif SC (decorative CJK anchors).
- **Structure**: hairline 1px dividers, multi-column grids, big tabular numbers, surgical use of red for "hot" markers, decorative Chinese-seal squares as section anchors.
If you want to retune the palette, edit `:root` in `app/globals.css` and the `colors` block in `tailwind.config.ts`.