fix(db): update FK constraints to SET NULL and expand delete_user anonymization
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -196,7 +196,7 @@ CREATE INDEX idx_patch_tests_name ON patch_tests(name);
|
||||
|
||||
CREATE TABLE user_patch_tests (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
user_id CHAR(12) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
user_id CHAR(12) REFERENCES users(id) ON DELETE SET NULL,
|
||||
patch_test_id CHAR(12) NOT NULL REFERENCES patch_tests(id) ON DELETE CASCADE,
|
||||
tested_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (user_id, patch_test_id)
|
||||
@@ -264,11 +264,11 @@ CREATE TABLE booking_services (
|
||||
CREATE TABLE booking_edit_requests (
|
||||
id CHAR(12) PRIMARY KEY DEFAULT generate_booking_id(),
|
||||
booking_id CHAR(12) NOT NULL REFERENCES bookings(id) ON DELETE CASCADE,
|
||||
requested_by CHAR(12) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
requested_by CHAR(12) REFERENCES users(id) ON DELETE SET NULL,
|
||||
new_start_time TIMESTAMPTZ,
|
||||
new_services CHAR(12)[] DEFAULT '{}', -- Array of service IDs to replace booking_services
|
||||
new_services CHAR(12)[] DEFAULT '{}',
|
||||
notes TEXT,
|
||||
has_overrides BOOLEAN NOT NULL DEFAULT FALSE, -- If TRUE, cannot change services, use existing overrides for duration
|
||||
has_overrides BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT chk_at_least_one_field CHECK (
|
||||
new_start_time IS NOT NULL OR
|
||||
@@ -280,11 +280,12 @@ CREATE TABLE booking_edit_requests (
|
||||
CREATE INDEX idx_booking_edit_requests_booking ON booking_edit_requests(booking_id);
|
||||
|
||||
CREATE TABLE user_referrals (
|
||||
referrer_id CHAR(12) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
referred_id CHAR(12) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
referrer_id CHAR(12) REFERENCES users(id) ON DELETE SET NULL,
|
||||
referred_id CHAR(12) REFERENCES users(id) ON DELETE SET NULL,
|
||||
referred_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
claimed_booking_id CHAR(12) REFERENCES bookings(id) ON DELETE SET NULL,
|
||||
PRIMARY KEY (referrer_id, referred_id)
|
||||
UNIQUE (referrer_id, referred_id)
|
||||
);
|
||||
|
||||
-- =======================================
|
||||
@@ -419,7 +420,7 @@ CREATE INDEX idx_payments_created_at_status ON payments(created_at, status);
|
||||
-- =======================================
|
||||
CREATE TABLE loyalty_redemptions (
|
||||
id CHAR(12) PRIMARY KEY DEFAULT generate_short_id('loyalty_redemptions'),
|
||||
user_id CHAR(12) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
user_id CHAR(12) REFERENCES users(id) ON DELETE SET NULL,
|
||||
stamps_redeemed INT NOT NULL DEFAULT 10,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'pending',
|
||||
applied_to_booking_id CHAR(12) REFERENCES bookings(id) ON DELETE SET NULL,
|
||||
@@ -467,7 +468,7 @@ CREATE INDEX idx_discount_campaigns_type ON discount_campaigns(campaign_type);
|
||||
CREATE TABLE booking_discounts (
|
||||
id CHAR(12) PRIMARY KEY DEFAULT generate_short_id('booking_discounts'),
|
||||
booking_id CHAR(12) NOT NULL REFERENCES bookings(id) ON DELETE CASCADE,
|
||||
user_id CHAR(12) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
user_id CHAR(12) REFERENCES users(id) ON DELETE SET NULL,
|
||||
discount_source VARCHAR(30) NOT NULL,
|
||||
source_id CHAR(12),
|
||||
campaign_type campaign_type,
|
||||
@@ -642,7 +643,7 @@ BEGIN
|
||||
-- Soft-delete all saved cards and clear PCI data
|
||||
UPDATE user_saved_cards
|
||||
SET deleted_at = NOW(),
|
||||
retained_until = NOW(),
|
||||
retained_until = NOW() + INTERVAL '7 years',
|
||||
last_4 = 'XXXX',
|
||||
fingerprint = NULL,
|
||||
exp_month = 1,
|
||||
@@ -668,6 +669,16 @@ BEGIN
|
||||
|
||||
-- Clear notification preferences (no contractual basis after account closure)
|
||||
DELETE FROM user_notification_preferences WHERE user_id = target_id;
|
||||
|
||||
-- Anonymize patch test records (medical-adjacent PII — unlink user, preserve test history)
|
||||
UPDATE user_patch_tests SET user_id = NULL WHERE user_id = target_id;
|
||||
|
||||
-- Anonymize referral relationships (unlink this user's side, preserve the other party's record)
|
||||
UPDATE user_referrals SET referrer_id = NULL WHERE referrer_id = target_id;
|
||||
UPDATE user_referrals SET referred_id = NULL WHERE referred_id = target_id;
|
||||
|
||||
-- Anonymize admin notification references (not customer data — just drop the user link)
|
||||
UPDATE admin_notifications SET user_id = NULL WHERE user_id = target_id;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
@@ -675,9 +686,21 @@ $$ LANGUAGE plpgsql;
|
||||
-- WHY: Guest accounts have no ongoing contractual or legal basis for retention
|
||||
-- WHEN: User requests deletion or GDPR cleanup
|
||||
-- OUTPUT: Full removal of guest account from users table
|
||||
-- SAFETY: FK constraints are SET NULL on financial tables — records survive deletion.
|
||||
-- Saved cards are soft-deleted with 7-year retention before unlinking.
|
||||
CREATE OR REPLACE FUNCTION delete_guest_user(target_id CHAR(12))
|
||||
RETURNS VOID AS $$
|
||||
BEGIN
|
||||
UPDATE user_saved_cards
|
||||
SET deleted_at = NOW(),
|
||||
retained_until = NOW() + INTERVAL '7 years',
|
||||
last_4 = 'XXXX',
|
||||
fingerprint = NULL,
|
||||
exp_month = 1,
|
||||
exp_year = 2000,
|
||||
user_id = NULL
|
||||
WHERE user_id = target_id;
|
||||
|
||||
DELETE FROM users WHERE id = target_id AND account_role = 'guest';
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
@@ -1457,7 +1480,7 @@ $$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TABLE user_saved_cards (
|
||||
id CHAR(12) PRIMARY KEY DEFAULT generate_user_saved_card_id(),
|
||||
user_id CHAR(12) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
user_id CHAR(12) REFERENCES users(id) ON DELETE SET NULL,
|
||||
square_card_id TEXT NOT NULL UNIQUE,
|
||||
brand TEXT NOT NULL,
|
||||
last_4 TEXT NOT NULL,
|
||||
|
||||
Reference in New Issue
Block a user