Giter VIP home page Giter VIP logo

payload-tenancy's People

Contributors

balinthaller avatar joas8211 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

payload-tenancy's Issues

Upload collection do not respect 'path' strategy

The link/image displayed in upload collections do not respect the 'path' strategy of this plugin.

Dashboard shows link/image for mydomain.com/myfolder/myimage.jpg when it should be mydomain.com/mytenant/myfolder/myimage.jpg

Screenshot 2023-08-24 at 18 02 02

Installing plugin brakes the Admin UI (payload "^1.8.2")

When installed package it brakes the Admin UI. Also tested several times on a blank Payload CMS project just to install payload-tenancy (npm install payload-tenancy) it brakes the admin UI, and when uninstalled the plugin, it is still broken.

React error:
Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:

Tenant parent / child access control

I was wondering if this was supported by default, behind an option or if possible to solve through writing custom hooks for access control.

I have three collections (Users, Media, Businesses)

My admin user is related to main business tenancy (let's call it A)
A user joins under a new business, as a child to my base tenancy (let's call that tenancy B)
User of tenancy B adds a image to the media file
My admin user associated to the base tenancy (A) can't see that image in the media collection

is that suppose to happen? Because when I go into the User and Business collections I can see all the child tenant data

Thank you for any clarification πŸ™πŸ½

Extending field names and visibility

I had a simpler multi tenant application and so far this has been much more seamless, so thank you!

I had two questions though, let me know if it's possible to support.

I am using a custom collection as the tenant ex: tenantCollection: Business.slug and noticed that the field still says tenant instead of the business name. I was wondering if it was possible to extend the configuration so that the relationship here could use another label? Also making it read only unless you are a user from the parent tenant could be handy too

Screen Shot 2023-05-03 at 2 53 03 PM

Also is it possible to hide this field from the table view, when I visit it as an admin user or user from a parent tenant it shows up as undefined
Screen Shot 2023-05-03 at 2 47 31 PM

When I view it was a user of the tenant it says no parent
Screen Shot 2023-05-03 at 2 51 26 PM

hiding the column from this view would be great!

Payload 3.0?

Are you going to upgrade this to Payload 3.0 when that is released?

Accesses aren't refreshed after creating root tenant

After installation, it's required to create the root tenant. Accesses are configured to restrict access to only tenant collection if there's no tenants yet. But after the root tenant is created, admin panel still doesn't show other collections since the accesses has not been refreshed after submitting the form.

Cannot access REST APIs via Users API key

Hi!

I'm having trouble with making REST requests with API requests. I'm using path strategy.

API key usage is enabled on the Users collection:

const Users: CollectionConfig = {
  // ...
  auth: {
    useAPIKey: true,
  },
  admin: {
    useAsTitle: "email",
  },
  // ...
};

When making REST requests like

curl --location 'http://localhost:3000/[TENANT_SLUG]/api/pages/[PAGE_ID]' \
--header 'Authorization: pages API-Key [API KEY GENERATED FOR THE USER ]' 

The request fails with

payload-cms-payload-1  | [13:43:35] ERROR (payload): Forbidden: You are not allowed to perform this action.
payload-cms-payload-1  |     at executeAccess (/home/node/app/node_modules/payload/src/auth/executeAccess.ts:10:43)
payload-cms-payload-1  |     at processTicksAndRejections (node:internal/process/task_queues:95:5)
payload-cms-payload-1  |     at async find (/home/node/app/node_modules/payload/src/collections/operations/find.ts:84:22)
payload-cms-payload-1  |     at async findHandler (/home/node/app/node_modules/payload/src/collections/requestHandlers/find.ts:30:20)

On the other hand, if I enable API keys of the pages collection and use the key generated that way, the request succeeds.

Is there something I'm missing?
Thanks in advance

Globals shared between tenants

First off, thank you for creating this!

The plugin works seamlessly for collections, however, it seems that globals are shared between all tenants.
I have two users and three tenants and no matter which tenant or user I use, the globals update for all of them.
I am not sure if this is by design or an issue with my setup.

Here is my payload config
NVIDIA_Share_A9M5Gk1R9b

Please let me know if there are other details I can provide.

Payload 2.0

Hey I'm thinking about using this with the newly released payload 2.0 and postgres adapter is it compatible with either of those?

2.0: Invalid field error when trying to create additional tenant manually

I have succeeded in creating my user and root tenant however, when trying to create further tenants I am receiving a unauthorized error on the parent field:

ERROR (payload): ValidationError: The following field is invalid: parent

    at beforeChange (/home/node/app/node_modules/payload/src/fields/hooks/beforeChange/index.ts:56:11)

    at processTicksAndRejections (node:internal/process/task_queues:95:5)

    at create (/home/node/app/node_modules/payload/src/collections/operations/create.ts:197:31)

    at createHandler (/home/node/app/node_modules/payload/src/collections/requestHandlers/create.ts:26:17)

Dependencies:

{
  "dependencies": {
    "@aws-sdk/client-s3": "^3.427.0",
    "@aws-sdk/lib-storage": "^3.427.0",
    "@payloadcms/bundler-webpack": "^1.0.3",
    "@payloadcms/db-mongodb": "^1.0.3",
    "@payloadcms/plugin-cloud": "^0.0.10",
    "@payloadcms/plugin-cloud-storage": "^1.0.19",
    "@payloadcms/richtext-lexical": "^0.1.5",
    "cross-env": "^7.0.3",
    "dotenv": "^8.2.0",
    "express": "^4.17.1",
    "payload": "^2.0.13",
    "payload-tenancy": "^2.0.0"
  },


It is also a required field so I can't create any additional tenants.

Any help appreciated!

Screencap

Screen.Recording.2023-10-26.at.3.35.05.PM.mov

Let user switch tenant, multiple tenants per user?

I would like to use this plugin as a multisite (blogging) solution. Our company manages 3 websites and I would like users to be able to switch environments (maybe through their profile), so they will only see the content for the website they are working on.

What I'm confused about is that the second tenant needs to have a parent tenant. Isn't it possible to have tenants act as websites that have nothing to do with each other?

Also, when my user is connected to the root tenant and then switch to a child tenant, it works, but I can never switch back to any other tenant.

Some advice would be appreciated!

Thanks

Postgres Error: "invalid reference to FROM-clause entry for table"

I've set up a simple project with the following config:

export default buildConfig({
  admin: {
    user: Users.slug,
    bundler: webpackBundler(),
  },
  editor: lexicalEditor({}),
  collections: [
    Media,
    Tenants,
    Users
  ],
  typescript: {
    outputFile: path.resolve(__dirname, 'payload-types.ts'),
  },
  graphQL: {
    schemaOutputFile: path.resolve(__dirname, 'generated-schema.graphql'),
  },
  globals: [
    Footer
  ],
  plugins: [
    tenancy({
      isolationStrategy: "domain"
    }),
  ],
  db: postgresAdapter({
    pool: {
      connectionString: process.env.DATABASE_URI,
    },
  }),
})

And here's my Footer global:

export const Footer: GlobalConfig = {
  slug: 'footer',
  access: {
    update: isAdmin,
  },
  fields: [
    {
      name: 'tenant',
      type: 'relationship',
      relationTo: 'tenants',
      hidden: true,
    },
    {
      name: 'columns',
      type: 'array',
      admin: {
        description: "The columns for your footer.",
        components: {
          RowLabel: ({data, index}: any) => data?.columnTitle || `Column ${index}`
        }
      },
      fields: [
        {
          name: "columnTitle",
          label: "Column Title",
          type: "text",
          required: true,
        },
      ],
    },
  ],
}

But I get the following error:

[11:35:46] ERROR (payload): error: invalid reference to FROM-clause entry for table "tenants"
    at /home/bradleyk/IdeaProjects/retrobie/retrobie-backend/node_modules/pg/lib/client.js:526:17
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at /home/bradleyk/IdeaProjects/retrobie/retrobie-backend/node_modules/src/node-postgres/session.ts:64:19
    at find (/home/bradleyk/IdeaProjects/retrobie/retrobie-backend/node_modules/@payloadcms/db-postgres/src/find/findMany.ts:97:28)
    at Object.findGlobal (/home/bradleyk/IdeaProjects/retrobie/retrobie-backend/node_modules/@payloadcms/db-postgres/src/findGlobal.ts:18:7)
    at findOne (/home/bradleyk/IdeaProjects/retrobie/retrobie-backend/node_modules/payload/src/globals/operations/findOne.ts:53:15)
    at handler (/home/bradleyk/IdeaProjects/retrobie/retrobie-backend/node_modules/payload/src/globals/requestHandlers/findOne.ts:28:22)

Any ideas?

Custom domains

This isn't an issue, more so a question (sorry I didn't see a discussion board to post to) - How do I create custom domains for tenants, eg. tenant1.app.com, tenant2.app.com?

Cloud Media Uploads broken with tenancy

Hello,

Thank you for building this πŸ‘

I'm using the Cloud Upload plugin to keep my media in a storage bucket. I'm able to upload while logged into a tenant (tested both path & user strats) and the file gets uploaded. I'm not however able to GET the image via any combination of paths to the asset. While uploading, I get the following error:

[18:14:30] ERROR (payload): TypeError: Cannot read properties of undefined (reading 'skipTenancyUploadAfterReadHook')
    at /Volumes/GenericUser/GenericUser/payload-cms/node_modules/payload-tenancy/dist/hooks/upload.js:67:41
    at step (/Volumes/GenericUser/GenericUser/payload-cms/node_modules/payload-tenancy/dist/hooks/upload.js:44:23)
    at Object.next (/Volumes/GenericUser/GenericUser/payload-cms/node_modules/payload-tenancy/dist/hooks/upload.js:25:53)
    at /Volumes/GenericUser/GenericUser/payload-cms/node_modules/payload-tenancy/dist/hooks/upload.js:19:71
    at new Promise (<anonymous>)
    at __awaiter (/Volumes/GenericUser/GenericUser/payload-cms/node_modules/payload-tenancy/dist/hooks/upload.js:15:12)
    at /Volumes/GenericUser/GenericUser/payload-cms/node_modules/payload-tenancy/dist/hooks/upload.js:61:16
    at /Volumes/GenericUser/GenericUser/payload-cms/node_modules/payload/src/collections/operations/find.ts:223:24
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async /Volumes/GenericUser/GenericUser/payload-cms/node_modules/payload/src/collections/operations/find.ts:220:7

The media upload gets mapped to a correct looking URL:
User Strategy

http://localhost:3000/media/images/random-image.jpg

Path Strategy

http://localhost:3000/root/media/images/random-image.jpg

But both result in a failure to retrieve the media asset:

Cannot GET /media/images/random-image.jpg

How to use with local api (on collections)

For the new globals feature you added the following explanation in the docs:

To operate on isolated globals using Local API, you must pass user object with a tenant so that the correct document is accessed.

const globalDocument = await payload.findGlobal({
  slug: "settings",
  user: { tenant: someTenantOrId },
});

For regular collection, is adding user: { tenant: someTenantOrId } also the way to go?

Possible bug: Slug field invalid format

I noticed that a slug can be saved in a invalid format for example my slug, ideally this would convert to my-slug for the user.

In other payload projects i've used this utils in a beforeValidate hook, what are your thoughts of including it into the project or a similar solution?

function formatSlug({ value }) {
  if (!value || typeof value !== "string") {
    return "";
  }

  return value
    .trim()
    .toLowerCase()
    .replace(/[^\w\s-]/g, "") // Remove non-word characters
    .replace(/\s+/g, "-") // Replace spaces with hyphens
    .replace(/--+/g, "-") // Replace multiple hyphens with a single hyphen
    .replace(/^-+|-+$/g, ""); // Remove leading/trailing hyphens
}

Alternatively is it possible to extend your tenancy collection to pass this in myself for the field?

Add option to limit tenant hierarchy depth

Hello! Thank you for making this very useful plugin!

Some use cases such as mine only need 1-2 layers of tenancy.
Restricting this to a certain depth also patches up the "infinite subtenants" possibility.

With that, I suggest adding a depth setting for creating tenants.

User with same email on different tenants not possible

When a user is created in a specific tenant, it cannot be created with the same email on another tenant. For backend users (user editing via the payload admin panel) this is not a problem, but it is a problem for front-end users (customers that log into an ecommerce site).

As I see it we can fix this in a couple of ways:

  1. Allow for multiple users with the same email, while they have a different ID (seems vulnerable and don't think this is possible with payload)
  2. Have the tenancy plugin add the tenant slug before of after the email, so test@gmail becomes [email protected].
  3. Have the tenancy plugin create a sepereate auth collection for each tenant. I think this is the best option of the 3, so let me elaborate:

How I see option 3 would start by adding a config option to this plugin which allows us to choose which auth collection should be used to set the tenant and filter the docs in each collection. So:

plugins: [tenancy({ authCollection: "users" })],

Then we can create a seperate auth collection in payload for the frontend, which we call customers in this case.

This plugin would then need to create a collection named mytenant-customers and route the api request to the correct ones.

Pro: seems like easiest of the 3 to make
Con: Not in line with the nature of this plugin, which is filtering the same collection, not creating new ones

Would love to hear what you think!

Make it easier to modify fields

Currently fields added by this plugin cannot be modified by the project using the plugin. Only plugins that are applied after this plugin can currently modify the fields.

It should be to be possible to define fields in project's config by using same names as fields in this plugin is using and override properties that way.

Example

// Plugin adds following field:
const pluginTenantSlugField = {
  type: "text",
  name: "slug",
  unique: true,
  required: true,
};

// Field can be extended by defining the field in project's config:
const projectConfig = {
  collections: [
    {
      slug: "tenants",
      fields: [
        {
          type: "text",
          name: "slug",
          beforeValidate: ({ value }) => {
            if (!value || typeof value !== "string") {
              return "";
            }

            // Format slug.
            return value
              .trim()
              .toLowerCase()
              .replace(/[^\w\s-]/g, "")
              .replace(/\s+/g, "-")
              .replace(/--+/g, "-")
              .replace(/^-+|-+$/g, "");
          },
        },
      ],
    },
  ],
};

// Resulting field uses the project's field if possible and applies missing
// properties from plugin's field. Or uses the plugin's field if project does
// not have a field with the same name.
const projectTenantSlugField = tenantCollection.fields.find(
  (field) => field.name === "slug"
);
const resultingField = { ...pluginTenantSlugField, ...projectTenantSlugField };

The above way of merging works for this case but it might be neccessary to implement a merge function that handles nested objects.

Tenant field is not queried in resouce collections

When querying resource collections, the tenant field is not returned, making it impossible for the frontend to filter documents according to the tenant.

For example, here is the "pages" resource.

image

Even querying via GraphQL leads to the same result - tenant is always null.

And, since the tenant field doesn't exist, the frontend can't filter by tenant id, and users can see each other's documents.

Adding hidden: false to the CollectionConfig seems to help, but only when querying for a single page:

image

But when querying all the pages, it remains null:

image

However, it works just fine for the user collection:

image

Any clues what the issue might be?

Readme update to include install step

The readme is great and figured that I should mention that I had to go back to discord to find the link to the package on NPM https://www.npmjs.com/package/payload-tenancy. I know it's a small detail but could be nice to include it with the install command in the readme directly.

Willing to open a quick PR if it's helpful

Can't access any collections after installing

When a user opens any collection in the admin view (e.g. comments, posts, pages, etc), payload gives this error:

QueryError: The following path cannot be queried: version.tenant
at validateQueryPaths (/home/node/app/node_modules/payload/src/database/queryValidation/validateQueryPaths.ts:91:13)

using:

    "payload": "^2.11.1",
    "payload-tenancy": "^2.1.1",

After some investigation, this seems to only happen on some collections, I'm using the payload seed project, and in collections that don't load there is this line:

  versions: {
    drafts: true,
  },

Error when creating first user

Hi there,

I've added this package to my payload, and configured it as specified, however I'm running into an error when logging in to my account.

If I try to create the account programmatically, I get this error on log-in. If I try to create the account via the payload CMS first-user sign up, I get the same error.

Error:

TypeError: Cannot read properties of undefined (reading 'id')
    at eval (webpack-internal:///(api)/./node_modules/payload-tenancy/dist/utils/defaultAccess.js:170:60)
    at step (webpack-internal:///(api)/./node_modules/payload-tenancy/dist/utils/defaultAccess.js:107:23)
    at Object.eval [as next] (webpack-internal:///(api)/./node_modules/payload-tenancy/dist/utils/defaultAccess.js:48:20)
    at fulfilled (webpack-internal:///(api)/./node_modules/payload-tenancy/dist/utils/defaultAccess.js:11:32)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)

Payload.config

collections: [Users, Tenants, Pages, Posts, Category, Media],
 admin: {
    user: Users.slug,
 },
...
tenancy({ isolationStrategy: "domain" }),
...

Users Collection:

export const Users: CollectionConfig = {
  slug: "users",
  auth: true,
  admin: {
    useAsTitle: "email",
  },
  fields: [
    {
      name: "firstName",
      type: "text",
    },
    {
      name: "lastName",
      type: "text",
    },
  ],
};

Tenants Collection:

export const Tenants: CollectionConfig = {
  slug: "tenants",
  admin: {
    useAsTitle: "name",
  },
  fields: [
    {
      type: "text",
      name: "name",
      label: "Name",
      required: true,
    },
  ],
};

Seed Script:

 const myTenant = await payload.create({
      collection: "tenants",
      data: {
        slug: "my-company",
        domains: [{ domain: "french-gossip.com" }],
        name: "French Gossip",
      },
    });

 await payload.create({
     collection: "users",
      data: {
          firstName: "Nachi",
          lastName: "Robbins",
          password: "password",
          email: "[email protected]",
          roles: ["super-admin"],
          tenant: myTenant.id,
       },
   });

Any suggestions would be super appreciated!

A way to modify field labels for "tenant"

This could be done by implementing an option to specify the label for tenant in the options. You could say that tenant = "Organization" and it would change all the field labels. Or maybe something like that was suggested in #7 (comment), so that it would find the tenant label from the existing config.

npm install error

npm install payload-tenancy
npm ERR! code EUNSUPPORTEDPROTOCOL
npm ERR! Unsupported URL Type "workspace:": workspace:*

Setup form shows tenant field

Tenant field should be hidden from the setup form. There's no tenants yet at setup, so no value can be set. Clicking the field shows the loading animation indefinitely. Tenant is created after creating the first user.

This cannot be achieved by restricting access, because access rights are not taken into account in setup. There might not be a solution for this at current point in time without a fix / feature for Payload.

kuva

Cannot update Global after creating

Hi!

I create new "blank" instance of payload with this plugin. Also create shared global "Settings".

In admin panel user can create shared global "Settings" record, successfully save it to DB(Mongo), but than user cannot change value of this global. In dev panel in browser I see that new values POST-ed to server, but server response old values.

2.0: Globals can't be localized

Steps to reproduce:

  1. Create a basic global
import { GlobalConfig } from "payload/types";

export const Global: GlobalConfig = {
    slug: "Test",
    label: "Test",
    fields: [
        {
            name: "name",
            type: "text",
            localized: true,
        }
    ]
}
  1. Have localization enabled with more than one locale
  2. Save global entry in one locale
  3. Switch locales, type to change field
  4. Switch back to the original locale and you will see that it is overwritten

I am able to get the desired behaviour when I disable the tenancy plugin, for this issue I'm using my blank payload template (not my working project). This only appears to be happening on globals and not collections.

Demo:

Screen.Recording.2023-11-02.at.4.40.31.PM.mov

Access control questions

I have a use case as follows:

I have a single global tenant (Let's call this HQ).

I have multiple "Groups" (sub-tenants of HQ).

Each group has one or more territories under them (sub-tenants of the group).

The Groups themselves don't have a website, but each territory does. The groups are here only for access control to their territories.

Some users need to be at the "Group" level to log into any individual territory within the group, but should not be able to login to the group itself.

Some users need to be at the group level so that they can create new territories within the group.

Some users are specific to a territory - this already works.

Also, the group does not need any other collections than User and Tenant... Is there a way to hide my resource collections and globals from the "Group" level so that they can only see their child tenants and users?

Any help/guidance would be appreciated here.

Isolation stragery path prevents accessing the admin dashboard

I have a fresh create-payload-app and ran yarn add payload-tenancy, and added basic Tenants Collection (as in docs), but added isolation strategy to 'path' and after it the payload admin panel cannot be loaded anymore. If tenant isolation is changed to domain or user, then it works all properly.

Tenant isolation "path" strategy

I tried to use the "path" strategy, but when I ran the server, This localhost page can’t be found. when I returned to the user strategy, the site could now be accessed in localhost:3000. Do Any Idea why?

SCR-20231026-rfnx

There is also no error in the server.

SCR-20231026-rgah

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.