-- Organizations, members, and invitations for multi-tenant support CREATE TABLE IF NOT EXISTS organizations ( id BIGSERIAL PRIMARY KEY, name TEXT NOT NULL, slug TEXT NOT NULL UNIQUE, plan TEXT NOT NULL DEFAULT 'free', status TEXT NOT NULL DEFAULT 'active', created_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT), updated_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT) ); CREATE UNIQUE INDEX IF NOT EXISTS org_slug_idx ON organizations (slug); CREATE INDEX IF NOT EXISTS org_plan_idx ON organizations (plan); CREATE TABLE IF NOT EXISTS organization_members ( id BIGSERIAL PRIMARY KEY, org_id BIGINT NOT NULL REFERENCES organizations(id) ON DELETE CASCADE, user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, role TEXT NOT NULL DEFAULT 'member', status TEXT NOT NULL DEFAULT 'active', joined_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT) ); CREATE UNIQUE INDEX IF NOT EXISTS org_member_unique ON organization_members (org_id, user_id); CREATE INDEX IF NOT EXISTS org_members_org_id_idx ON organization_members (org_id); CREATE INDEX IF NOT EXISTS org_members_user_id_idx ON organization_members (user_id); CREATE TABLE IF NOT EXISTS invitations ( id BIGSERIAL PRIMARY KEY, org_id BIGINT NOT NULL REFERENCES organizations(id) ON DELETE CASCADE, email TEXT NOT NULL, token TEXT NOT NULL UNIQUE, role TEXT NOT NULL DEFAULT 'member', invited_by BIGINT REFERENCES users(id), status TEXT NOT NULL DEFAULT 'pending', expires_at TEXT NOT NULL, created_at TEXT DEFAULT (CURRENT_TIMESTAMP::TEXT) ); CREATE UNIQUE INDEX IF NOT EXISTS invitations_token_unique ON invitations (token); CREATE INDEX IF NOT EXISTS invitations_status_idx ON invitations (status);