FRE-592: Fix remaining code review blockers and add tests

- Replace in-memory Maps with Drizzle ORM queries for all CRUD operations
- Use integer IDs matching SQLite schema instead of UUIDs
- Fix scriptId to projectId inconsistency in characters and scenes
- Add project ownership verification on all mutation procedures
- Make getCharacter/getScene procedures protected (not public)
- Proper JWT-based userId validation via context
- Add cascade delete for characters/relationships/scenes on project deletion
- Add verifyProjectOwnership helper for authorization checks
- Rewrite tests with createCallerFactory pattern for tRPC v11
- Use better-sqlite3 for in-memory test database
- Split vitest config into separate file from vite config
This commit is contained in:
2026-04-24 08:31:42 -04:00
parent 4d9b4ecf2a
commit 79d153f75a
11 changed files with 443 additions and 352 deletions

View File

@@ -172,12 +172,15 @@ export const projectRouter = {
}),
// Character CRUD procedures
listCharacters: projectProcedure.query(async ({ ctx }) => {
return await ctx.db!.select()
.from(characters)
.where(eq(characters.projectId, ctx.projectId!))
.orderBy(characters.name);
}),
listCharacters: protectedProcedure
.input(z.object({ projectId: z.number().int().positive() }))
.query(async ({ input, ctx }) => {
await verifyProjectOwnership(ctx.db!, input.projectId, ctx.userId!);
return await ctx.db!.select()
.from(characters)
.where(eq(characters.projectId, input.projectId))
.orderBy(characters.name);
}),
getCharacter: protectedProcedure
.input(z.object({ id: z.number().int().positive() }))
@@ -529,12 +532,15 @@ export const projectRouter = {
}),
// Scene procedures
listScenes: projectProcedure.query(async ({ ctx }) => {
return await ctx.db!.select()
.from(scenes)
.where(eq(scenes.projectId, ctx.projectId!))
.orderBy(scenes.order);
}),
listScenes: protectedProcedure
.input(z.object({ projectId: z.number().int().positive() }))
.query(async ({ input, ctx }) => {
await verifyProjectOwnership(ctx.db!, input.projectId, ctx.userId!);
return await ctx.db!.select()
.from(scenes)
.where(eq(scenes.projectId, input.projectId))
.orderBy(scenes.order);
}),
getScene: protectedProcedure
.input(z.object({ id: z.number().int().positive() }))