From 18bd9602b15baee3e5a8c3266bc3e77bf635c5f2 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 3 Aug 2026 14:10:53 +0000 Subject: [PATCH 1/2] fix(rbac): scope role_permissions unique index per property role_permissions was unique on (role_id, permission_key) only. Once any property seeded a system role, later property bootstraps could skip grants for the same pair. Permission checks are property-scoped, so those tenants effectively had no grants. Co-authored-by: telivity-otaip --- .../src/migrations/0016_role_permissions_per_property.sql | 8 ++++++++ packages/database/src/push-schema.ts | 2 +- packages/database/src/schema/rbac.ts | 7 ++++++- 3 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 packages/database/src/migrations/0016_role_permissions_per_property.sql diff --git a/packages/database/src/migrations/0016_role_permissions_per_property.sql b/packages/database/src/migrations/0016_role_permissions_per_property.sql new file mode 100644 index 00000000..37744ae8 --- /dev/null +++ b/packages/database/src/migrations/0016_role_permissions_per_property.sql @@ -0,0 +1,8 @@ +-- role_permissions must be unique per property. The old unique index +-- (role_id, permission_key) made Cloud tenant bootstrap silently skip +-- permission grants once any other property had seeded the same system role. + +DROP INDEX IF EXISTS role_permissions_role_perm_unique; + +CREATE UNIQUE INDEX IF NOT EXISTS role_permissions_role_perm_unique + ON role_permissions (property_id, role_id, permission_key); diff --git a/packages/database/src/push-schema.ts b/packages/database/src/push-schema.ts index 6878971a..83d6f358 100644 --- a/packages/database/src/push-schema.ts +++ b/packages/database/src/push-schema.ts @@ -1020,7 +1020,7 @@ async function main() { permission_key varchar(100) NOT NULL, created_at timestamptz NOT NULL DEFAULT now() )`, - `CREATE UNIQUE INDEX IF NOT EXISTS role_permissions_role_perm_unique ON role_permissions (role_id, permission_key)`, + `CREATE UNIQUE INDEX IF NOT EXISTS role_permissions_role_perm_unique ON role_permissions (property_id, role_id, permission_key)`, `CREATE TABLE IF NOT EXISTS user_roles ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), property_id uuid NOT NULL REFERENCES properties(id), diff --git a/packages/database/src/schema/rbac.ts b/packages/database/src/schema/rbac.ts index 0cff1a41..da6e9207 100644 --- a/packages/database/src/schema/rbac.ts +++ b/packages/database/src/schema/rbac.ts @@ -93,7 +93,12 @@ export const rolePermissions = pgTable( createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), }, (t) => ({ - rolePermUnique: uniqueIndex('role_permissions_role_perm_unique').on(t.roleId, t.permissionKey), + // Grants are property-scoped even when role_id points at a global system role. + rolePermUnique: uniqueIndex('role_permissions_role_perm_unique').on( + t.propertyId, + t.roleId, + t.permissionKey, + ), }), ); From b62dcc7976688e39ad40a20f9b50ddff01728f20 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 3 Aug 2026 17:03:33 +0000 Subject: [PATCH 2/2] fix(desk): filter check-in and room-move dropdowns by room type Check-in and room-move dropdowns listed all vacant rooms regardless of reservation room type, causing API validation errors when a room of a different type was selected. Filter assignable rooms by reservation roomTypeId (matching walk-in behavior). Co-authored-by: telivity-otaip --- apps/dashboard/src/pages/FrontDesk.tsx | 33 +++++++++++++++----------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/apps/dashboard/src/pages/FrontDesk.tsx b/apps/dashboard/src/pages/FrontDesk.tsx index 2faddc04..14f939a5 100644 --- a/apps/dashboard/src/pages/FrontDesk.tsx +++ b/apps/dashboard/src/pages/FrontDesk.tsx @@ -1249,15 +1249,17 @@ export default function FrontDesk() { className="w-full border border-gray-200 rounded-xl px-3 py-2 text-xs focus:outline-none focus:border-telivity-teal focus:ring-2 focus:ring-telivity-teal/10 transition-all bg-white" > - {roomList.map((room) => ( - - ))} + {roomList + .filter((room) => !checkInModal.roomTypeId || !room.roomTypeId || room.roomTypeId === checkInModal.roomTypeId) + .map((room) => ( + + ))} @@ -1431,11 +1433,14 @@ export default function FrontDesk() { className="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:border-telivity-teal" > - {roomList.map((room) => ( - - ))} + {roomList + .filter((room) => room.id !== moveModal.roomId) + .filter((room) => !moveModal.roomTypeId || !room.roomTypeId || room.roomTypeId === moveModal.roomTypeId) + .map((room) => ( + + ))}