diff options
| author | nsfisis <nsfisis@gmail.com> | 2025-12-07 17:37:08 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2025-12-07 17:37:08 +0900 |
| commit | 797ef2fcfaa7ac63355c13809a644401a76250bc (patch) | |
| tree | 70814083131572b31b72fdbaeea74b2d7aa60d91 /src/server/routes/decks.ts | |
| parent | 943674471d062ea4494727ce308c8c429afd6f98 (diff) | |
| download | kioku-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/routes/decks.ts')
| -rw-r--r-- | src/server/routes/decks.ts | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/src/server/routes/decks.ts b/src/server/routes/decks.ts new file mode 100644 index 0000000..4604ea9 --- /dev/null +++ b/src/server/routes/decks.ts @@ -0,0 +1,82 @@ +import { zValidator } from "@hono/zod-validator"; +import { Hono } from "hono"; +import { z } from "zod"; +import { authMiddleware, Errors, getAuthUser } from "../middleware/index.js"; +import { type DeckRepository, deckRepository } from "../repositories/index.js"; +import { createDeckSchema, updateDeckSchema } from "../schemas/index.js"; + +export interface DeckDependencies { + deckRepo: DeckRepository; +} + +const deckIdParamSchema = z.object({ + id: z.string().uuid(), +}); + +export function createDecksRouter(deps: DeckDependencies) { + const { deckRepo } = deps; + + return new Hono() + .use("*", authMiddleware) + .get("/", async (c) => { + const user = getAuthUser(c); + const decks = await deckRepo.findByUserId(user.id); + return c.json({ decks }, 200); + }) + .post("/", zValidator("json", createDeckSchema), async (c) => { + const user = getAuthUser(c); + const data = c.req.valid("json"); + + const deck = await deckRepo.create({ + userId: user.id, + name: data.name, + description: data.description, + newCardsPerDay: data.newCardsPerDay, + }); + + return c.json({ deck }, 201); + }) + .get("/:id", zValidator("param", deckIdParamSchema), async (c) => { + const user = getAuthUser(c); + const { id } = c.req.valid("param"); + + const deck = await deckRepo.findById(id, user.id); + if (!deck) { + throw Errors.notFound("Deck not found", "DECK_NOT_FOUND"); + } + + return c.json({ deck }, 200); + }) + .put( + "/:id", + zValidator("param", deckIdParamSchema), + zValidator("json", updateDeckSchema), + async (c) => { + const user = getAuthUser(c); + const { id } = c.req.valid("param"); + const data = c.req.valid("json"); + + const deck = await deckRepo.update(id, user.id, data); + if (!deck) { + throw Errors.notFound("Deck not found", "DECK_NOT_FOUND"); + } + + return c.json({ deck }, 200); + }, + ) + .delete("/:id", zValidator("param", deckIdParamSchema), async (c) => { + const user = getAuthUser(c); + const { id } = c.req.valid("param"); + + const deleted = await deckRepo.softDelete(id, user.id); + if (!deleted) { + throw Errors.notFound("Deck not found", "DECK_NOT_FOUND"); + } + + return c.json({ success: true }, 200); + }); +} + +export const decks = createDecksRouter({ + deckRepo: deckRepository, +}); |
