refactor: remove obsolete member management API module
Some checks failed
Workflow / ʦ TypeScript (pull_request) Failing after 5m57s
Workflow / ⚫️ Test (pull_request) Has been skipped

This commit is contained in:
T. Zehetbauer
2026-04-03 14:08:31 +02:00
parent 124c6a632a
commit 5c5aaabae5
132 changed files with 10107 additions and 3442 deletions

View File

@@ -0,0 +1,128 @@
-- =====================================================
-- Atomic Booking Creation with Overlap Prevention
--
-- Problem: Creating a booking requires checking room
-- availability, validating capacity, and inserting — all
-- as separate queries. Race conditions can double-book
-- a room for overlapping dates.
--
-- Fix:
-- A) Enable btree_gist extension for exclusion constraints.
-- B) Add GiST exclusion constraint to prevent overlapping
-- bookings for the same room (non-cancelled/no_show).
-- C) Single transactional PG function that locks the room,
-- validates inputs, calculates price, and inserts. The
-- exclusion constraint provides a final safety net.
-- =====================================================
-- A) Enable btree_gist extension (required for exclusion constraints on non-GiST types)
CREATE EXTENSION IF NOT EXISTS btree_gist;
-- B) Add exclusion constraint to prevent overlapping bookings (idempotent)
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint WHERE conname = 'excl_booking_room_dates'
) THEN
ALTER TABLE public.bookings
ADD CONSTRAINT excl_booking_room_dates
EXCLUDE USING gist (
room_id WITH =,
daterange(check_in, check_out) WITH &&
) WHERE (status NOT IN ('cancelled', 'no_show'));
END IF;
END;
$$;
-- C) Atomic booking creation function
CREATE OR REPLACE FUNCTION public.create_booking_atomic(
p_account_id uuid,
p_room_id uuid,
p_guest_id uuid DEFAULT NULL,
p_check_in date DEFAULT NULL,
p_check_out date DEFAULT NULL,
p_adults integer DEFAULT 1,
p_children integer DEFAULT 0,
p_status text DEFAULT 'confirmed',
p_total_price numeric DEFAULT NULL,
p_notes text DEFAULT NULL
)
RETURNS uuid
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = ''
AS $$
DECLARE
v_room record;
v_computed_price numeric(10,2);
v_booking_id uuid;
BEGIN
-- 1. Lock the room row to serialize booking attempts
SELECT * INTO v_room
FROM public.rooms
WHERE id = p_room_id
FOR UPDATE;
-- 2. Validate room exists
IF v_room IS NULL THEN
RAISE EXCEPTION 'Room % not found', p_room_id
USING ERRCODE = 'P0002';
END IF;
-- 3. Validate check_out > check_in
IF p_check_in IS NULL OR p_check_out IS NULL THEN
RAISE EXCEPTION 'check_in and check_out dates are required'
USING ERRCODE = 'P0001';
END IF;
IF p_check_out <= p_check_in THEN
RAISE EXCEPTION 'check_out (%) must be after check_in (%)', p_check_out, p_check_in
USING ERRCODE = 'P0001';
END IF;
-- 4. Validate total guests do not exceed room capacity
IF (p_adults + p_children) > v_room.capacity THEN
RAISE EXCEPTION 'Total guests (%) exceed room capacity (%)', (p_adults + p_children), v_room.capacity
USING ERRCODE = 'P0001';
END IF;
-- 5. Calculate price if not provided
IF p_total_price IS NOT NULL THEN
v_computed_price := p_total_price;
ELSE
v_computed_price := v_room.price_per_night * (p_check_out - p_check_in);
END IF;
-- 6. Insert the booking (exclusion constraint prevents double-booking)
INSERT INTO public.bookings (
account_id,
room_id,
guest_id,
check_in,
check_out,
adults,
children,
status,
total_price,
notes
) VALUES (
p_account_id,
p_room_id,
p_guest_id,
p_check_in,
p_check_out,
p_adults,
p_children,
p_status,
v_computed_price,
p_notes
)
RETURNING id INTO v_booking_id;
-- 7. Return the new booking id
RETURN v_booking_id;
END;
$$;
GRANT EXECUTE ON FUNCTION public.create_booking_atomic(uuid, uuid, uuid, date, date, integer, integer, text, numeric, text) TO authenticated;
GRANT EXECUTE ON FUNCTION public.create_booking_atomic(uuid, uuid, uuid, date, date, integer, integer, text, numeric, text) TO service_role;