【问题标题】:Question about multi-tenant application design/architecture关于多租户应用程序设计/架构的问题
【发布时间】:2019-05-16 18:22:22
【问题描述】:

我正在制作一个应用程序,它使用 Apartment 进行多租户并使用 Devise 进行用户身份验证。

最初,会创建一个租户实例,然后将重定向到 subdomain.lvh.me/users/sign_up 以便用户可以注册并且仅是该特定子域架构的一部分。这样用户只被授权登录他们最初注册的子域,这就是我想要的。这也是有益的,因为我将每个 id=1 的用户设置为管理员,因此每次创建新租户时,它都会重定向到子域,并且第一个注册的用户将是默认管理员(有意义,因为用户会开始登陆页面,创建公司帐户(租户),然后创建一个包含其姓名、电子邮件、密码等的用户帐户,他们将是与该租户关联的第一个用户,因此是默认管理员。

这很有效,因为来自登陆页面的人(想象一下团队负责人 - 营销总监或其他)可以注册产品并设置公司信息,并且成为第一个具有管理员权限的用户。团队中的每个员工都将在子域上注册,并且无权登录另一个子域,或者他们必须在登录页面上注册,在这种情况下,他们将被视为新公司正在注册,而不是只是一个用户。

最近我实现了嵌套属性,以便我可以在一个表单/视图中完成初始注册过程,而不是两个。问题是因为用户实例是在租户控制器中创建的,它不是公司子域架构的一部分,因此无法登录 subdomain.lvh.me/users/sign_in。

我想知道之前是否有人遇到过这个挑战,以及您的解决方案是什么。改回原来的样子,先创建Tenant,重定向到subdomain/sign_up,再创建第一个User,会不会更好?

很抱歉,如果我没有尽可能清楚地表达这一点。

我在调用@tenant.users.build 之前尝试过重定向到子域,但它并没有像我想象的那样工作。

tenants_controller.rb 的一部分

def new
    @tenant = Tenant.new
    @tenant.users.build
    render layout: false
end

def create
    @tenant = Tenant.new(tenant_params)
    @tenant.update_attribute :subdomain, @tenant.company.downcase
    respond_to do |format|
      if @tenant.save 
        format.html { redirect_to "http://#{@tenant.subdomain}.lvh.me:3000/users/sign_in", notice: 'Domain was successfully created.' }
        #format.html { redirect_to new_user_registration_path, notice: 'Tenant was successfully created.' }
        format.json { render :show, status: :created, location: @tenant }
      else
        format.html { render :new }
        format.json { render json: @tenant.errors, status: :unprocessable_entity }
      end
   end
end

【问题讨论】:

  • “成为该特定子域架构的一部分”是什么意思
  • 也许我理解的不够准确,但从我读过的内容来看,每个子域都有一个单独的数据库模式,这就是为什么在我嵌套属性之前你可以在一个子域上创建一个帐户,它会'没有任何其他子域的授权

标签: ruby-on-rails multi-tenant


【解决方案1】:

我不确定您的应用和公寓是如何配置的。 我设置了一个租户(在我的情况下是工作区)属于一个所有者(它的类是用户)。这是摘录。

控制器/workspace_controller.rb

class WorkspacesController < ApplicationController
  def new
    @workspace = Workspace.new
    @workspace.build_owner
  end

  def create
    @workspace = Workspace.new(workspace_params)
    respond_to do |format|
      if @workspace.valid?
        Apartment::Tenant.create(@workspace.subdomain)
        Apartment::Tenant.switch(@workspace.subdomain) do
          @workspace.save
        end
        format.html { redirect_to new_user_session_url(subdomain: @workspace.subdomain), notice: t("flash.controllers.#{controller_name}.#{action_name}.success") }
      else
        format.html { render :new }
      end
    end
  end
end

models/workspace.rb

class Workspace < ApplicationRecord
  belongs_to :owner, class_name: 'User', required: true
  accepts_nested_attributes_for :owner
end

表单是使用工作区和所有者字段构建的。如果验证通过,则使用Apartment::Tenant.create(@workspace.subdomain) 创建一个新模式。然后我们将租户的实际创建包裹在Apartment switchApartment::Tenant.switch(@workspace.subdomain) do … 该块中的所有内容都将保存在架构subdomain 中,除了您在公寓配置中分配给config.excluded_models 的任何内容。在我的例子中,用户被保存到'subdomain'.'users',工作空间被保存到public.'workspaces'

不要对您的重定向目标进行硬编码。您可以将子域传递给 url-helper。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-01-26
    • 1970-01-01
    • 1970-01-01
    • 2011-11-03
    • 2021-05-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多