1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
import { zValidator } from "@hono/zod-validator";
import { Hono } from "hono";
import { z } from "zod";
import { authMiddleware, getAuthUser } from "../middleware/index.js";
import {
type SyncPullQuery,
type SyncPushData,
type SyncRepository,
syncRepository,
} from "../repositories/sync.js";
export interface SyncDependencies {
syncRepo: SyncRepository;
}
const syncDeckSchema = z.object({
id: z.uuid(),
name: z.string().min(1).max(255),
description: z.string().nullable(),
newCardsPerDay: z.number().int().min(0).max(1000),
createdAt: z.string().datetime(),
updatedAt: z.string().datetime(),
deletedAt: z.string().datetime().nullable(),
});
const syncCardSchema = z.object({
id: z.uuid(),
deckId: z.uuid(),
front: z.string().min(1),
back: z.string().min(1),
state: z.number().int().min(0).max(3),
due: z.string().datetime(),
stability: z.number().min(0),
difficulty: z.number().min(0),
elapsedDays: z.number().int().min(0),
scheduledDays: z.number().int().min(0),
reps: z.number().int().min(0),
lapses: z.number().int().min(0),
lastReview: z.string().datetime().nullable(),
createdAt: z.string().datetime(),
updatedAt: z.string().datetime(),
deletedAt: z.string().datetime().nullable(),
});
const syncReviewLogSchema = z.object({
id: z.uuid(),
cardId: z.uuid(),
rating: z.number().int().min(1).max(4),
state: z.number().int().min(0).max(3),
scheduledDays: z.number().int().min(0),
elapsedDays: z.number().int().min(0),
reviewedAt: z.string().datetime(),
durationMs: z.number().int().min(0).nullable(),
});
const syncPushSchema = z.object({
decks: z.array(syncDeckSchema).default([]),
cards: z.array(syncCardSchema).default([]),
reviewLogs: z.array(syncReviewLogSchema).default([]),
});
const syncPullQuerySchema = z.object({
lastSyncVersion: z.coerce.number().int().min(0).default(0),
});
export function createSyncRouter(deps: SyncDependencies) {
const { syncRepo } = deps;
return new Hono()
.use("*", authMiddleware)
.post("/push", zValidator("json", syncPushSchema), async (c) => {
const user = getAuthUser(c);
const data = c.req.valid("json") as SyncPushData;
const result = await syncRepo.pushChanges(user.id, data);
return c.json(result, 200);
})
.get("/pull", zValidator("query", syncPullQuerySchema), async (c) => {
const user = getAuthUser(c);
const query = c.req.valid("query") as SyncPullQuery;
const result = await syncRepo.pullChanges(user.id, query);
return c.json(result, 200);
});
}
export const sync = createSyncRouter({
syncRepo: syncRepository,
});
|