Open zamith opened 3 years ago
Hi, I'm also in the works of trying to build a multi-tenant application dashboard and are looking into Kaffy for doing the heavy lifting. For multi-tenancy support on the database level I'm considering using Triplex, which is based around the concept of having a separate db schema for each tenant and then using Ecto's Query Prefix for querying the correct schema.
It would be super nice if the query prefix could somehow be supported in Kaffy as well, as it (at least for me) seems like a good way of separating ie. tenant data from each other as well as giving us the possibility to do cross-schema queries for "common data".
Unfortunately that does not work for me, since I need to have different databases, not only schemas. Have you tried using the custom queries callbacks? You can define a custom_show_query
on the admin module and add the prefix there, for example.
Then you have the issue of how to change between tenants, which I changed in my PR to be a select added via a type of extension called navigation extras. This is the trickier part in terms of UI and you can obviously choose to do it differently.
I see, thanks for the tip! I haven't gotten that far really so I wasn't aware of that possibility. That would certainly work for different schemas. As for using multiple databases, I totally agree. That would've been a very nice feature to support. I've looked through your PR and it looks really useful.
Hope to see it merged soon 👍
For completeness and based on the answer by @zamith I share my implementation of tenanted resourced based
defmodule AppWeb.Admin.Tenanted do
defmacro __using__(_opts) do
quote do
alias App.Repo
def custom_index_query(conn, _schema, query) do
query
|> Ecto.Queryable.to_query()
|> Map.put(:prefix, conn.assigns.prefix)
end
def custom_show_query(conn, _schema, query) do
query
|> Ecto.Queryable.to_query()
|> Map.put(:prefix, conn.assigns.prefix)
end
def insert(conn, changeset) do
Repo.insert(changeset, prefix: conn.assigns.prefix)
end
def update(conn, changeset) do
Repo.update(changeset, prefix: conn.assigns.prefix)
end
def delete(conn, changeset) do
Repo.delete(changeset, prefix: conn.assigns.prefix)
end
end
Then, when defining a resource (in this example, for the schema Student
)
defmodule AppWeb.Admin.StudentAdmin do
use AppWeb.Admin.Tenanted
end
Hope it might help someone out there.
Thanks for sharing @zenbaku
How are you handling the tenant switching? @zamith mentioned "navigation extras" but I don't see that mentioned in the documentation?
Currently kaffy assumes that there is only one repo, and even for that repo it does use the dynamic_repo which is now widespread in Ecto. This makes it hard to implement admin pages for multi tenant resources, e.g. user accounts within different organizations, in different databases or database schemas.
I understand supporting this is not straightforward, as we'd also need a way to change between tenants, which has UI implications. Given that it's not really that easy to change a template in kaffy it means having a generic answer to the problem. Have you given this any thought?