Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: adds multi-tenant plugin #10447

Merged
merged 49 commits into from
Jan 15, 2025
Merged

feat: adds multi-tenant plugin #10447

merged 49 commits into from
Jan 15, 2025

Conversation

JarrodMFlesch
Copy link
Contributor

@JarrodMFlesch JarrodMFlesch commented Jan 8, 2025

Multi Tenant Plugin

This PR adds a @payloadcms/plugin-multi-tenant package. The goal is to consolidate a source of truth for multi-tenancy. Currently we are maintaining different implementations for clients, users in discord and our examples repo. When updates or new paradigms arise we need to communicate this with everyone and update code examples which is hard to maintain.

What does it do?

  • adds a tenant selector to the sidebar, above the nav links
  • adds a hidden tenant field to every collection that you specify
  • adds an array field to your users collection, allowing you to assign users to tenants
  • by default combines the access control (to enabled collections) that you define, with access control based on the tenants assigned to user on the request
  • by default adds a baseListFilter that filters the documents shown in the list view with the selected tenant in the admin panel

What does it not do?

  • it does not implement multi-tenancy for your frontend. You will need to query data for specific tenants to build your website/application
  • it does not add a tenants collection, you NEED to add a tenants collection, where you can define what types of fields you would like on it

The plugin config

Most of the options listed below are optional, but it is easier to just lay out all of the configuration options.

TS Type

type MultiTenantPluginConfig<ConfigTypes = unknown> = {
  /**
   * After a tenant is deleted, the plugin will attempt to clean up related documents
   * - removing documents with the tenant ID
   * - removing the tenant from users
   *
   * @default true
   */
  cleanupAfterTenantDelete?: boolean
  /**
   * Automatically
   */
  collections: {
    [key in CollectionSlug]?: {
      /**
       * Set to `true` if you want the collection to behave as a global
       *
       * @default false
       */
      isGlobal?: boolean
      /**
       * Set to `false` if you want to manually apply the baseListFilter
       *
       * @default true
       */
      useBaseListFilter?: boolean
      /**
       * Set to `false` if you want to handle collection access manually without the multi-tenant constraints applied
       *
       * @default true
       */
      useTenantAccess?: boolean
    }
  }
  /**
   * Enables debug mode
   * - Makes the tenant field visible in the admin UI within applicable collections
   *
   * @default false
   */
  debug?: boolean
  /**
   * Enables the multi-tenant plugin
   *
   * @default true
   */
  enabled?: boolean
  /**
   * Field configuration for the field added to all tenant enabled collections
   */
  tenantField?: {
    access?: RelationshipField['access']
    /**
     * The name of the field added to all tenant enabled collections
     *
     * @default 'tenant'
     */
    name?: string
  }
  /**
   * Field configuration for the field added to the users collection
   *
   * If `includeDefaultField` is `false`, you must include the field on your users collection manually
   * This is useful if you want to customize the field or place the field in a specific location
   */
  tenantsArrayField?:
    | {
        /**
         * Access configuration for the array field
         */
        arrayFieldAccess?: ArrayField['access']
        /**
         * When `includeDefaultField` is `true`, the field will be added to the users collection automatically
         */
        includeDefaultField?: true
        /**
         * Additional fields to include on the tenants array field
         */
        rowFields?: Field[]
        /**
         * Access configuration for the tenant field
         */
        tenantFieldAccess?: RelationshipField['access']
      }
    | {
        arrayFieldAccess?: never
        /**
         * When `includeDefaultField` is `false`, you must include the field on your users collection manually
         */
        includeDefaultField?: false
        rowFields?: never
        tenantFieldAccess?: never
      }
  /**
   * The slug for the tenant collection
   *
   * @default 'tenants'
   */
  tenantsSlug?: string
  /**
   * Function that determines if a user has access to _all_ tenants
   *
   * Useful for super-admin type users
   */
  userHasAccessToAllTenants?: (
    user: ConfigTypes extends { user: User } ? ConfigTypes['user'] : User,
  ) => boolean
}

Example usage

import type { Config } from './payload-types'
import { buildConfig } from 'payload'

export default buildConfig({
  plugins: [
    multiTenantPlugin<Config>({
      collections: {
        pages: {},
      },
      userHasAccessToAllTenants: (user) => isSuperAdmin(user),
    }),
  ],
})

How to configure Collections as Globals for multi-tenant

When using multi-tenant, globals need to actually be configured as collections so the content can be specific per tenant.
To do that, you can mark a collection with isGlobal and it will behave like a global and users will not see the list view.

multiTenantPlugin({
  collections: {
    navigation: {
      isGlobal: true,
    },
  },
})

@JarrodMFlesch JarrodMFlesch changed the title feat(plugin): adds multi-tenant plugin feat: adds multi-tenant plugin Jan 8, 2025
@tan-ahmed
Copy link

great work

@JarrodMFlesch JarrodMFlesch merged commit 813e70b into main Jan 15, 2025
67 checks passed
@JarrodMFlesch JarrodMFlesch deleted the feat/multi-tenant-plugin branch January 15, 2025 19:47
Copy link
Contributor

🚀 This is included in version v3.18.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants