aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/server/repositories/deck.ts
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2025-12-07 17:37:08 +0900
committernsfisis <nsfisis@gmail.com>2025-12-07 17:37:08 +0900
commit797ef2fcfaa7ac63355c13809a644401a76250bc (patch)
tree70814083131572b31b72fdbaeea74b2d7aa60d91 /src/server/repositories/deck.ts
parent943674471d062ea4494727ce308c8c429afd6f98 (diff)
downloadkioku-797ef2fcfaa7ac63355c13809a644401a76250bc.tar.gz
kioku-797ef2fcfaa7ac63355c13809a644401a76250bc.tar.zst
kioku-797ef2fcfaa7ac63355c13809a644401a76250bc.zip
feat(server): add Deck CRUD endpoints with tests
Implement complete Deck management API: - GET /api/decks - List user's decks - POST /api/decks - Create new deck - GET /api/decks/:id - Get deck by ID - PUT /api/decks/:id - Update deck - DELETE /api/decks/:id - Soft delete deck All endpoints require authentication and scope data to the authenticated user. Includes 22 unit tests covering success and error cases. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'src/server/repositories/deck.ts')
-rw-r--r--src/server/repositories/deck.ts95
1 files changed, 95 insertions, 0 deletions
diff --git a/src/server/repositories/deck.ts b/src/server/repositories/deck.ts
new file mode 100644
index 0000000..77985a7
--- /dev/null
+++ b/src/server/repositories/deck.ts
@@ -0,0 +1,95 @@
+import { and, eq, isNull, sql } from "drizzle-orm";
+import { db } from "../db/index.js";
+import { decks } from "../db/schema.js";
+import type { Deck, DeckRepository } from "./types.js";
+
+export const deckRepository: DeckRepository = {
+ async findByUserId(userId: string): Promise<Deck[]> {
+ const result = await db
+ .select()
+ .from(decks)
+ .where(and(eq(decks.userId, userId), isNull(decks.deletedAt)));
+ return result;
+ },
+
+ async findById(id: string, userId: string): Promise<Deck | undefined> {
+ const result = await db
+ .select()
+ .from(decks)
+ .where(
+ and(
+ eq(decks.id, id),
+ eq(decks.userId, userId),
+ isNull(decks.deletedAt),
+ ),
+ );
+ return result[0];
+ },
+
+ async create(data: {
+ userId: string;
+ name: string;
+ description?: string | null;
+ newCardsPerDay?: number;
+ }): Promise<Deck> {
+ const [deck] = await db
+ .insert(decks)
+ .values({
+ userId: data.userId,
+ name: data.name,
+ description: data.description ?? null,
+ newCardsPerDay: data.newCardsPerDay ?? 20,
+ })
+ .returning();
+ if (!deck) {
+ throw new Error("Failed to create deck");
+ }
+ return deck;
+ },
+
+ async update(
+ id: string,
+ userId: string,
+ data: {
+ name?: string;
+ description?: string | null;
+ newCardsPerDay?: number;
+ },
+ ): Promise<Deck | undefined> {
+ const result = await db
+ .update(decks)
+ .set({
+ ...data,
+ updatedAt: new Date(),
+ syncVersion: sql`${decks.syncVersion} + 1`,
+ })
+ .where(
+ and(
+ eq(decks.id, id),
+ eq(decks.userId, userId),
+ isNull(decks.deletedAt),
+ ),
+ )
+ .returning();
+ return result[0];
+ },
+
+ async softDelete(id: string, userId: string): Promise<boolean> {
+ const result = await db
+ .update(decks)
+ .set({
+ deletedAt: new Date(),
+ updatedAt: new Date(),
+ syncVersion: sql`${decks.syncVersion} + 1`,
+ })
+ .where(
+ and(
+ eq(decks.id, id),
+ eq(decks.userId, userId),
+ isNull(decks.deletedAt),
+ ),
+ )
+ .returning({ id: decks.id });
+ return result.length > 0;
+ },
+};