hwhub/supabase_migration.sql

235 lines
9.2 KiB
PL/PgSQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- ══════════════════════════════════════════════════════════════════════
-- HW Collector Hub Multi-Collection Migration
-- Run this in the Supabase SQL Editor (one-time).
-- ══════════════════════════════════════════════════════════════════════
-- 1) Collections table
CREATE TABLE IF NOT EXISTS collections (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
name TEXT NOT NULL,
description TEXT,
owner_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ DEFAULT now()
);
-- 2) Collection members (join table)
CREATE TABLE IF NOT EXISTS collection_members (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
collection_id UUID NOT NULL REFERENCES collections(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
role TEXT NOT NULL DEFAULT 'member'
CHECK (role IN ('owner', 'member')),
created_at TIMESTAMPTZ DEFAULT now(),
UNIQUE(collection_id, user_id)
);
-- 3) Add collection_id column to hotwheels
ALTER TABLE hotwheels
ADD COLUMN IF NOT EXISTS collection_id UUID REFERENCES collections(id) ON DELETE CASCADE;
-- ── RLS: collections ──────────────────────────────────────────────────
ALTER TABLE collections ENABLE ROW LEVEL SECURITY;
-- Users can see collections they are a member of.
CREATE POLICY "Members can view collections"
ON collections FOR SELECT
USING (
owner_id = auth.uid()
OR id IN (
SELECT collection_id FROM collection_members
WHERE user_id = auth.uid()
)
);
-- Any authenticated user can create a collection.
CREATE POLICY "Authenticated users can create collections"
ON collections FOR INSERT
WITH CHECK (auth.uid() = owner_id);
-- Only the owner can update.
CREATE POLICY "Owner can update collection"
ON collections FOR UPDATE
USING (owner_id = auth.uid());
-- Only the owner can delete.
CREATE POLICY "Owner can delete collection"
ON collections FOR DELETE
USING (owner_id = auth.uid());
-- ── RLS: collection_members ──────────────────────────────────────────
ALTER TABLE collection_members ENABLE ROW LEVEL SECURITY;
-- Users can see their own memberships (avoids infinite recursion).
CREATE POLICY "Users can view own memberships"
ON collection_members FOR SELECT
USING (user_id = auth.uid());
-- Owner can add members.
CREATE POLICY "Owner can add members"
ON collection_members FOR INSERT
WITH CHECK (
collection_id IN (
SELECT id FROM collections WHERE owner_id = auth.uid()
)
);
-- Owner can remove members.
CREATE POLICY "Owner can remove members"
ON collection_members FOR DELETE
USING (
collection_id IN (
SELECT id FROM collections WHERE owner_id = auth.uid()
)
OR user_id = auth.uid() -- members can remove themselves
);
-- ── RLS: hotwheels (update existing) ────────────────────────────────
-- Drop old policies first (they were user_id based).
DROP POLICY IF EXISTS "Enable read access for all users" ON hotwheels;
DROP POLICY IF EXISTS "Enable insert for authenticated users only" ON hotwheels;
DROP POLICY IF EXISTS "Enable update for users based on user_id" ON hotwheels;
DROP POLICY IF EXISTS "Enable delete for users based on user_id" ON hotwheels;
-- Members of a collection can see its cars.
CREATE POLICY "Collection members can view cars"
ON hotwheels FOR SELECT
USING (
collection_id IN (
SELECT collection_id FROM collection_members
WHERE user_id = auth.uid()
)
);
-- Members can add cars.
CREATE POLICY "Collection members can insert cars"
ON hotwheels FOR INSERT
WITH CHECK (
collection_id IN (
SELECT collection_id FROM collection_members
WHERE user_id = auth.uid()
)
);
-- Members can update cars in their collections.
CREATE POLICY "Collection members can update cars"
ON hotwheels FOR UPDATE
USING (
collection_id IN (
SELECT collection_id FROM collection_members
WHERE user_id = auth.uid()
)
);
-- Members can delete cars from their collections.
CREATE POLICY "Collection members can delete cars"
ON hotwheels FOR DELETE
USING (
collection_id IN (
SELECT collection_id FROM collection_members
WHERE user_id = auth.uid()
)
);
-- ══════════════════════════════════════════════════════════════════════
-- MIGRATION HELPER: Move existing cars into a default collection
-- for each user that already has cars.
-- ══════════════════════════════════════════════════════════════════════
DO $$
DECLARE
_user RECORD;
_coll UUID;
BEGIN
FOR _user IN
SELECT DISTINCT user_id FROM hotwheels WHERE collection_id IS NULL
LOOP
-- Create a default "My Collection" for this user.
INSERT INTO collections (name, owner_id)
VALUES ('My Collection', _user.user_id)
RETURNING id INTO _coll;
-- Owner is also a member.
INSERT INTO collection_members (collection_id, user_id, role)
VALUES (_coll, _user.user_id, 'owner');
-- Assign all existing cars to this collection.
UPDATE hotwheels
SET collection_id = _coll
WHERE user_id = _user.user_id AND collection_id IS NULL;
END LOOP;
END $$;
-- After migration, make collection_id NOT NULL.
ALTER TABLE hotwheels ALTER COLUMN collection_id SET NOT NULL;
-- ══════════════════════════════════════════════════════════════════════
-- RPC: Look up a user's ID by email (for inviting)
-- ══════════════════════════════════════════════════════════════════════
CREATE OR REPLACE FUNCTION get_user_id_by_email(lookup_email TEXT)
RETURNS UUID
LANGUAGE sql
SECURITY DEFINER -- runs as postgres, can read auth.users
SET search_path = ''
AS $$
SELECT id FROM auth.users
WHERE email = lower(lookup_email)
LIMIT 1;
$$;
-- ══════════════════════════════════════════════════════════════════════
-- RPC: Get collection members with their emails
-- ══════════════════════════════════════════════════════════════════════
CREATE OR REPLACE FUNCTION get_collection_members(p_collection_id UUID)
RETURNS TABLE(
id UUID,
user_id UUID,
email TEXT,
role TEXT,
created_at TIMESTAMPTZ
)
LANGUAGE sql
SECURITY DEFINER
SET search_path = ''
AS $$
SELECT
cm.id,
cm.user_id,
u.email,
cm.role,
cm.created_at
FROM public.collection_members cm
JOIN auth.users u ON u.id = cm.user_id
WHERE cm.collection_id = p_collection_id
AND cm.collection_id IN (
SELECT cm2.collection_id FROM public.collection_members cm2
WHERE cm2.user_id = auth.uid()
)
ORDER BY
CASE cm.role WHEN 'owner' THEN 0 ELSE 1 END,
cm.created_at;
$$;
-- ══════════════════════════════════════════════════════════════════════
-- Community reporting: car_reports
-- ══════════════════════════════════════════════════════════════════════
CREATE TABLE IF NOT EXISTS car_reports (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
hw_id VARCHAR NOT NULL REFERENCES global_cars(hw_id) ON DELETE CASCADE,
hotwheels_id BIGINT REFERENCES hotwheels(id) ON DELETE SET NULL,
reporter_user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
reason TEXT NOT NULL,
note TEXT,
status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open', 'reviewed', 'resolved', 'dismissed')),
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
ALTER TABLE car_reports ENABLE ROW LEVEL SECURITY;
-- Reporters can create reports only for themselves.
CREATE POLICY "Users can create own car reports"
ON car_reports FOR INSERT
WITH CHECK (reporter_user_id = auth.uid());
-- Reporters can see their own reports.
CREATE POLICY "Users can view own car reports"
ON car_reports FOR SELECT
USING (reporter_user_id = auth.uid());