tia/drizzle/0003_circles.sql
Mannu 5fdb69679d Circle feature: C0–C9 multi-tenant social groups (Sprint 9 + 10)
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>
2026-05-24 01:04:50 +05:30

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;