diff --git a/api/paidAction/itemCreate.js b/api/paidAction/itemCreate.js index 6c41ee24..0573b80b 100644 --- a/api/paidAction/itemCreate.js +++ b/api/paidAction/itemCreate.js @@ -2,6 +2,7 @@ import { ANON_ITEM_SPAM_INTERVAL, ITEM_SPAM_INTERVAL, PAID_ACTION_PAYMENT_METHOD import { notifyItemMention, notifyItemParents, notifyMention, notifyTerritorySubscribers, notifyUserSubscribers } from '@/lib/webPush' import { getItemMentions, getMentions, performBotBehavior } from './lib/item' import { msatsToSats, satsToMsats } from '@/lib/format' +import { GqlInputError } from '@/lib/error' export const anonable = true @@ -137,7 +138,15 @@ export async function perform (args, context) { } })).bio } else { - item = await tx.item.create({ data: itemData }) + try { + item = await tx.item.create({ data: itemData }) + } catch (err) { + if (err.message.includes('violates exclusion constraint \\"Item_unique_time_constraint\\"')) { + const message = `you already submitted this ${itemData.title ? 'post' : 'comment'}` + throw new GqlInputError(message) + } + throw err + } } // store a reference to the item in the invoice diff --git a/prisma/migrations/20241220000308_fix_duplicate_comments/migration.sql b/prisma/migrations/20241220000308_fix_duplicate_comments/migration.sql new file mode 100644 index 00000000..3f625c62 --- /dev/null +++ b/prisma/migrations/20241220000308_fix_duplicate_comments/migration.sql @@ -0,0 +1,15 @@ +-- create a partial exclusion constraint that prevents insertion of duplicate items within 10 minutes +CREATE EXTENSION IF NOT EXISTS btree_gist; +ALTER TABLE "Item" ADD CONSTRAINT "Item_unique_time_constraint" + EXCLUDE USING gist ( + "userId" WITH =, + -- we use COALESCE so NULL is considered equal to itself. we also use md5 hashes because columns + -- of type TEXT can make index row too large and columns of type CITEXT are not supported by GiST. + COALESCE("parentId", -1) WITH =, + md5(COALESCE("title", '')) WITH =, + md5(COALESCE("subName", '')) WITH =, + md5(COALESCE("text", '')) WITH =, + tsrange(created_at, created_at + INTERVAL '10 minutes') WITH && + ) + -- enforce constraint after this date + WHERE (created_at > '2024-12-30');