Add Google Calendar sync: OAuth connect flow, dedicated Бронирования calendar, best-effort event create/update/delete on booking changes

This commit is contained in:
ogrechko committed 2026-08-29 11:45:46 +00:00
1 parent 14a849bfb9
commit 7654a7b616
14 files changed
+981 -3

No files matched your search

+40 -2
View File
@@ -1,6 +1,7 @@
import { and, eq, gte, lt, inArray, sql } from "drizzle-orm";
import { db } from "@/lib/db/client";
import { bookings, bookingObjects, bookableObjects, users } from "@/lib/db/schema";
import { createCalendarEvent, updateCalendarEvent, deleteCalendarEvent } from "@/lib/google/calendar";
export interface BookableObjectDTO {
id: string;
@@ -52,6 +53,7 @@ export interface BookingDTO {
status: "confirmed" | "cancelled";
objects: { id: string; name: string }[];
bookedByName: string;
googleEventId: string | null;
createdAt: Date;
}
@@ -81,6 +83,7 @@ async function toBookingDTO(row: typeof bookings.$inferSelect): Promise<BookingD
status: row.status,
objects: objRows,
bookedByName: bookedBy?.name ?? "—",
googleEventId: row.googleEventId,
createdAt: row.createdAt,
};
}
@@ -126,7 +129,20 @@ export async function createBooking(input: CreateBookingInput): Promise<BookingD
await db.insert(bookingObjects).values(input.objectIds.map((objectId) => ({ bookingId: row.id, objectId })));
}
return toBookingDTO(row);
const dto = await toBookingDTO(row);
// Best-effort Google Calendar sync — never blocks/fails booking creation.
try {
const googleEventId = await createCalendarEvent(dto);
if (googleEventId) {
await db.update(bookings).set({ googleEventId }).where(eq(bookings.id, row.id));
dto.googleEventId = googleEventId;
}
} catch (err) {
console.error("Google Calendar sync failed for booking", row.bookingNumber, err);
}
return dto;
}
export interface UpdateBookingInput {
@@ -160,7 +176,29 @@ export async function updateBooking(id: string, input: UpdateBookingInput): Prom
}
const row = await db.query.bookings.findFirst({ where: eq(bookings.id, id) });
if (!row) return null;
return toBookingDTO(row);
const dto = await toBookingDTO(row);
// Best-effort Google Calendar sync — never blocks/fails the update.
try {
if (dto.status === "cancelled" && dto.googleEventId) {
await deleteCalendarEvent(dto.googleEventId);
await db.update(bookings).set({ googleEventId: null }).where(eq(bookings.id, id));
dto.googleEventId = null;
} else if (dto.status === "confirmed" && dto.googleEventId) {
await updateCalendarEvent(dto);
} else if (dto.status === "confirmed" && !dto.googleEventId) {
// Never synced yet (created before Google was connected, or just restored from cancelled).
const googleEventId = await createCalendarEvent(dto);
if (googleEventId) {
await db.update(bookings).set({ googleEventId }).where(eq(bookings.id, id));
dto.googleEventId = googleEventId;
}
}
} catch (err) {
console.error("Google Calendar sync failed for booking", dto.bookingNumber, err);
}
return dto;
}
export async function getBooking(id: string): Promise<BookingDTO | null> {