163 lines
6.1 KiB
PL/PgSQL
163 lines
6.1 KiB
PL/PgSQL
-- ExposedGays v6: Sniffies-style user hosting events (map parties)
|
|
-- Run on SUPABASE_01 after migration_v5_privacy.sql
|
|
|
|
CREATE TABLE IF NOT EXISTS public.hosting_events (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
host_id UUID NOT NULL REFERENCES public.profiles(id) ON DELETE CASCADE,
|
|
title TEXT NOT NULL CHECK (char_length(title) <= 80),
|
|
description TEXT NOT NULL DEFAULT '' CHECK (char_length(description) <= 2000),
|
|
event_type TEXT NOT NULL DEFAULT 'custom'
|
|
CHECK (event_type IN ('cum-dump', 'orgy', 'group', 'pnp', 'vanilla', 'gloryhole', 'custom')),
|
|
status TEXT NOT NULL DEFAULT 'scheduled'
|
|
CHECK (status IN ('scheduled', 'live', 'ended', 'cancelled')),
|
|
starts_at TIMESTAMPTZ NOT NULL,
|
|
ends_at TIMESTAMPTZ NOT NULL,
|
|
location GEOGRAPHY(POINT, 4326) NOT NULL,
|
|
city TEXT,
|
|
auto_accept BOOLEAN NOT NULL DEFAULT false,
|
|
max_guests INTEGER CHECK (max_guests IS NULL OR max_guests BETWEEN 1 AND 500),
|
|
tags TEXT[] NOT NULL DEFAULT '{}',
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
CHECK (ends_at > starts_at)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_hosting_events_host ON public.hosting_events (host_id);
|
|
CREATE INDEX IF NOT EXISTS idx_hosting_events_status ON public.hosting_events (status);
|
|
CREATE INDEX IF NOT EXISTS idx_hosting_events_starts ON public.hosting_events (starts_at);
|
|
CREATE INDEX IF NOT EXISTS idx_hosting_events_location ON public.hosting_events USING GIST (location);
|
|
|
|
CREATE TABLE IF NOT EXISTS public.hosting_event_media (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
event_id UUID NOT NULL REFERENCES public.hosting_events(id) ON DELETE CASCADE,
|
|
media_type TEXT NOT NULL CHECK (media_type IN ('photo', 'video')),
|
|
url TEXT NOT NULL,
|
|
thumbnail_url TEXT,
|
|
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_hosting_event_media_event ON public.hosting_event_media (event_id);
|
|
|
|
CREATE TABLE IF NOT EXISTS public.hosting_event_attendees (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
event_id UUID NOT NULL REFERENCES public.hosting_events(id) ON DELETE CASCADE,
|
|
user_id UUID NOT NULL REFERENCES public.profiles(id) ON DELETE CASCADE,
|
|
status TEXT NOT NULL DEFAULT 'requested'
|
|
CHECK (status IN ('invited', 'requested', 'accepted', 'declined', 'cancelled')),
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
UNIQUE (event_id, user_id)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_hosting_event_attendees_event ON public.hosting_event_attendees (event_id);
|
|
CREATE INDEX IF NOT EXISTS idx_hosting_event_attendees_user ON public.hosting_event_attendees (user_id);
|
|
|
|
-- Max 2 photos + 1 video per event
|
|
CREATE OR REPLACE FUNCTION public.enforce_hosting_event_media_limit()
|
|
RETURNS TRIGGER AS $$
|
|
DECLARE
|
|
photo_count INTEGER;
|
|
video_count INTEGER;
|
|
BEGIN
|
|
SELECT COUNT(*) INTO photo_count
|
|
FROM public.hosting_event_media
|
|
WHERE event_id = NEW.event_id AND media_type = 'photo';
|
|
|
|
SELECT COUNT(*) INTO video_count
|
|
FROM public.hosting_event_media
|
|
WHERE event_id = NEW.event_id AND media_type = 'video';
|
|
|
|
IF NEW.media_type = 'photo' AND photo_count >= 2 THEN
|
|
RAISE EXCEPTION 'Maximum 2 photos per hosting event';
|
|
END IF;
|
|
|
|
IF NEW.media_type = 'video' AND video_count >= 1 THEN
|
|
RAISE EXCEPTION 'Maximum 1 video per hosting event';
|
|
END IF;
|
|
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
DROP TRIGGER IF EXISTS trg_hosting_event_media_limit ON public.hosting_event_media;
|
|
CREATE TRIGGER trg_hosting_event_media_limit
|
|
BEFORE INSERT ON public.hosting_event_media
|
|
FOR EACH ROW EXECUTE FUNCTION public.enforce_hosting_event_media_limit();
|
|
|
|
CREATE OR REPLACE FUNCTION public.touch_hosting_event_updated()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
UPDATE public.hosting_events SET updated_at = now() WHERE id = NEW.event_id;
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
DROP TRIGGER IF EXISTS trg_hosting_event_attendee_touch ON public.hosting_event_attendees;
|
|
CREATE TRIGGER trg_hosting_event_attendee_touch
|
|
AFTER INSERT OR UPDATE ON public.hosting_event_attendees
|
|
FOR EACH ROW EXECUTE FUNCTION public.touch_hosting_event_updated();
|
|
|
|
ALTER TABLE public.hosting_events ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE public.hosting_event_media ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE public.hosting_event_attendees ENABLE ROW LEVEL SECURITY;
|
|
|
|
CREATE POLICY "Hosting events viewable by authenticated users"
|
|
ON public.hosting_events FOR SELECT
|
|
USING (auth.role() = 'authenticated' AND status != 'cancelled');
|
|
|
|
CREATE POLICY "Users can create hosting events"
|
|
ON public.hosting_events FOR INSERT
|
|
WITH CHECK (auth.uid() = host_id);
|
|
|
|
CREATE POLICY "Hosts can update own events"
|
|
ON public.hosting_events FOR UPDATE
|
|
USING (auth.uid() = host_id);
|
|
|
|
CREATE POLICY "Hosts can delete own events"
|
|
ON public.hosting_events FOR DELETE
|
|
USING (auth.uid() = host_id);
|
|
|
|
CREATE POLICY "Event media viewable by authenticated users"
|
|
ON public.hosting_event_media FOR SELECT
|
|
USING (auth.role() = 'authenticated');
|
|
|
|
CREATE POLICY "Hosts manage event media"
|
|
ON public.hosting_event_media FOR ALL
|
|
USING (
|
|
EXISTS (
|
|
SELECT 1 FROM public.hosting_events e
|
|
WHERE e.id = event_id AND e.host_id = auth.uid()
|
|
)
|
|
);
|
|
|
|
CREATE POLICY "Attendees viewable by authenticated users"
|
|
ON public.hosting_event_attendees FOR SELECT
|
|
USING (auth.role() = 'authenticated');
|
|
|
|
CREATE POLICY "Users manage own attendance"
|
|
ON public.hosting_event_attendees FOR INSERT
|
|
WITH CHECK (auth.uid() = user_id);
|
|
|
|
CREATE POLICY "Users update own attendance"
|
|
ON public.hosting_event_attendees FOR UPDATE
|
|
USING (auth.uid() = user_id OR EXISTS (
|
|
SELECT 1 FROM public.hosting_events e
|
|
WHERE e.id = event_id AND e.host_id = auth.uid()
|
|
));
|
|
|
|
CREATE POLICY "Hosts delete attendees"
|
|
ON public.hosting_event_attendees FOR DELETE
|
|
USING (
|
|
auth.uid() = user_id OR EXISTS (
|
|
SELECT 1 FROM public.hosting_events e
|
|
WHERE e.id = event_id AND e.host_id = auth.uid()
|
|
)
|
|
);
|
|
|
|
ALTER PUBLICATION supabase_realtime ADD TABLE public.hosting_events;
|
|
ALTER PUBLICATION supabase_realtime ADD TABLE public.hosting_event_attendees;
|
|
|
|
INSERT INTO storage.buckets (id, name, public)
|
|
VALUES ('hosting-events', 'hosting-events', true)
|
|
ON CONFLICT (id) DO NOTHING; |