feat: ditch triplit, move to postgresql + drizzle orm
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user