This commit is contained in:
Vendored
+289
@@ -0,0 +1,289 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const express_1 = require("express");
|
||||
const asyncHandler_1 = require("../utils/asyncHandler");
|
||||
const db_1 = require("../db");
|
||||
const schema_1 = require("../db/schema");
|
||||
const drizzle_orm_1 = require("drizzle-orm");
|
||||
const auth_1 = require("../utils/auth");
|
||||
const errorHandler_1 = require("../middleware/errorHandler");
|
||||
const validation_1 = require("../utils/validation");
|
||||
const router = (0, express_1.Router)();
|
||||
router.use(auth_1.authMiddleware);
|
||||
router.get('/', (0, asyncHandler_1.asyncHandler)(async (req, res) => {
|
||||
const { since } = validation_1.syncQuerySchema.parse(req.query);
|
||||
const userId = req.user.userId;
|
||||
const sinceDate = since;
|
||||
// Fetch categories changed since timestamp
|
||||
const changedCategories = await db_1.db
|
||||
.select()
|
||||
.from(schema_1.categories)
|
||||
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.categories.userId, userId), (0, drizzle_orm_1.gte)(schema_1.categories.updatedAt, sinceDate)))
|
||||
.orderBy((0, drizzle_orm_1.asc)(schema_1.categories.updatedAt));
|
||||
// Fetch tasks changed since timestamp
|
||||
const changedTasks = await db_1.db
|
||||
.select()
|
||||
.from(schema_1.tasks)
|
||||
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.tasks.userId, userId), (0, drizzle_orm_1.gte)(schema_1.tasks.updatedAt, sinceDate)))
|
||||
.orderBy((0, drizzle_orm_1.asc)(schema_1.tasks.updatedAt));
|
||||
// Fetch subtasks changed since timestamp
|
||||
const taskIds = changedTasks.map(t => t.id);
|
||||
let changedSubtasks = [];
|
||||
if (taskIds.length > 0) {
|
||||
changedSubtasks = await db_1.db
|
||||
.select()
|
||||
.from(schema_1.subtasks)
|
||||
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.subtasks.userId, userId), (0, drizzle_orm_1.gte)(schema_1.subtasks.updatedAt, sinceDate)))
|
||||
.orderBy((0, drizzle_orm_1.asc)(schema_1.subtasks.updatedAt));
|
||||
}
|
||||
else {
|
||||
// Also fetch subtasks for tasks that might have been deleted (we track by updatedAt)
|
||||
changedSubtasks = await db_1.db
|
||||
.select()
|
||||
.from(schema_1.subtasks)
|
||||
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.subtasks.userId, userId), (0, drizzle_orm_1.gte)(schema_1.subtasks.updatedAt, sinceDate)))
|
||||
.orderBy((0, drizzle_orm_1.asc)(schema_1.subtasks.updatedAt));
|
||||
}
|
||||
// Fetch repeat profiles changed since timestamp
|
||||
const changedRepeatProfiles = await db_1.db
|
||||
.select()
|
||||
.from(schema_1.repeatProfiles)
|
||||
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.repeatProfiles.userId, userId), (0, drizzle_orm_1.gte)(schema_1.repeatProfiles.updatedAt, sinceDate)))
|
||||
.orderBy((0, drizzle_orm_1.asc)(schema_1.repeatProfiles.updatedAt));
|
||||
// Fetch friendships changed since timestamp
|
||||
const changedFriendships = await db_1.db
|
||||
.select()
|
||||
.from(schema_1.friendships)
|
||||
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.or)((0, drizzle_orm_1.eq)(schema_1.friendships.userId, userId), (0, drizzle_orm_1.eq)(schema_1.friendships.friendId, userId)), (0, drizzle_orm_1.gte)(schema_1.friendships.updatedAt, sinceDate)))
|
||||
.orderBy((0, drizzle_orm_1.asc)(schema_1.friendships.updatedAt));
|
||||
const timestamp = Date.now();
|
||||
res.json({
|
||||
categories: changedCategories,
|
||||
tasks: changedTasks,
|
||||
subtasks: changedSubtasks,
|
||||
repeatProfiles: changedRepeatProfiles,
|
||||
friendships: changedFriendships,
|
||||
timestamp,
|
||||
});
|
||||
}));
|
||||
router.post('/push', (0, asyncHandler_1.asyncHandler)(async (req, res) => {
|
||||
const data = validation_1.pushChangesSchema.parse(req.body);
|
||||
const userId = req.user.userId;
|
||||
const conflicts = [];
|
||||
const timestamp = Date.now();
|
||||
try {
|
||||
await db_1.db.transaction(async (tx) => {
|
||||
// Process categories
|
||||
if (data.changes.categories && data.changes.categories.length > 0) {
|
||||
for (const cat of data.changes.categories) {
|
||||
const existing = await tx
|
||||
.select()
|
||||
.from(schema_1.categories)
|
||||
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.categories.id, cat.id), (0, drizzle_orm_1.eq)(schema_1.categories.userId, userId)))
|
||||
.limit(1);
|
||||
if (existing.length > 0) {
|
||||
// Check for conflict
|
||||
if (existing[0].updatedAt > cat.updatedAt) {
|
||||
conflicts.push({
|
||||
entity: 'categories',
|
||||
id: cat.id,
|
||||
serverVersion: existing[0],
|
||||
clientVersion: cat,
|
||||
resolution: 'server_wins',
|
||||
});
|
||||
continue; // Server wins
|
||||
}
|
||||
await tx
|
||||
.update(schema_1.categories)
|
||||
.set({
|
||||
name: cat.name,
|
||||
color: cat.color,
|
||||
order: cat.order,
|
||||
updatedAt: cat.updatedAt,
|
||||
})
|
||||
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.categories.id, cat.id), (0, drizzle_orm_1.eq)(schema_1.categories.userId, userId)));
|
||||
}
|
||||
else {
|
||||
await tx.insert(schema_1.categories).values({
|
||||
...cat,
|
||||
userId,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// Process tasks
|
||||
if (data.changes.tasks && data.changes.tasks.length > 0) {
|
||||
for (const task of data.changes.tasks) {
|
||||
const existing = await tx
|
||||
.select()
|
||||
.from(schema_1.tasks)
|
||||
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.tasks.id, task.id), (0, drizzle_orm_1.eq)(schema_1.tasks.userId, userId)))
|
||||
.limit(1);
|
||||
if (existing.length > 0) {
|
||||
// Check for conflict
|
||||
if (existing[0].updatedAt > task.updatedAt) {
|
||||
conflicts.push({
|
||||
entity: 'tasks',
|
||||
id: task.id,
|
||||
serverVersion: existing[0],
|
||||
clientVersion: task,
|
||||
resolution: 'server_wins',
|
||||
});
|
||||
continue; // Server wins
|
||||
}
|
||||
await tx
|
||||
.update(schema_1.tasks)
|
||||
.set({
|
||||
title: task.title,
|
||||
description: task.description,
|
||||
categoryId: task.categoryId,
|
||||
priority: task.priority,
|
||||
completed: task.completed,
|
||||
dueDate: task.dueDate,
|
||||
dueTime: task.dueTime,
|
||||
endTime: task.endTime ?? '',
|
||||
repeat: task.repeat ?? 'none',
|
||||
repeatInterval: task.repeatInterval ?? 1,
|
||||
repeatDays: task.repeatDays ?? '',
|
||||
seriesId: task.seriesId ?? '',
|
||||
reminder: task.reminder ?? 'none',
|
||||
assigneeId: task.assigneeId ?? null,
|
||||
updatedAt: task.updatedAt,
|
||||
})
|
||||
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.tasks.id, task.id), (0, drizzle_orm_1.eq)(schema_1.tasks.userId, userId)));
|
||||
}
|
||||
else {
|
||||
await tx.insert(schema_1.tasks).values({
|
||||
...task,
|
||||
userId,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// Process subtasks
|
||||
if (data.changes.subtasks && data.changes.subtasks.length > 0) {
|
||||
for (const sub of data.changes.subtasks) {
|
||||
const existing = await tx
|
||||
.select()
|
||||
.from(schema_1.subtasks)
|
||||
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.subtasks.id, sub.id), (0, drizzle_orm_1.eq)(schema_1.subtasks.userId, userId)))
|
||||
.limit(1);
|
||||
if (existing.length > 0) {
|
||||
// Check for conflict
|
||||
if (existing[0].updatedAt > sub.updatedAt) {
|
||||
conflicts.push({
|
||||
entity: 'subtasks',
|
||||
id: sub.id,
|
||||
serverVersion: existing[0],
|
||||
clientVersion: sub,
|
||||
resolution: 'server_wins',
|
||||
});
|
||||
continue; // Server wins
|
||||
}
|
||||
await tx
|
||||
.update(schema_1.subtasks)
|
||||
.set({
|
||||
taskId: sub.taskId,
|
||||
title: sub.title,
|
||||
completed: sub.completed,
|
||||
order: sub.order,
|
||||
updatedAt: sub.updatedAt,
|
||||
})
|
||||
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.subtasks.id, sub.id), (0, drizzle_orm_1.eq)(schema_1.subtasks.userId, userId)));
|
||||
}
|
||||
else {
|
||||
await tx.insert(schema_1.subtasks).values({
|
||||
...sub,
|
||||
userId,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// Process repeat profiles
|
||||
if (data.changes.repeatProfiles && data.changes.repeatProfiles.length > 0) {
|
||||
for (const profile of data.changes.repeatProfiles) {
|
||||
const existing = await tx
|
||||
.select()
|
||||
.from(schema_1.repeatProfiles)
|
||||
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.repeatProfiles.id, profile.id), (0, drizzle_orm_1.eq)(schema_1.repeatProfiles.userId, userId)))
|
||||
.limit(1);
|
||||
if (existing.length > 0) {
|
||||
// Check for conflict
|
||||
if (existing[0].updatedAt > profile.updatedAt) {
|
||||
conflicts.push({
|
||||
entity: 'repeatProfiles',
|
||||
id: profile.id,
|
||||
serverVersion: existing[0],
|
||||
clientVersion: profile,
|
||||
resolution: 'server_wins',
|
||||
});
|
||||
continue; // Server wins
|
||||
}
|
||||
await tx
|
||||
.update(schema_1.repeatProfiles)
|
||||
.set({
|
||||
name: profile.name,
|
||||
repeat: profile.repeat,
|
||||
repeatInterval: profile.repeatInterval,
|
||||
repeatDays: profile.repeatDays,
|
||||
updatedAt: profile.updatedAt,
|
||||
})
|
||||
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.repeatProfiles.id, profile.id), (0, drizzle_orm_1.eq)(schema_1.repeatProfiles.userId, userId)));
|
||||
}
|
||||
else {
|
||||
await tx.insert(schema_1.repeatProfiles).values({
|
||||
...profile,
|
||||
userId,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// Process friendships
|
||||
if (data.changes.friendships && data.changes.friendships.length > 0) {
|
||||
for (const friendship of data.changes.friendships) {
|
||||
const existing = await tx
|
||||
.select()
|
||||
.from(schema_1.friendships)
|
||||
.where((0, drizzle_orm_1.eq)(schema_1.friendships.id, friendship.id))
|
||||
.limit(1);
|
||||
if (existing.length > 0) {
|
||||
// Check for conflict
|
||||
if (existing[0].updatedAt > friendship.updatedAt) {
|
||||
conflicts.push({
|
||||
entity: 'friendships',
|
||||
id: friendship.id,
|
||||
serverVersion: existing[0],
|
||||
clientVersion: friendship,
|
||||
resolution: 'server_wins',
|
||||
});
|
||||
continue; // Server wins
|
||||
}
|
||||
await tx
|
||||
.update(schema_1.friendships)
|
||||
.set({
|
||||
userId: friendship.userId,
|
||||
friendId: friendship.friendId,
|
||||
status: friendship.status,
|
||||
updatedAt: friendship.updatedAt,
|
||||
})
|
||||
.where((0, drizzle_orm_1.eq)(schema_1.friendships.id, friendship.id));
|
||||
}
|
||||
else {
|
||||
await tx.insert(schema_1.friendships).values(friendship);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Sync push error:', error);
|
||||
throw new errorHandler_1.AppError('SERVER_ERROR', 'Failed to process sync push', 500);
|
||||
}
|
||||
res.json({
|
||||
success: true,
|
||||
timestamp,
|
||||
conflicts,
|
||||
});
|
||||
}));
|
||||
exports.default = router;
|
||||
//# sourceMappingURL=sync.js.map
|
||||
Reference in New Issue
Block a user