- Migration 0003: teams + team_memberships (owner/admin/member, seat_limit)
- POST /v1/teams (Team-tier gated), GET /v1/teams/{id},
GET|POST /v1/teams/{id}/members, DELETE …/members/{userId}
- Add-by-email (existing accounts), seat-cap enforcement (402), role-based
authorization (admin to mutate; non-members get 404; owner unremovable)
- Invite notification email; Tier::can_own_team()
100 backend tests; fmt + clippy clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
24 lines
873 B
SQL
24 lines
873 B
SQL
-- 0003_teams.sql — Team tier data layer (PRD §21 Phase 4).
|
|
|
|
CREATE TABLE teams (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
name TEXT NOT NULL,
|
|
owner_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
-- NULL = unlimited seats; otherwise the billed seat cap.
|
|
seat_limit INTEGER,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE INDEX idx_teams_owner ON teams(owner_id);
|
|
|
|
CREATE TABLE team_memberships (
|
|
team_id UUID NOT NULL REFERENCES teams(id) ON DELETE CASCADE,
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
role TEXT NOT NULL DEFAULT 'member'
|
|
CHECK (role IN ('owner', 'admin', 'member')),
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
PRIMARY KEY (team_id, user_id)
|
|
);
|
|
|
|
CREATE INDEX idx_team_memberships_user ON team_memberships(user_id);
|