/** * Setup tables */ /* ---------------------------------------- Setup tables --------------------------------------- */ -- Profiles (Exposed to other users) CREATE TABLE public.profiles ( -- Primary key (Foreign key to auth.users) id UUID NOT NULL PRIMARY KEY REFERENCES auth.users ON UPDATE CASCADE ON DELETE CASCADE, -- Random color color TEXT NOT NULL DEFAULT utilities.get_random_color(), -- Random emoji emoji TEXT NOT NULL DEFAULT utilities.get_random_emoji() ); -- User locations CREATE TABLE public.locations ( -- Primary key id UUID NOT NULL PRIMARY KEY DEFAULT uuid_generate_v4(), -- User ID (Foreign key to auth.users) user_id UUID NOT NULL REFERENCES auth.users ON UPDATE CASCADE ON DELETE CASCADE DEFAULT auth.uid(), -- Creation timestamp created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() CHECK (created_at <= NOW()), -- Location (EPSG4326 - used by the W3C geolocation API) location extensions.GEOGRAPHY(POINT, 4326) NOT NULL ); -- Posts CREATE TABLE public.posts ( -- Primary key id UUID NOT NULL PRIMARY KEY DEFAULT uuid_generate_v4(), -- Private poster user ID (Foreign key to auth.users) private_poster_id UUID NOT NULL REFERENCES auth.users ON UPDATE CASCADE ON DELETE CASCADE DEFAULT auth.uid(), -- Whether or not the post is anonymous private_anonymous BOOLEAN NOT NULL DEFAULT FALSE, -- Public poster ID (Only show if the post is not anonymous) poster_id UUID NULL GENERATED ALWAYS AS ( CASE WHEN private_anonymous THEN NULL ELSE private_poster_id END ) STORED, -- Private post filter location (EPSG4326 - used by the W3C geolocation API) private_location extensions.GEOGRAPHY(POINT, 4326) NOT NULL DEFAULT public.get_latest_location(), -- Creation timestamp created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() CHECK (created_at <= NOW()), -- Filter radius in meters (Clamped between 500 meters and 50 kilometers) radius DOUBLE PRECISION NOT NULL CHECK (500 <= radius AND radius <= 50000), -- Plain-text content (Up to 300 characters) content VARCHAR(300) NOT NULL, -- Whether or not the post has media (e.g.: an image or video) -- Note: media is stored in the `media` bucket with the name `posts/[Post ID]`, where `[Post ID]` refers to the `id` column of this table. -- Therefore, media can only be uploaded after a row is inserted into this table and its `id` column is retrieved. has_media BOOLEAN NOT NULL DEFAULT FALSE, -- Media blur hash (Up to 6 x 8 components) blur_hash VARCHAR(100) NULL, -- Media aspect ratio (Used to prevent layout shifts) aspect_ratio DOUBLE PRECISION NULL, -- View count views BIGINT NOT NULL DEFAULT 0 CHECK (views >= 0), -- Comment count comments BIGINT NOT NULL DEFAULT 0 CHECK (views >= 0), -- Upvote count upvotes BIGINT NOT NULL DEFAULT 0 CHECK (views >= 0), -- Downvote count downvotes BIGINT NOT NULL DEFAULT 0 CHECK (views >= 0), CHECK ( (has_media AND blur_hash IS NOT NULL AND aspect_ratio IS NOT NULL) OR (NOT has_media AND blur_hash IS NULL AND aspect_ratio IS NULL) ) ); -- Post views CREATE TABLE public.post_views ( -- Primary key id UUID NOT NULL PRIMARY KEY DEFAULT uuid_generate_v4(), -- Post ID (Foreign key to public.posts) post_id UUID NOT NULL REFERENCES public.posts ON UPDATE CASCADE ON DELETE CASCADE, -- Viewer user ID (Foreign key to auth.users) viewer_id UUID NOT NULL REFERENCES auth.users ON UPDATE CASCADE ON DELETE CASCADE DEFAULT auth.uid(), -- Creation timestamp created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() CHECK (created_at <= NOW()), -- Ensure the viewer can only view once per post UNIQUE (viewer_id, post_id) ); -- Post votes CREATE TABLE public.post_votes ( -- Primary key id UUID NOT NULL PRIMARY KEY DEFAULT uuid_generate_v4(), -- Voter user ID (Foreign key to auth.users) voter_id UUID NOT NULL REFERENCES auth.users ON UPDATE CASCADE ON DELETE CASCADE DEFAULT auth.uid(), -- Post ID (Foreign key to public.posts) post_id UUID NOT NULL REFERENCES public.posts ON UPDATE CASCADE ON DELETE CASCADE, -- Creation timestamp created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() CHECK (created_at <= NOW()), -- Whether the vote is an upvote (true) or a downvote (false) upvote BOOLEAN NOT NULL, -- Ensure the voter can only vote once per post UNIQUE (voter_id, post_id) ); -- Post reports CREATE TABLE public.post_reports ( -- Primary key id UUID NOT NULL PRIMARY KEY DEFAULT uuid_generate_v4(), -- Reporter user ID (Foreign key to auth.users) reporter_id UUID NOT NULL REFERENCES auth.users ON UPDATE CASCADE ON DELETE CASCADE DEFAULT auth.uid(), -- Post ID (Foreign key to public.posts) post_id UUID NOT NULL REFERENCES public.posts ON UPDATE CASCADE ON DELETE CASCADE, -- Creation timestamp created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() CHECK (created_at <= NOW()), -- Ensure the reporter can only report once per post UNIQUE (reporter_id, post_id) ); -- Comments (Nestable) CREATE TABLE public.comments ( -- Primary key id UUID NOT NULL PRIMARY KEY DEFAULT uuid_generate_v4(), -- Private commenter user ID (Foreign key to auth.users) private_commenter_id UUID NOT NULL REFERENCES auth.users ON UPDATE CASCADE ON DELETE CASCADE DEFAULT auth.uid(), -- Whether or not the comment is anonymous private_anonymous BOOLEAN NOT NULL DEFAULT FALSE, -- Public commenter ID (Only show if the comment is not anonymous) commenter_id UUID NULL GENERATED ALWAYS AS ( CASE WHEN private_anonymous THEN NULL ELSE private_commenter_id END ) STORED, -- Parent post ID (Foreign key to public.posts) post_id UUID NOT NULL REFERENCES public.posts ON UPDATE CASCADE ON DELETE CASCADE, -- Parent comment ID (Foreign key to public.comments) parent_id UUID NULL REFERENCES public.comments ON UPDATE CASCADE ON DELETE CASCADE, -- Creation timestamp created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() CHECK (created_at <= NOW()), -- Comment content (Up to 1000 characters) content VARCHAR(1000) NOT NULL, -- View count views BIGINT NOT NULL DEFAULT 0 CHECK (views >= 0), -- Upvote count upvotes BIGINT NOT NULL DEFAULT 0 CHECK (views >= 0), -- Downvote count downvotes BIGINT NOT NULL DEFAULT 0 CHECK (views >= 0) ); -- Comment view CREATE TABLE public.comment_views ( -- Primary key id UUID NOT NULL PRIMARY KEY DEFAULT uuid_generate_v4(), -- Comment ID (Foreign key to public.comments) comment_id UUID NOT NULL REFERENCES public.comments ON UPDATE CASCADE ON DELETE CASCADE, -- Viewer user ID (Foreign key to auth.users) viewer_id UUID NOT NULL REFERENCES auth.users ON UPDATE CASCADE ON DELETE CASCADE DEFAULT auth.uid(), -- Creation timestamp created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() CHECK (created_at <= NOW()), -- Ensure the viewer can only view once per comment UNIQUE (viewer_id, comment_id) ); -- Comment votes CREATE TABLE public.comment_votes ( -- Primary key id UUID NOT NULL PRIMARY KEY DEFAULT uuid_generate_v4(), -- Voter user ID (Foreign key to auth.users) voter_id UUID NOT NULL REFERENCES auth.users ON UPDATE CASCADE ON DELETE CASCADE DEFAULT auth.uid(), -- Comment ID (Foreign key to public.comments) comment_id UUID NOT NULL REFERENCES public.comments ON UPDATE CASCADE ON DELETE CASCADE, -- Creation timestamp created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() CHECK (created_at <= NOW()), -- Whether the vote is an upvote (true) or a downvote (false) upvote BOOLEAN NOT NULL, -- Ensure the voter can only vote once per comment UNIQUE (voter_id, comment_id) ); -- Comment reports CREATE TABLE public.comment_reports ( -- Primary key id UUID NOT NULL PRIMARY KEY DEFAULT uuid_generate_v4(), -- Reporter user ID (Foreign key to auth.users) reporter_id UUID NOT NULL REFERENCES auth.users ON UPDATE CASCADE ON DELETE CASCADE DEFAULT auth.uid(), -- Comment ID (Foreign key to public.comments) comment_id UUID NOT NULL REFERENCES public.comments ON UPDATE CASCADE ON DELETE CASCADE, -- Creation timestamp created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() CHECK (created_at <= NOW()), -- Ensure the reporter can only report once per comment UNIQUE (reporter_id, comment_id) );