diff options
Diffstat (limited to 'src/server/repositories')
| -rw-r--r-- | src/server/repositories/card.ts | 102 | ||||
| -rw-r--r-- | src/server/repositories/index.ts | 1 | ||||
| -rw-r--r-- | src/server/repositories/types.ts | 44 |
3 files changed, 147 insertions, 0 deletions
diff --git a/src/server/repositories/card.ts b/src/server/repositories/card.ts new file mode 100644 index 0000000..0a47c50 --- /dev/null +++ b/src/server/repositories/card.ts @@ -0,0 +1,102 @@ +import { and, eq, isNull, sql } from "drizzle-orm"; +import { db } from "../db/index.js"; +import { CardState, cards } from "../db/schema.js"; +import type { Card, CardRepository } from "./types.js"; + +export const cardRepository: CardRepository = { + async findByDeckId(deckId: string): Promise<Card[]> { + const result = await db + .select() + .from(cards) + .where(and(eq(cards.deckId, deckId), isNull(cards.deletedAt))); + return result; + }, + + async findById(id: string, deckId: string): Promise<Card | undefined> { + const result = await db + .select() + .from(cards) + .where( + and( + eq(cards.id, id), + eq(cards.deckId, deckId), + isNull(cards.deletedAt), + ), + ); + return result[0]; + }, + + async create( + deckId: string, + data: { + front: string; + back: string; + }, + ): Promise<Card> { + const [card] = await db + .insert(cards) + .values({ + deckId, + front: data.front, + back: data.back, + state: CardState.New, + due: new Date(), + stability: 0, + difficulty: 0, + elapsedDays: 0, + scheduledDays: 0, + reps: 0, + lapses: 0, + }) + .returning(); + if (!card) { + throw new Error("Failed to create card"); + } + return card; + }, + + async update( + id: string, + deckId: string, + data: { + front?: string; + back?: string; + }, + ): Promise<Card | undefined> { + const result = await db + .update(cards) + .set({ + ...data, + updatedAt: new Date(), + syncVersion: sql`${cards.syncVersion} + 1`, + }) + .where( + and( + eq(cards.id, id), + eq(cards.deckId, deckId), + isNull(cards.deletedAt), + ), + ) + .returning(); + return result[0]; + }, + + async softDelete(id: string, deckId: string): Promise<boolean> { + const result = await db + .update(cards) + .set({ + deletedAt: new Date(), + updatedAt: new Date(), + syncVersion: sql`${cards.syncVersion} + 1`, + }) + .where( + and( + eq(cards.id, id), + eq(cards.deckId, deckId), + isNull(cards.deletedAt), + ), + ) + .returning({ id: cards.id }); + return result.length > 0; + }, +}; diff --git a/src/server/repositories/index.ts b/src/server/repositories/index.ts index 9a703ab..298666e 100644 --- a/src/server/repositories/index.ts +++ b/src/server/repositories/index.ts @@ -1,3 +1,4 @@ +export { cardRepository } from "./card.js"; export { deckRepository } from "./deck.js"; export { refreshTokenRepository } from "./refresh-token.js"; export * from "./types.js"; diff --git a/src/server/repositories/types.ts b/src/server/repositories/types.ts index 740fa29..1e8ba21 100644 --- a/src/server/repositories/types.ts +++ b/src/server/repositories/types.ts @@ -77,3 +77,47 @@ export interface DeckRepository { ): Promise<Deck | undefined>; softDelete(id: string, userId: string): Promise<boolean>; } + +export interface Card { + id: string; + deckId: string; + front: string; + back: string; + + // FSRS fields + state: number; + due: Date; + stability: number; + difficulty: number; + elapsedDays: number; + scheduledDays: number; + reps: number; + lapses: number; + lastReview: Date | null; + + createdAt: Date; + updatedAt: Date; + deletedAt: Date | null; + syncVersion: number; +} + +export interface CardRepository { + findByDeckId(deckId: string): Promise<Card[]>; + findById(id: string, deckId: string): Promise<Card | undefined>; + create( + deckId: string, + data: { + front: string; + back: string; + }, + ): Promise<Card>; + update( + id: string, + deckId: string, + data: { + front?: string; + back?: string; + }, + ): Promise<Card | undefined>; + softDelete(id: string, deckId: string): Promise<boolean>; +} |
