π’Organizations & Multitenancy
Last updated
Last updated
We have provided a basic structure for organizations, which are basically groups of people. Often user want to create some kind of organization (eg. a company), and then invite their teammates into it. This is a step towards multi-tenancy, with an organization representing a tenant.
If you were introducing a payment system, it would likely be associated with the org (we will implement this in v1.3.0.
The following routes are provided:
This provides some basic functionality to get you started:
Any user can CRUD an org
Basic roles (admin
& member
)
Members can create invitations for new members
Invitations turn into memberships upon acceptance
Admins can delete memberships
We have some plugs to help with assigning org related data
This will assign to the conn:
@orgs
Always
A list of orgs the current_user
is in
@current_membership
Only if :org_slug
is in the route
The current user's membership with the current org (using the provided slug).
@current_org
Only if :org_slug
is in the route
The current org (using the provided slug)
This makes sure the current_user is a member of the current org (determined by the URL param :org_slug
). The path must have :org_slug
in it.
Same as require_org_member
above but the user must have a role of admin on their membership for the org.
You can run on_mount
hooks in your live_session
calls. For example:
We have hooks matching the plugs above:
Currently you can only see your current org if you are in a route scoped to that org (/:org_slug/page
), with the org slug representing the tenant (org).
There are several options when it comes to identifying the current org (listed below). Please let us know if you want more options supported. We have a feedback form here.
Load the org from a body or query param (how it's currently done).
Set the current org as a cookie and fetch it in the plug from the cookie. Then you don't have to scope the route:
You will need to create a plug that sets the current org - you could use set_locale_plug.ex
as inspiration.
Set the tenant slug as a subdomain. Similar to "params" but means you don't have to scope all of your routes.
You would need to create a plug to do this.
Currently, all your orgs (tenants) data is in one database. It is up to you to scope all of your Ecto queries to ensure the data shown on the page belongs to the right org.
A potentially safer option is to make a new Postgres Schema per tenant with the help of Triplex. In which case your queries will be like this:
Triplex provides some plugs to help set the org:
If you are unsure about multi-tenancy, this blog post explains the different approaches well.
You will have to extract orgs out of your code. This will take approximately 15 minutes. If you have time, consider letting us know at support@petal.build if you do this so we can get an idea of how many people are doing it (if it's a lot, then we will consider making it easier to delete).
Delete the migrations:
Delete files and folders related to orgs:
Remove references in user.ex
and log.ex:
From here, you can just do a global search for "org" and delete what's left.
Accounts.preload_org_data/2
.
Router org routes.
Router.assign_org_data/2
plug.
Reference in Logs.build/2
.
Org related logs in log.ex
- @action_options
UserNotifier.deliver_org_invitation/3
Delete org_seeder.ex
References in seeds.exs
References in email_testing_controller.ex
References in email.ex
Email template: org_invitation.html.heex
Test folder:
org_team_live_test.exs
orgs_fixtures.ex
dashboard_live_test.exs