π’Organizations & Multitenancy
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.
Database structure
Org routes
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
Org plugs
We have some plugs to help with assigning org related data
assign_org_data
This will assign to the conn:
require_org_member
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.
require_org_admin
Same as require_org_member
above but the user must have a role of admin on their membership for the org.
Org on_mount hooks
You can run on_mount
hooks in your live_session
calls. For example:
We have hooks matching the plugs above:
Multi-tenancy
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.
1. Params (supported)
Load the org from a body or query param (how it's currently done).
2. Session (not yet supported)
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.
3. Subdomain (not yet supported)
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.
How to prevent data leakage - true multi-tenancy
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.
Removing organizations
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).
Steps 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
Last updated