From 9632d70ea0d326ac0df4e9bffb7fb669013f0755 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sun, 7 Dec 2025 19:20:04 +0900 Subject: feat(server): add GET /api/sync/pull endpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement sync pull endpoint to fetch entities updated since a given syncVersion. Returns decks, cards, and review logs with their current sync versions for incremental client synchronization. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/server/repositories/sync.ts | 73 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) (limited to 'src/server/repositories/sync.ts') diff --git a/src/server/repositories/sync.ts b/src/server/repositories/sync.ts index 3051121..87acdb4 100644 --- a/src/server/repositories/sync.ts +++ b/src/server/repositories/sync.ts @@ -62,8 +62,20 @@ export interface SyncPushResult { }; } +export interface SyncPullQuery { + lastSyncVersion: number; +} + +export interface SyncPullResult { + decks: Deck[]; + cards: Card[]; + reviewLogs: ReviewLog[]; + currentSyncVersion: number; +} + export interface SyncRepository { pushChanges(userId: string, data: SyncPushData): Promise; + pullChanges(userId: string, query: SyncPullQuery): Promise; } export const syncRepository: SyncRepository = { @@ -276,4 +288,65 @@ export const syncRepository: SyncRepository = { return result; }, + + async pullChanges(userId: string, query: SyncPullQuery): Promise { + const { lastSyncVersion } = query; + + // Get all decks with syncVersion > lastSyncVersion + const pulledDecks = await db + .select() + .from(decks) + .where(and(eq(decks.userId, userId), gt(decks.syncVersion, lastSyncVersion))); + + // Get all cards from user's decks with syncVersion > lastSyncVersion + const userDeckIds = await db + .select({ id: decks.id }) + .from(decks) + .where(eq(decks.userId, userId)); + + const deckIdList = userDeckIds.map((d) => d.id); + + let pulledCards: Card[] = []; + if (deckIdList.length > 0) { + const cardResults = await db + .select() + .from(cards) + .where(gt(cards.syncVersion, lastSyncVersion)); + + // Filter cards that belong to user's decks + pulledCards = cardResults.filter((c) => deckIdList.includes(c.deckId)); + } + + // Get all review logs for user with syncVersion > lastSyncVersion + const pulledReviewLogs = await db + .select() + .from(reviewLogs) + .where(and(eq(reviewLogs.userId, userId), gt(reviewLogs.syncVersion, lastSyncVersion))); + + // Calculate current max sync version across all entities + let currentSyncVersion = lastSyncVersion; + + for (const deck of pulledDecks) { + if (deck.syncVersion > currentSyncVersion) { + currentSyncVersion = deck.syncVersion; + } + } + for (const card of pulledCards) { + if (card.syncVersion > currentSyncVersion) { + currentSyncVersion = card.syncVersion; + } + } + for (const log of pulledReviewLogs) { + if (log.syncVersion > currentSyncVersion) { + currentSyncVersion = log.syncVersion; + } + } + + return { + decks: pulledDecks, + cards: pulledCards, + reviewLogs: pulledReviewLogs, + currentSyncVersion, + }; + }, }; -- cgit v1.2.3-70-g09d2