feat: ditch triplit, move to postgresql + drizzle orm

This commit is contained in:
Zoe
2026-04-08 17:03:07 -05:00
parent c341c96798
commit e2e3ac6e86
121 changed files with 6680 additions and 4373 deletions
+54 -26
View File
@@ -1,6 +1,6 @@
# Veridian AGENTS.md
**Nuxt 4 AI chat platform with Triplit database + better-auth**
**Nuxt 4 AI chat platform with Drizzle + PostgreSQL + better-auth**
## Architecture Overview
@@ -21,9 +21,10 @@ server/ # Server-side API routes
├── chat/ # Generation, cancel endpoints
└── provider/ # Model fetching from providers
triplit/ # Database
├── schema.ts # Full schema with auth + app collections
└── auth-schema.ts # Auth collections (users, sessions, accounts)
drizzle/ # Database (migrated from Triplit)
├── schema.ts # Full schema with auth + app tables
├── relations.ts # Table relationships
└── migrations/ # SQL migration files
lib/ # Shared utilities
├── auth.ts # Better-auth server config
@@ -39,7 +40,9 @@ database commands.
### Database Commands
- `bunx triplit schema push` - Push schema changes to database
- `bunx drizzle-kit push` - Push schema changes to database
- `bunx drizzle-kit generate` - Generate migration from schema changes
- `bunx drizzle-kit migrate` - Run pending migrations
## Code Style
@@ -57,15 +60,12 @@ database commands.
Group imports in this order (alphabetical within groups):
1. Type imports (`types/`)
2. Dependency imports (npm packages)
3. Triplit imports (`#triplit/`, `@triplit/`)
4. Vue/Nuxt imports (`vue`, `#app`, `~~/`, `~/`, `@/`)
5. Local imports (`lib/`, `server/`)
3. Vue/Nuxt imports (`vue`, `#app`, `~~/`, `~/`, `@/`)
4. Local imports (`lib/`, `server/`)
```typescript
// Example import order
import type { Result, Ok, Err } from '~~/types/result';
import type { Entity } from '@triplit/client';
import type schema from '#triplit/schema';
import type { Foo } from 'vue';
import { ref, computed } from 'vue';
import { useFeature } from '~/composables/useFeature';
@@ -128,23 +128,28 @@ export default defineEventHandler(async (event) => {
});
```
## Database Query Pattern (Triplit)
## Database Query Pattern (Drizzle)
```typescript
// Client queries use useQuery() with auto-includes
useQuery('collection', triplit, triplit.query('collection').Include('relation'));
// Server-side queries use Drizzle ORM
import { db } from '~~/server/db';
import { agents } from '~~/drizzle/schema';
// Server-side
import { httpClient } from '~~/server/lib/triplit';
await httpClient.fetchOne(httpClient.query('collection').Where('id', '=', providerId));
// Query with relations
const result = await db.query.agents.findMany({
where: eq(agents.userId, userId),
with: { topics: true, messages: true },
});
// Insert with conflict handling
await db.insert(agents).values(data).onConflictDoNothing();
```
## Authentication Flow
1. **Login**: Client uses `authClient.signIn.email()` → derives encryption key from password
2. **Session**: Better-auth creates session token → stored in cookie
3. **Triplit**: Session token passed to `triplit.startSession(token)` for DB access
4. **API Keys**: Encrypted client-side with AES-GCM (key derived from password + userId)
3. **API Keys**: Encrypted client-side with AES-GCM (key derived from password + userId)
**Key files**: `lib/auth.ts`, `lib/auth-client.ts`, `app/plugins/auth.*.ts`, `app/middleware/auth.global.ts`
@@ -159,10 +164,8 @@ await httpClient.fetchOne(httpClient.query('collection').Where('id', '=', provid
| Variable | Purpose |
|----------|---------|
| `TRIPLIT_SERVICE_TOKEN` | Admin DB access |
| `NUXT_TRIPLIT_ANON_TOKEN` | Anonymous DB access |
| `DATABASE_URL` | PostgreSQL connection string |
| `BETTER_AUTH_SECRET` | Auth encryption |
| `NUXT_PUBLIC_TRIPLIT_URL` | DB server URL |
## Key Composables
@@ -198,15 +201,40 @@ await httpClient.fetchOne(httpClient.query('collection').Where('id', '=', provid
## Known Issues (from BUGS.md)
1. Triplit occasionally makes duplicate connections (race condition)
2. Sidebar hover animation occasionally glitches on agent routes
3. Theme switcher + sidebar interaction bug
1. Sidebar hover animation occasionally glitches on agent routes
2. Theme switcher + sidebar interaction bug
## Critical Architecture Notes
### Chat Endpoint God File
- `server/api/topic/[topicId]/chat/index.post.ts` is 1263 lines handling route validation, tool definitions, streaming orchestration, DB transactions, and event emission
- Files that change together: this file + `server/utils/events.ts` + `server/utils/generations.ts`
- Tool definitions (bash, python, file access) should be extracted to separate module before extending
### Security: Code Execution Tools Have No Sandboxing
- `bashTool` uses `exec()` and `pythonTool` uses `python3 -c` with user-controlled input — full RCE risk
- Do NOT extend these tools without adding: timeouts, path restrictions, resource limits
- Location: `server/api/topic/[topicId]/chat/index.post.ts` ~lines 261-455
### Crash Risk: `todo()` Function
- `todo()` literally throws `new Error('TODO')` on unhandled token types in chat handler
- Handle gracefully before production deployment
- Location: `server/api/topic/[topicId]/chat/index.post.ts` ~line 256
### Module-Scoped Mutable State Limitations
- `useDialog.ts` has module-scoped `actionCallback` — not SSR-safe, overwritten on concurrent dialogs
- `pendingGenerations` in `server/utils/generations.ts` and in-memory event channels in `server/utils/events.ts` won't work in multi-process deployments
- Fine for single-instance dev, but architectural ceiling for scaling
### Pagination Params Validated But Unused
- `agents.get.ts` validates `page`/`limit` query params but never applies them to the query
- All agent queries return everything regardless of pagination params
## Development Notes
- **Never run dev server as agent** - just complete tasks and signal done
- Always run `bunx triplit schema push` after schema changes
- Always run `bunx drizzle-kit push` after schema changes
- Use `protectRoute()` in all API routes for auth
- All UI state that persists → use cookies (`useCookie()`)
- Real-time data → Triplit subscriptions via `useQuery()`
- Real-time data → SSE events via `server/utils/events.ts`
- API keys → encrypted client-side before DB storage