Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 28 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,26 +178,47 @@ bun run build # → dist/cli.mjs

---

## Web ビューワ (Railway 一発デプロイ)
## Web ビューワ (Cloudflare Workers / Railway 両対応)

CLI で S3 / R2 に push したアーカイブを、ブラウザで閲覧する読取専用 Next.js アプリ。コードは [`web/`](./web/)。
CLI で R2 / S3 に push したアーカイブを、ブラウザで閲覧する読取専用アプリ。コードは [`web/`](./web/)。

[Hono](https://hono.dev) + JSX SSR 単一コードベース → 2 経路でデプロイ可能:

- **Cloudflare Workers** — R2 native binding 直結。認証情報を Worker 内に持たない。bundle gzip 188KB
- **Railway / Node** — S3-compatible env vars 経由 (`@aws-sdk/client-s3` fallback)

### Cloudflare Workers

```bash
cd web
npm install
npx wrangler r2 bucket create cchist
npx wrangler secret put BASIC_AUTH_USER
npx wrangler secret put BASIC_AUTH_PASS
npm run deploy:cf
```

### Railway

[![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/new/template?template=https://github.com/InterfaceX-co-jp/cchist/tree/main/web&referralCode=REPLACE_ME)

- セルフホスト前提 (SaaS 提供なし) — 自分の Railway アカウントで動かす
共通仕様:

- セルフホスト前提 (SaaS 提供なし)
- DB 不要 — bucket をそのまま読む
- HTTP Basic Auth で保護 (`BASIC_AUTH_USER` / `BASIC_AUTH_PASS`)
- 必要な env vars [`web/README.md`](./web/README.md) 参照
- HTTP Basic Auth で保護 (`BASIC_AUTH_USER` / `BASIC_AUTH_PASS`)。未設定なら public
- 詳細 env vars / scripts / アーキテクチャは [`web/README.md`](./web/README.md) 参照

> ボタンの `REPLACE_ME` を Railway の referral code に置換、かつ Railway dashboard で template として登録すると、ワンクリックで env var 入力フォームに遷移デプロイまで進む
> Railway ボタンの `REPLACE_ME` を referral code に置換、Railway dashboard で template 登録するとワンクリックで env var 入力フォームデプロイ

---

## ロードマップ

- [x] `cchist install-hook`: hook 経由のリアルタイム同期 (`Stop` hook → `cchist sync --only local`)
- [ ] API ログ取り込み(SDK ラッパー or OpenTelemetry コレクタ)
- [x] Web UI (検索 / セッション差分 / タイムライン) — [`web/`](./web/)
- [x] Web UI — [`web/`](./web/) (Hono + JSX, Cloudflare Workers / Railway 両対応)
- [ ] Web UI: 検索 / セッション差分 / タイムライン
- [ ] 個別セッションを markdown にエクスポート
- [ ] memory のエクスポート(claude.ai)取り込み

Expand Down
4 changes: 2 additions & 2 deletions web/.gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
node_modules
.next
out
dist
.wrangler
public/styles.css
.env
.env.local
.env*.local
Expand Down
89 changes: 84 additions & 5 deletions web/README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,44 @@
# cchist-web

Read-only web viewer for [cchist](../). Browses Claude Code conversation history stored on S3-compatible object storage (AWS S3, Cloudflare R2, etc.).
Read-only web viewer for [cchist](../). Browses Claude Code conversation history stored on object storage (Cloudflare R2, AWS S3, MinIO, Backblaze B2, …).

Built with [Hono](https://hono.dev) + JSX SSR. Single codebase runs on:
- **Cloudflare Workers** — R2 native binding, no credentials inside Worker
- **Node.js** (Railway, Fly, bare VM) — S3-compatible client via `@aws-sdk/client-s3`

## Deploy on Cloudflare Workers

```bash
cd web
npm install

# Create the R2 bucket (matches `bucket_name` in wrangler.toml)
npx wrangler r2 bucket create cchist

# Optional: protect the viewer with basic auth
npx wrangler secret put BASIC_AUTH_USER
npx wrangler secret put BASIC_AUTH_PASS

npm run deploy:cf
```

The `cchist sync` CLI writes `.jsonl` files into the same R2 bucket — point the CLI at the R2 S3 API and the Worker reads them back via the binding (no credentials needed in-Worker).

### wrangler.toml

- `[assets]` directory: `./public` — serves the Tailwind CSS bundle
- `[[r2_buckets]]` binding: `BUCKET` → bucket name `cchist` (edit to your bucket name)
- `compatibility_flags`: `nodejs_compat` (required for the `@aws-sdk/client-s3` dynamic-import path, even though Workers prefer the R2 binding)

## Deploy on Railway

[![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/new/template?template=https://github.com/InterfaceX-co-jp/cchist/tree/main/web&referralCode=REPLACE_ME)

> Replace `REPLACE_ME` above with your Railway referral code (Account → Referrals). The template URL also needs to be registered as a Railway template via the dashboard for one-click env-var promotion.
> Replace `REPLACE_ME` above with your Railway referral code (Account → Referrals).

Set the S3-style env vars below — the Node entry (`src/node.ts`) reads them and falls back to the `@aws-sdk/client-s3` client.

## Required env vars
### Required env vars (Node / Railway only)

| name | required | example |
|---|---|---|
Expand All @@ -21,19 +51,68 @@ Read-only web viewer for [cchist](../). Browses Claude Code conversation history
| `BASIC_AUTH_USER` | recommended | `viewer` |
| `BASIC_AUTH_PASS` | recommended | (strong password) |

If `BASIC_AUTH_USER` / `BASIC_AUTH_PASS` are unset, the viewer is **public** — anyone with the URL can read your archive. Set both for production.
On Cloudflare Workers, `STORAGE_*` is ignored — the R2 binding handles list/get directly.

If `BASIC_AUTH_USER` / `BASIC_AUTH_PASS` are unset, the viewer is **public** — anyone with the URL can read your archive. Set both for production (use `wrangler secret put` on Workers).

## Local dev

```bash
cd web
npm install
cp .env.example .env.local # then fill in values
npm run dev
npm run dev # Node + tsx watch + Tailwind build
```

Visit http://localhost:3000.

For local Workers dev (R2 binding via miniflare):

```bash
npm run dev:cf
```

## Scripts

| script | purpose |
|---|---|
| `npm run dev` | Node dev server (`tsx watch src/node.ts`) |
| `npm run dev:cf` | Workers dev via `wrangler dev` |
| `npm run build` | Build Tailwind CSS into `public/styles.css` (Railway uses this) |
| `npm run start` | Production Node start (`tsx src/node.ts`) |
| `npm run deploy:cf` | `wrangler deploy` to Cloudflare |
| `npm run typecheck` | `tsc --noEmit` |

## Architecture

```
src/
├── index.tsx # Cloudflare Workers entry
├── node.ts # Node entry (Railway / local)
├── app.tsx # Hono app — shared by both entries
├── components/
│ └── layout.tsx # JSX layout
├── routes/
│ ├── home.tsx # session list
│ └── session.tsx # JSONL viewer
├── middleware/
│ └── basic-auth.ts # opt-in HTTP basic auth
├── lib/
│ ├── storage.ts # R2 binding + @aws-sdk/client-s3 fallback
│ └── jsonl.ts # parse + extract turns
└── styles/
└── input.css # Tailwind v4 source
```

### Storage layer

`src/lib/storage.ts` exposes `listJsonl()` / `getObjectText()` with two backends:

- If `env.BUCKET` (R2 binding) is set → native R2 calls. No credentials, no `@aws-sdk` in hot path.
- Otherwise → dynamic-imports `@aws-sdk/client-s3` and uses `STORAGE_*` env vars.

The dynamic import keeps the Workers bundle slim when the binding is present, and lets the Node build keep full S3 compatibility (AWS, MinIO, B2, etc.).

## How it reads data

The CLI `cchist sync` writes one `.jsonl` per session into your bucket. This app lists those keys and renders each session's user/assistant turns. No database, no writes — strictly read-only.
6 changes: 0 additions & 6 deletions web/app/healthz/route.ts

This file was deleted.

30 changes: 0 additions & 30 deletions web/app/layout.tsx

This file was deleted.

73 changes: 0 additions & 73 deletions web/app/page.tsx

This file was deleted.

92 changes: 0 additions & 92 deletions web/app/session/[id]/page.tsx

This file was deleted.

Loading
Loading