89 lines
3.4 KiB
Markdown
89 lines
3.4 KiB
Markdown
# web — Disclosure Bureau Next.js app
|
|
|
|
Next.js 15 + React 19 + Tailwind + Supabase + assistant-ui.
|
|
|
|
## Quick start (local dev)
|
|
|
|
```bash
|
|
# 1. Install deps
|
|
npm install
|
|
|
|
# 2. (Optional) Start local Supabase
|
|
# Requires Docker. Skip if pointing at remote Supabase.
|
|
npx supabase init # first time only — creates supabase/ folder
|
|
npx supabase start # spins up Postgres/GoTrue/Storage on :54321
|
|
|
|
# 3. Configure env
|
|
cp .env.local.example .env.local
|
|
# Edit .env.local — paste local Supabase keys (printed by `supabase start`)
|
|
|
|
# 4. Apply migrations
|
|
psql postgresql://postgres:postgres@localhost:54322/postgres \
|
|
-f ../infra/supabase/migrations/0001_chat_schema.sql
|
|
|
|
# 5. Start dev
|
|
npm run dev
|
|
# http://localhost:3030
|
|
```
|
|
|
|
## Without Supabase
|
|
|
|
The app degrades gracefully if Supabase env vars are unset:
|
|
- Wiki browsing works (read-only from filesystem)
|
|
- Auth bar shows "auth: disabled (dev)"
|
|
- Chat bubble shows "Auth not configured"
|
|
|
|
Useful for quick UI work without spinning up Docker.
|
|
|
|
## Production (Coolify on VPS)
|
|
|
|
See [`../infra/coolify/`](../infra/coolify/). Stack:
|
|
|
|
- Coolify orchestrates everything
|
|
- Supabase self-hosted: `db.disclosure.top`, `studio.disclosure.top`
|
|
- Next.js: `disclosure.top`
|
|
- Meilisearch (shared): `search.disclosure.top`
|
|
- Imgproxy (shared): `img.disclosure.top`
|
|
- Caddy: TLS + reverse proxy (built into Coolify)
|
|
|
|
## Architecture
|
|
|
|
```
|
|
app/
|
|
├── page.tsx # home — 116 docs grouped by collection
|
|
├── auth/
|
|
│ ├── signin/page.tsx # magic-link form
|
|
│ ├── callback/route.ts # exchanges code for session
|
|
│ └── signout/route.ts
|
|
├── d/[docId]/
|
|
│ ├── page.tsx # doc detail
|
|
│ └── [page]/page.tsx # page reader (OCR + entity highlights + crops + sidebar PNG)
|
|
├── api/
|
|
│ ├── me/route.ts # GET current profile
|
|
│ ├── sessions/route.ts # GET list, POST new
|
|
│ ├── sessions/[id]/route.ts # GET detail, PATCH, DELETE
|
|
│ ├── sessions/[id]/messages/route.ts # POST send → assistant reply
|
|
│ ├── documents/, pages/, entities/, tables/ # read-only data
|
|
│ └── static/[...path]/route.ts # sandboxed file serve
|
|
components/
|
|
├── chat-bubble.tsx # floating Sherlock — auth-aware, session list
|
|
├── entity-modal.tsx # opens on entity click
|
|
├── reader-content.tsx # OCR + highlights + crops
|
|
└── auth-bar.tsx # sign in / out + budget tracker
|
|
lib/
|
|
├── wiki.ts # markdown reader (gray-matter)
|
|
├── entity-index.ts # match loader + text segmentation
|
|
└── supabase/{server,client}.ts # SSR helpers
|
|
middleware.ts # session refresh on every request
|
|
```
|
|
|
|
## Tech notes
|
|
|
|
- **No RAG**: chat agent reads markdown directly. Wiki-link traversal substitutes for vector search.
|
|
- **RLS-first**: Supabase Row Level Security enforces "user sees only own sessions" at the DB layer.
|
|
- **Magic-link auth**: no passwords. GoTrue handles email delivery.
|
|
- **Anti-abuse**: per-user budget cap (default $5) + daily message quota (default 100) enforced via `check_budget` RPC before each Claude call.
|
|
|
|
## Cost
|
|
|
|
Each chat turn costs ~$0.005-0.05 depending on context size (mostly Haiku $1/M input, $5/M output).
|