From f6949a062f9f9ca72592b75d8f79e92c2f28d229 Mon Sep 17 00:00:00 2001 From: kurihada Date: Tue, 24 Feb 2026 19:46:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=88=BF=E9=97=B4=2024=20=E5=B0=8F?= =?UTF-8?q?=E6=97=B6=E8=87=AA=E5=8A=A8=E8=BF=87=E6=9C=9F=EF=BC=8C=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=20TTL=20=E6=B8=85=E7=90=86=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../20260224114226_add_room_ttl/migration.sql | 20 +++++++++++ prisma/schema.prisma | 1 + src/lib/store.ts | 33 +++++++++++++++++-- 3 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 prisma/migrations/20260224114226_add_room_ttl/migration.sql diff --git a/prisma/migrations/20260224114226_add_room_ttl/migration.sql b/prisma/migrations/20260224114226_add_room_ttl/migration.sql new file mode 100644 index 0000000..27c4aa7 --- /dev/null +++ b/prisma/migrations/20260224114226_add_room_ttl/migration.sql @@ -0,0 +1,20 @@ +/* + Warnings: + + - Added the required column `expiresAt` to the `Room` table without a default value. This is not possible if the table is not empty. + +*/ +-- RedefineTables +PRAGMA defer_foreign_keys=ON; +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Room" ( + "id" TEXT NOT NULL PRIMARY KEY, + "data" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "expiresAt" DATETIME NOT NULL +); +INSERT INTO "new_Room" ("createdAt", "data", "id", "expiresAt") SELECT "createdAt", "data", "id", "createdAt" FROM "Room"; +DROP TABLE "Room"; +ALTER TABLE "new_Room" RENAME TO "Room"; +PRAGMA foreign_keys=ON; +PRAGMA defer_foreign_keys=OFF; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 8fa6f1c..8f0ede4 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -11,4 +11,5 @@ model Room { id String @id data String createdAt DateTime @default(now()) + expiresAt DateTime } diff --git a/src/lib/store.ts b/src/lib/store.ts index 5097ae0..2f07990 100644 --- a/src/lib/store.ts +++ b/src/lib/store.ts @@ -1,6 +1,8 @@ import { prisma } from "./prisma"; import { Restaurant } from "@/types"; +const ROOM_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours + export interface RoomData { users: string[]; restaurants: Restaurant[]; @@ -23,7 +25,29 @@ function normalize(raw: Partial): RoomData { }; } +let lastCleanup = 0; +const CLEANUP_INTERVAL_MS = 60 * 60 * 1000; // at most once per hour + +async function cleanupExpiredRooms() { + const now = Date.now(); + if (now - lastCleanup < CLEANUP_INTERVAL_MS) return; + lastCleanup = now; + + try { + const { count } = await prisma.room.deleteMany({ + where: { expiresAt: { lt: new Date() } }, + }); + if (count > 0) { + console.log(`Cleaned up ${count} expired room(s)`); + } + } catch (e) { + console.error("Room cleanup failed:", e); + } +} + export async function createRoom(restaurants: Restaurant[]): Promise { + await cleanupExpiredRooms(); + const data: RoomData = { users: [], restaurants, @@ -32,6 +56,7 @@ export async function createRoom(restaurants: Restaurant[]): Promise { match: null, }; + const expiresAt = new Date(Date.now() + ROOM_TTL_MS); let roomId: string; let attempts = 0; @@ -40,7 +65,7 @@ export async function createRoom(restaurants: Restaurant[]): Promise { const existing = await prisma.room.findUnique({ where: { id: roomId } }); if (!existing) { await prisma.room.create({ - data: { id: roomId, data: JSON.stringify(data) }, + data: { id: roomId, data: JSON.stringify(data), expiresAt }, }); return roomId; } @@ -49,7 +74,7 @@ export async function createRoom(restaurants: Restaurant[]): Promise { roomId = generateRoomId() + String(Date.now()).slice(-2); await prisma.room.create({ - data: { id: roomId, data: JSON.stringify(data) }, + data: { id: roomId, data: JSON.stringify(data), expiresAt }, }); return roomId; } @@ -59,6 +84,10 @@ export async function getRoomData( ): Promise { const room = await prisma.room.findUnique({ where: { id: roomId } }); if (!room) return null; + if (room.expiresAt < new Date()) { + prisma.room.delete({ where: { id: roomId } }).catch(() => {}); + return null; + } return normalize(JSON.parse(room.data)); }