From f09450e7ea0192f10da514d44bc16591cdc0c251 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Sat, 30 Aug 2025 07:52:05 +0200 Subject: [PATCH] fix: Update unique db constraint to account for revoked share links (#10022) * fix: Update unique db constraint to account for revoked share links closes #10017 * Remove pointless try/catch * Switch order of migrations to ensure no 'dead zone' without constraint --- ...nique-constraint-with-revoked-condition.js | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 server/migrations/20250826204500-modify-shares-unique-constraint-with-revoked-condition.js diff --git a/server/migrations/20250826204500-modify-shares-unique-constraint-with-revoked-condition.js b/server/migrations/20250826204500-modify-shares-unique-constraint-with-revoked-condition.js new file mode 100644 index 0000000000..0213080178 --- /dev/null +++ b/server/migrations/20250826204500-modify-shares-unique-constraint-with-revoked-condition.js @@ -0,0 +1,30 @@ +"use strict"; + +module.exports = { + async up(queryInterface, Sequelize) { + // Create a partial unique index that only applies when revokedAt is NULL + // This ensures that only non-revoked shares have the unique constraint + await queryInterface.sequelize.query( + `CREATE UNIQUE INDEX CONCURRENTLY "shares_urlId_teamId_not_revoked_uk" + ON "shares" ("urlId", "teamId") + WHERE "revokedAt" IS NULL;` + ); + + // Remove the existing unique constraint + await queryInterface.removeConstraint("shares", "shares_urlId_teamId_uk"); + }, + + async down(queryInterface, Sequelize) { + // Restore the original unique constraint + await queryInterface.addConstraint("shares", { + fields: ["urlId", "teamId"], + type: "unique", + name: "shares_urlId_teamId_uk", + }); + + // Remove the partial unique index + await queryInterface.sequelize.query( + `DROP INDEX CONCURRENTLY IF EXISTS "shares_urlId_teamId_not_revoked_uk";` + ); + }, +};