Adds full circle functionality — private social groups for trusted families to share milestones, memories, and posts with reactions and comments. - 7-table DB migration: circles, members, invites, posts, comments, reactions, reports - 11 API routes: create/list circles, posts feed, comments, emoji reactions, invite tokens, join flow, member management, reporting - 3 new pages: /circle (list), /circle/[id] (feed + PostCard + CreatePostModal), /circle/join/[token] - Copy-on-share for memory photos (independent R2 objects, never references originals) - Admin controls: invite generation, member promote/demote/remove, last-admin guard - C9 privacy consent screen before first post - Menu entry added Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
87 lines
3.8 KiB
SQL
87 lines
3.8 KiB
SQL
-- C0: Circle multi-tenant social tables
|
|
-- Security model: RLS enabled (deny-by-default at DB layer).
|
|
-- App-level enforcement via requireFamily() + WHERE family_id checks
|
|
-- mirrors the existing pattern used for all other tables in this codebase.
|
|
|
|
CREATE TABLE circles (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
name text NOT NULL,
|
|
created_by uuid NOT NULL REFERENCES families(id),
|
|
created_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE circle_members (
|
|
circle_id uuid NOT NULL REFERENCES circles(id) ON DELETE CASCADE,
|
|
family_id uuid NOT NULL REFERENCES families(id),
|
|
role text NOT NULL DEFAULT 'member', -- admin | member
|
|
joined_at timestamptz NOT NULL DEFAULT now(),
|
|
PRIMARY KEY (circle_id, family_id)
|
|
);
|
|
|
|
CREATE TABLE circle_invites (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
circle_id uuid NOT NULL REFERENCES circles(id) ON DELETE CASCADE,
|
|
token text NOT NULL UNIQUE, -- crypto-random, unguessable
|
|
created_by uuid NOT NULL REFERENCES families(id),
|
|
expires_at timestamptz NOT NULL,
|
|
consumed_at timestamptz,
|
|
created_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE circle_posts (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
circle_id uuid NOT NULL REFERENCES circles(id) ON DELETE CASCADE,
|
|
author_family_id uuid NOT NULL REFERENCES families(id),
|
|
body text,
|
|
image_key text, -- R2 key under circle-posts/{id}/ prefix
|
|
source_kind text, -- NULL | 'milestone' | 'memory'
|
|
created_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE circle_post_comments (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
post_id uuid NOT NULL REFERENCES circle_posts(id) ON DELETE CASCADE,
|
|
author_family_id uuid NOT NULL REFERENCES families(id),
|
|
body text NOT NULL,
|
|
created_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE circle_post_reactions (
|
|
post_id uuid NOT NULL REFERENCES circle_posts(id) ON DELETE CASCADE,
|
|
family_id uuid NOT NULL REFERENCES families(id),
|
|
emoji text NOT NULL,
|
|
PRIMARY KEY (post_id, family_id, emoji)
|
|
);
|
|
|
|
CREATE TABLE post_reports (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
post_id uuid NOT NULL REFERENCES circle_posts(id) ON DELETE CASCADE,
|
|
reported_by uuid NOT NULL REFERENCES families(id),
|
|
reason text,
|
|
created_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
|
|
-- Indexes for common query patterns
|
|
CREATE INDEX circle_members_family_idx ON circle_members(family_id);
|
|
CREATE INDEX circle_members_circle_idx ON circle_members(circle_id);
|
|
CREATE INDEX circle_posts_circle_idx ON circle_posts(circle_id);
|
|
CREATE INDEX circle_posts_author_idx ON circle_posts(author_family_id);
|
|
CREATE INDEX circle_comments_post_idx ON circle_post_comments(post_id);
|
|
CREATE INDEX circle_reactions_post_idx ON circle_post_reactions(post_id);
|
|
CREATE INDEX circle_invites_token_idx ON circle_invites(token);
|
|
|
|
-- Enable RLS (deny-by-default at DB layer).
|
|
-- The app connects as tia_app which handles row visibility via
|
|
-- explicit WHERE clauses in every query (requireFamily() pattern).
|
|
ALTER TABLE circles ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE circle_members ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE circle_invites ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE circle_posts ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE circle_post_comments ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE circle_post_reactions ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE post_reports ENABLE ROW LEVEL SECURITY;
|
|
|
|
-- Grant app role full access (app enforces row visibility itself)
|
|
GRANT ALL ON circles, circle_members, circle_invites, circle_posts,
|
|
circle_post_comments, circle_post_reactions, post_reports
|
|
TO tia_app;
|