npm package: @rivtor/tenant
Description: Production-ready multi-tenancy and RBAC for B2B SaaS
Author: Rivtor
Latest version: 1.0.1

# @rivtor/tenant

[![npm version](https://badge.fury.io/js/%40rivtor%2Ftenant.svg)](https://www.npmjs.com/package/@rivtor/tenant)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

Production-ready multi-tenancy and RBAC for B2B SaaS applications.

## Overview

`@rivtor/tenant` is the "Tenant Guard" module that solves the nightmare of building Teams, Invites, Roles, Permissions, and Data Isolation for B2B SaaS. It provides automatic tenant_id injection, pre-built components, and complete organization management.

**Key Features:**
- 🏢 **Organization Management** - Create and manage teams/workspaces
- 👥 **Role-Based Access Control** - Admin, Member, Viewer roles with custom permissions
- 📧 **Invite System** - Email-based team invitations
- 🔒 **Data Isolation** - Automatic tenant_id injection in all queries
- ⚛️ **Pre-built Components** - Team switcher, invite UI, settings

## Installation

```bash
npm install @rivtor/tenant
```

## Quick Start

### Server-Side (API Routes)

```typescript
import { tenant } from '@rivtor/tenant';

// Create organization
export async function POST(request: Request) {
  const { name } = await request.json();
  const user = await auth.requireUser(request);
  
  const org = await tenant.createOrganization({
    name,
    ownerId: user.id,
  });
  
  return Response.json({ organization: org });
}

// Invite member
export async function POST(request: Request) {
  const { email, role, organizationId } = await request.json();
  
  await tenant.inviteMember({
    organizationId,
    email,
    role: 'member', // or 'admin', 'viewer'
  });
  
  return Response.json({ success: true });
}
```

### Client-Side (React Components)

```tsx
import { RivtorTeamSwitcher, RivtorInviteMember } from '@rivtor/tenant/react';

// Navbar with team switcher
export default function Navbar() {
  return (
    <nav>
      <RivtorTeamSwitcher />
    </nav>
  );
}

// Settings page with invite UI
export default function TeamSettings() {
  return (
    <div>
      <h1>Team Settings</h1>
      <RivtorInviteMember role="admin" />
    </div>
  );
}
```

### Middleware (Automatic Tenant Injection)

```typescript
// middleware.ts
import { createTenantMiddleware } from '@rivtor/tenant';

export const middleware = createTenantMiddleware({
  // All database queries automatically include tenant_id
  enforceIsolation: true,
});

export const config = {
  matcher: ['/api/:path*', '/dashboard/:path*'],
};
```

## API Reference

### Server Methods

- `createOrganization(options)` - Create new organization
- `inviteMember(options)` - Send organization invite
- `acceptInvite(token)` - Accept organization invite
- `setMemberRole(options)` - Update member role
- `removeMember(options)` - Remove member from organization
- `getOrganizationMembers(orgId)` - List all members
- `getUserOrganizations(userId)` - Get user's organizations

### Client Methods

- `switchOrganization(orgId)` - Switch active organization
- `getCurrentOrganization()` - Get current active organization

### React Components

- `<RivtorTeamSwitcher />` - Notion-style workspace switcher
- `<RivtorInviteMember role="admin" />` - Invite member UI

## Environment Variables

```bash
# Supabase (Required)
NEXT_PUBLIC_SUPABASE_URL=your_supabase_url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key

# App Configuration
NEXT_PUBLIC_APP_URL=https://yourapp.com
SUPPORT_EMAIL=support@yourapp.com
```

## Database Schema

The package expects the following tables:

```sql
-- Organizations
CREATE TABLE organizations (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  name TEXT NOT NULL,
  owner_id UUID NOT NULL REFERENCES auth.users(id),
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Organization Members
CREATE TABLE organization_members (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
  user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
  role TEXT NOT NULL CHECK (role IN ('admin', 'member', 'viewer')),
  created_at TIMESTAMPTZ DEFAULT NOW(),
  UNIQUE(organization_id, user_id)
);

-- Invites
CREATE TABLE organization_invites (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
  email TEXT NOT NULL,
  role TEXT NOT NULL,
  token TEXT UNIQUE NOT NULL,
  expires_at TIMESTAMPTZ NOT NULL,
  created_at TIMESTAMPTZ DEFAULT NOW()
);
```

## License

MIT © Rivtor

## Support

- Email: legal@rivtor.com
- Documentation: [GitHub Wiki](https://github.com/rivtor/rivtor-tenant/wiki)

---

**Built with care by [Rivtor](https://rivtor.com)**