4.7 KiB
4.7 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Commands
# Start all services (rebuilds images)
make up # docker compose up --build
# Run standalone (requires env vars)
make dev-backend # cd backend && go run ./cmd/server
make dev-frontend # cd frontend && npm run dev (proxies /api → :8080)
# Build
cd backend && go build ./cmd/server
cd frontend && npm run build # tsc -b && vite build
# Lint / type-check
cd frontend && npm run lint # eslint
cd backend && go vet ./...
# Tests (no test files exist yet)
cd backend && go test ./...
# Production release
./build-push.sh v1.0.0 # builds and pushes all three images to Gitea registry
docker compose -f docker-compose.prod.yml up -d
Architecture
Three Docker services behind nginx:
Browser → nginx:80
├── serves React SPA (static build)
└── proxies /api/* → backend:8080
backend (Go/Gin) → PostgreSQL:5432
→ scraper-service:3001 (Bloomberg only)
→ ollama:11434 (optional)
scraper-service (Node.js/Puppeteer) — headless Chromium for Bloomberg login
Backend layout
cmd/server/main.go— entry point, wires all dependenciesinternal/models/— all DB structs (models.go) and every SQL query (repository.go)internal/api/router.go— all Gin routes;handlers/has one file per domaininternal/ai/— AI providers, two-pass pipeline, async report managerinternal/scraper/— scraper interface, registry, per-source Go implementationsinternal/scheduler/—robfig/cronv3 scheduler that runs scrape → summarize on each tickinternal/database/migrations/— numbered SQL migrations (auto-applied at startup via golang-migrate)
Frontend layout
src/api/client.ts— fetch wrapper: readstokenfromlocalStorage, setsAuthorization: Bearer, auto-redirects on 401, unwraps{"data": ...}envelopessrc/lib/auth.tsx—AuthProvider/useAuthReact context withlogin()/logout()src/lib/router.tsx—createBrowserRouter; authenticated pages nested underAppLayout; admin pages under/admin- UI: Radix UI primitives + Tailwind CSS + CVA; path alias
@→src/
AI pipeline
- Filter pass (optional): if article count >
2 × summary_max_articles, batches articles in groups offilter_batch_size(default 20) and asks AI for relevant indices. Falls back to summary provider if no filter role is configured. - Summary pass: structured French-language prompt with watchlist symbols + truncated article bodies (max 1000 chars). Called with
Think: true, NumCtx: 32768. - Report generation: async goroutine, 30-minute context. DB row created immediately with
status=generating; frontend pollsGET /reports— there is no WebSocket/SSE.
Three AI roles (summary, report, filter) resolve their provider via settings keys ai_role_<role>_provider / ai_role_<role>_model, falling back to the single is_active=TRUE provider.
Supported providers: openai, anthropic, gemini, ollama, claudecode (shells out to claude -p).
Key non-obvious patterns
httputil.OKuses reflection to convert nil slices to[]so the frontend never receives JSONnullfor lists.- Bloomberg scraping is split: the Go backend calls
POST http://scraper:3001/bloomberg/scrape— it does not run Chromium in-process. - API keys are never stored raw:
ListAIProvidersreplaces the encrypted key with a booleanhas_keyfield. - Scheduler timezone: cron specs are prefixed with
TZ=<iana>so schedules respect the app timezone, not UTC. - Nginx resolver trick:
resolver 127.0.0.11+set $backend http://backend:8080(variable) prevents startup failures when the backend container isn't ready yet — Docker DNS is queried per-request. claudecodeprovider model list is hardcoded inbackend/internal/ai/claudecode.go— update it when new Claude models are released.- UUID primary keys require the
pgcryptoPostgres extension (gen_random_uuid()). Migrations handle this automatically. scrape_jobsstatus values:pending | running | done | error.reportsstatus values:generating | done | error.
Environment variables
| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
Yes | Postgres DSN |
JWT_SECRET |
Yes | JWT signing secret |
ENCRYPTION_KEY |
Yes | 32-byte hex for AES-256-GCM (openssl rand -hex 32) |
PORT |
No | HTTP port, default 8080 |
SCRAPER_URL |
No | Scraper service URL, default http://scraper:3001 |
ADMIN_EMAIL / ADMIN_PASSWORD |
No | Bootstrap admin account created on startup |