managarten/apps/context/CLAUDE.md
Till JS ea4b585f37 feat(context): add NestJS backend, PostgreSQL database, and migrate web app from Supabase to API
- Create NestJS backend on port 3020 with 4 modules (space, document, ai, token)
- Add Drizzle schema with 5 tables (spaces, documents, token_transactions, model_prices, user_tokens)
- Rewrite web services (spaces, documents, tokens, ai) to use shared API client instead of Supabase
- Move AI API keys server-side (Azure OpenAI, Google Gemini)
- Add seed script for model prices (gpt-4.1, gemini-pro, gemini-flash)
- Add 70 unit tests across 4 test suites (space, document, token, ai services)
- Add monorepo integration (setup-databases.sh, generate-env.mjs, docker init-db, root scripts)
- Remove @supabase/supabase-js dependency and delete supabase.ts from web app
- Update CLAUDE.md with full API documentation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 09:28:01 +01:00

210 lines
7.5 KiB
Markdown

# Context App
AI-powered document management and context system for knowledge organization.
| App | Port | URL |
|-----|------|-----|
| Backend | 3020 | http://localhost:3020 |
| Web App | 5192 | http://localhost:5192 |
| Mobile | 8081 | Expo Go |
## Structure
```
apps/context/
├── apps/
│ ├── backend/ # NestJS API server (@context/backend)
│ │ └── src/
│ │ ├── main.ts
│ │ ├── app.module.ts
│ │ ├── db/ # Drizzle schemas + migrations
│ │ │ ├── schema/
│ │ │ │ ├── spaces.schema.ts
│ │ │ │ ├── documents.schema.ts
│ │ │ │ ├── token-transactions.schema.ts
│ │ │ │ ├── model-prices.schema.ts
│ │ │ │ └── user-tokens.schema.ts
│ │ │ ├── connection.ts
│ │ │ ├── database.module.ts
│ │ │ ├── migrate.ts
│ │ │ └── seed.ts
│ │ ├── space/ # Space CRUD
│ │ ├── document/ # Document CRUD + versions + tags
│ │ ├── ai/ # AI generation (Azure + Google)
│ │ ├── token/ # Token balance + stats
│ │ └── common/
│ ├── web/ # SvelteKit web application (@context/web)
│ ├── mobile/ # Expo React Native app (@context/mobile)
│ └── landing/ # (Planned) Astro Landing Page
├── packages/ # Project-specific shared code
└── package.json # Workspace root
```
## Development Commands
```bash
# From monorepo root
pnpm dev:context:full # Start auth + backend + web (with DB setup)
pnpm dev:context:backend # Start backend only (port 3020)
pnpm dev:context:web # Start web only (port 5192)
pnpm dev:context:app # Start web + backend together
pnpm dev:context:mobile # Start mobile app
# Database
pnpm context:db:push # Push schema to database
pnpm context:db:studio # Open Drizzle Studio
pnpm context:db:seed # Seed model prices
pnpm setup:db:context # Create DB + push schema
```
## Tech Stack
| Layer | Technology |
|-------|------------|
| **Backend** | NestJS 10, Drizzle ORM, PostgreSQL |
| **Web** | SvelteKit 2.x, Svelte 5 (runes mode), Tailwind CSS 4 |
| **Mobile** | React Native 0.76 + Expo SDK 52, NativeWind |
| **Auth** | Mana Core Auth (JWT) |
| **AI** | Azure OpenAI (GPT-4.1), Google Gemini (Pro, Flash) |
| **i18n** | svelte-i18n (DE, EN) |
## Core Features
- **Spaces**: Organize documents into collections with prefix-based short IDs
- **Documents**: Text, context references, and AI prompts with versioning
- **AI Generation**: Multi-model support (Azure OpenAI, Google Gemini)
- **Token Economy**: Track and manage AI usage credits per user
- **Document Versioning**: AI-generated summaries, continuations, rewrites
## Backend API Endpoints
### Health
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/health` | GET | Health check |
### Spaces
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/api/v1/spaces` | GET | List user's spaces |
| `/api/v1/spaces` | POST | Create space |
| `/api/v1/spaces/:id` | GET | Get space details |
| `/api/v1/spaces/:id` | PUT | Update space |
| `/api/v1/spaces/:id` | DELETE | Delete space (cascades documents) |
### Documents
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/api/v1/documents` | GET | List documents (?spaceId=&preview=true&limit=) |
| `/api/v1/documents/recent` | GET | Recent documents (?limit=) |
| `/api/v1/documents` | POST | Create document |
| `/api/v1/documents/:id` | GET | Get document |
| `/api/v1/documents/:id` | PUT | Update document |
| `/api/v1/documents/:id` | DELETE | Delete document |
| `/api/v1/documents/:id/tags` | PUT | Update document tags |
| `/api/v1/documents/:id/pinned` | PUT | Toggle pinned |
| `/api/v1/documents/:id/versions` | GET | Get document versions |
| `/api/v1/documents/:id/versions` | POST | Create AI version |
### AI
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/api/v1/ai/generate` | POST | Generate text (server-side AI) |
| `/api/v1/ai/estimate` | POST | Estimate token cost |
### Tokens
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/api/v1/tokens/balance` | GET | Get user token balance |
| `/api/v1/tokens/stats` | GET | Usage stats (?timeframe=day\|week\|month\|year) |
| `/api/v1/tokens/transactions` | GET | Transaction history (?limit=&offset=) |
| `/api/v1/tokens/models` | GET | Available model prices |
## Database Schema
### spaces
| Column | Type | Description |
|--------|------|-------------|
| `id` | UUID | Primary key |
| `user_id` | TEXT | Owner |
| `name` | VARCHAR(255) | Space name |
| `description` | TEXT | Optional description |
| `settings` | JSONB | Space settings |
| `pinned` | BOOLEAN | Pinned in sidebar |
| `prefix` | VARCHAR(10) | Short ID prefix (e.g. "A") |
| `text_doc_counter` | INTEGER | Counter for text docs |
| `context_doc_counter` | INTEGER | Counter for context docs |
| `prompt_doc_counter` | INTEGER | Counter for prompt docs |
### documents
| Column | Type | Description |
|--------|------|-------------|
| `id` | UUID | Primary key |
| `user_id` | TEXT | Owner |
| `space_id` | UUID | FK to spaces (cascade delete) |
| `title` | VARCHAR(500) | Document title |
| `content` | TEXT | Document content |
| `type` | VARCHAR(20) | text / context / prompt |
| `short_id` | VARCHAR(20) | Short ID (e.g. "AD1") |
| `pinned` | BOOLEAN | Pinned flag |
| `metadata` | JSONB | Tags, word count, version info |
### token_transactions
| Column | Type | Description |
|--------|------|-------------|
| `id` | UUID | Primary key |
| `user_id` | TEXT | User |
| `amount` | INTEGER | Tokens used (negative for usage) |
| `transaction_type` | VARCHAR(50) | usage / bonus / purchase |
| `model_used` | VARCHAR(100) | AI model name |
| `prompt_tokens` | INTEGER | Input tokens |
| `completion_tokens` | INTEGER | Output tokens |
| `cost_usd` | NUMERIC(10,6) | Actual USD cost |
### model_prices
| Column | Type | Description |
|--------|------|-------------|
| `model_name` | VARCHAR(100) | Unique model name |
| `input_price_per_1k_tokens` | NUMERIC(10,6) | Input price |
| `output_price_per_1k_tokens` | NUMERIC(10,6) | Output price |
| `tokens_per_dollar` | INTEGER | App tokens per USD |
### user_tokens
| Column | Type | Description |
|--------|------|-------------|
| `user_id` | TEXT | Primary key |
| `token_balance` | INTEGER | Current balance |
| `monthly_free_tokens` | INTEGER | Free monthly allocation |
## Environment Variables
### Backend (.env)
```env
NODE_ENV=development
PORT=3020
DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/context
MANA_CORE_AUTH_URL=http://localhost:3001
AZURE_OPENAI_API_KEY=your-key
AZURE_OPENAI_ENDPOINT=https://your-endpoint.openai.azure.com/
GOOGLE_API_KEY=your-key
```
### Web (.env)
```env
PUBLIC_BACKEND_URL=http://localhost:3020
PUBLIC_MANA_CORE_AUTH_URL=http://localhost:3001
```
## Important Patterns
1. **API Client pattern** - All web services use `@manacore/shared-api-client` (Go-style `{ data, error }`)
2. **Svelte 5 runes** - `$state`, `$derived`, `$effect` throughout
3. **Server-side AI keys** - API keys only on backend, never in frontend
4. **Auto word/token count** - Backend calculates on create/update
5. **Optimistic updates** - Immediate UI feedback in stores
6. **Document versioning** - AI generations linked via `parent_document` in metadata