Commit Graph

29 Commits

Author SHA1 Message Date
Jarrod Flesch
813e70be1f feat: adds multi-tenant plugin (#10447)
### 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**
```ts
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**
```ts
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.

```ts
multiTenantPlugin({
  collections: {
    navigation: {
      isGlobal: true,
    },
  },
})
```
2025-01-15 14:47:46 -05:00
Elliot DeNolf
4629784c99 ci(scripts): publish-canary script always bump minor, more realistic [skip ci][skip lint] 2025-01-14 21:16:11 -05:00
Elliot DeNolf
1b1dc82cfb feat!: rename @payloadcms/plugin-cloud (#8828)
BREAKING CHANGE: Rename `@payloadcms/plugin-cloud` to
`@payloadcms/payload-cloud`. Anyone using the existing plugin will need
to switch to using the new package.

## Why?

Since v3 will be using _fixed versioning_, all versions of `^3` must be
available. Unfortunately, the `@payloadcms/plugin-cloud` version has
already breached that version number. Renaming will allow it to be on
the same version as other monorepo packages.

Additionally, the name `plugin-cloud` is quite ambiguous and sometimes
is confused with `plugin-cloud-storage`, so using `payload-cloud` feels
like a good move to make this more evident.
2024-10-24 21:19:15 -04:00
Elliot DeNolf
a15765395d chore: add plugin-sentry to publish list 2024-10-10 16:40:01 -04:00
Dan Ribbens
6ef2bdea15 feat!: join field (#7518)
## Description

- Adds a new "join" field type to Payload and is supported by all database adapters
- The UI uses a table view for the new field
- `db-mongodb` changes relationships to be stored as ObjectIDs instead of strings (for now querying works using both types internally to the DB so no data migration should be necessary unless you're querying directly, see breaking changes for details
- Adds a reusable traverseFields utility to Payload to make it easier to work with nested fields, used internally and for plugin maintainers

```ts
export const Categories: CollectionConfig = {
    slug: 'categories',
    fields: [
        {
            name: 'relatedPosts',
            type: 'join',
            collection: 'posts',
            on: 'category',
        }
    ]
}
```

BREAKING CHANGES:
All mongodb relationship and upload values will be stored as MongoDB ObjectIDs instead of strings going forward. If you have existing data and you are querying data directly, outside of Payload's APIs, you get different results. For example, a `contains` query will no longer works given a partial ID of a relationship since the ObjectID requires the whole identifier to work. 

---------

Co-authored-by: Jacob Fletcher <jacobsfletch@gmail.com>
Co-authored-by: James <james@trbl.design>
2024-09-20 11:10:16 -04:00
Elliot DeNolf
3e5c31a024 chore(scripts): add db-vercel-postgres to publish list 2024-08-21 22:55:29 -04:00
Alessio Gravili
ebd43c7763 feat: pre-compile ui and richtext-lexical with react compiler (#7688)
This noticeably improves performance in the admin panel, for example
when there are multiple richtext editors on one page (& likely
performance in other areas too, though I mainly tested rich text).

The babel plugin currently only optimizes files with a 'use client'
directive at the top - thus we have to make sure to add use client
wherever possible, even if it's imported by a parent client component.

There's one single component that broke when it was compiled using the
React compiler (it stopped being reactive and failed one of our admin
e2e tests):
150808f608
opting out of it completely fixed that issue

Fixes https://github.com/payloadcms/payload/issues/7366
2024-08-19 17:31:36 -04:00
Dan Ribbens
09ad6e4280 feat(drizzle): abstract shared sql code to new package (#7320)
- Abstract shared sql code to a new drizzle package
- Adds sqlite package, not ready to publish until drizzle patches some
issues
- Add `transactionOptions` to allow customizing or disabling db
transactions
- Adds "experimental" label to the `schemaName` property until drizzle
patches an issue
2024-07-24 12:43:29 -04:00
Jacob Fletcher
904ec0160e feat: adds @payloadcms/live-preview-vue to release pipeline (#7328) 2024-07-24 10:07:34 -04:00
Alessio Gravili
1038e1c228 chore: move to eslint v9 (#7041)
- Upgrades eslint from v8 to v9
- Upgrades all other eslint packages. We will have to do a new
full-project lint, as new rules have been added
- Upgrades husky from v8 to v9
- Upgrades lint-staged from v14 to v15
- Moves the old .eslintrc.cjs file format to the new eslint.config.js
flat file format.

Previously, we were very specific regarding which rules are applied to
which files. Now that `extends` is no longer a thing, I have to use
deepMerge & imports instead.

This is rather uncommon and is not a documented pattern - e.g.
typescript-eslint docs want us to add the default typescript-eslint
rules to the top-level & then disable it in files using the
disable-typechecked config.

However, I hate this opt-out approach. The way I did it here adds a lot
of clarity as to which rules are applied to which files, and is pretty
easy to read. Much less black magic

## .eslintignore

These files are no longer supported (see
https://eslint.org/docs/latest/use/configure/migration-guide#ignoring-files).
I moved the entries to the ignores property in the eslint config. => one
less file in each package folder!
2024-07-09 09:50:37 -04:00
Elliot DeNolf
23c7ab2bc4 ci: add plugin-relationship-object-ids to publish list (#6335) 2024-05-13 10:14:10 -04:00
Elliot DeNolf
5b201392cc ci: add storage-uploadthing 2024-05-10 17:15:22 -04:00
Elliot DeNolf
f9d862d854 ci(scripts): update getPackageRegistryVersions [skip ci] 2024-05-09 23:35:50 -04:00
Elliot DeNolf
f41576dd65 ci: canary releases (#6308) 2024-05-09 23:12:47 -04:00
Elliot DeNolf
19a78297b4 ci: add live-preview and live-preview-react to publish list 2024-05-08 13:48:17 -04:00
Elliot DeNolf
37be06448c ci: more resilient release script (#6202) 2024-05-03 14:39:26 -04:00
Elliot DeNolf
e9004a93a4 ci(scripts): safer package details retrieval 2024-04-25 10:50:41 -04:00
Elliot DeNolf
7ef5493414 ci(scripts): misc improvements 2024-04-24 21:04:12 -04:00
Elliot DeNolf
95b3f6d40d chore(scripts): add new packages to getPackageRegistryVersions 2024-04-23 09:10:25 -04:00
Elliot DeNolf
2fc9885abc chore(scripts): add getPackageRegistryVersions script 2024-04-10 21:26:50 -04:00
Dan Ribbens
19d2a4d1cd chore: esm compatible scripts 2024-03-06 12:24:25 -05:00
Elliot DeNolf
5b86a8d7c9 ci: adjust whitelist of release script 2024-03-04 09:51:22 -05:00
Elliot DeNolf
399b799002 ci: update release script 2024-03-03 13:01:59 -05:00
Elliot DeNolf
6ff42d1627 ci: fixed versioning (#5214)
* chore(deps): add lerna-lite

* feat: update-1

* feat(db-mongodb): update 2

* chore: lerna init

* chore: add version option to lerna config

* chore(ci): add gh usernames to changelog and user root package.json for version

* chore(ci): whitelist poc branches

* chore(ci): add contributors section

* chore(ci): use turbo for prepublishOnly scripts, enable caching

* chore(deps): update turborepo, add execa

* feat(plugin-stripe): adjust type import

* chore: remove lerna-lite

* chore(ci): new and improved release script for fixed versioning

* chore: remove unused lerna-lite packages

* chore: sync root package.json version

* chore: remove remnants of bundler packages

* chore(plugin-seo): update packagea.json from main, disable build

* chore: disable turbo caching

* chore(ci): update release script

* chore: sync pnpm-lock.yaml

* chore: ci cleanup
2024-02-29 16:01:51 -05:00
Elliot DeNolf
62403584ad chore(scripts): cleanup package details log 2023-11-21 10:28:17 -05:00
Elliot DeNolf
00a1ce754a ci: conventional commits changelog (#3843)
* feat(live-preview): another oen

* wip: changelog script

* wippppp

* chore: this worked

* wip: changelog working

* chore(script): working changelog gen

* chore(script): update changelog during release
2023-10-24 14:10:14 -04:00
Elliot DeNolf
c8cc6ea1cc chore(script): more prompts during publish 2023-10-18 21:29:26 -04:00
Elliot DeNolf
f50a392d59 chore(script): update release script [skip ci] 2023-10-17 17:00:14 -04:00
Elliot DeNolf
ff5e174497 chore(script): multiselect publishing prompt 2023-10-15 20:53:16 -04:00