Ruby on rails 具有域/子域体系结构(如Shopify)的多租户rails应用程序

Ruby on rails 具有域/子域体系结构(如Shopify)的多租户rails应用程序,ruby-on-rails,subdomain,multi-tenant,Ruby On Rails,Subdomain,Multi Tenant,我正在构建一个多租户rails应用程序,类似于Shopify。每当客户注册帐户时,他们都会在customer.myapp.com上创建一个子域。此子域返回一个视图,其中包含与其帐户(包括a/admin区域)相关的数据 现在,在某些情况下,客户希望使用自己的自定义域,而不是为他们创建的子域。我需要如何调整我的rails路由和控制器,以返回一个包含与客户帐户相关数据的视图,不仅基于子域,而且基于自定义域或子域 这就是我如何设置处理子域的config/routes.rb: # config/route

我正在构建一个多租户rails应用程序,类似于Shopify。每当客户注册
帐户时,他们都会在
customer.myapp.com
上创建一个子域。此子域返回一个视图,其中包含与其帐户(包括a/admin区域)相关的数据

现在,在某些情况下,客户希望使用自己的自定义域,而不是为他们创建的子域。我需要如何调整我的rails路由和控制器,以返回一个包含与客户帐户相关数据的视图,不仅基于子域,而且基于自定义域或子域

这就是我如何设置处理子域的
config/routes.rb

# config/routes.rb
Rails.application.routes.draw do
  ...
  constraints(SubdomainRequired) do
    scope module: :accounts do
      ...
    end
  end
end
使用
app/constraints/subdomain\u required.rb
如下所示:

# app/constraints/subdomain_required.rb
class SubdomainRequired
  def self.matches?(request)
    request.subdomain.present? && request.subdomain != "www"
  end
end
最后是my
app/controllers/accounts/base\u controller.rb
根据请求子域设置当前帐户:

# app/controllers/accounts/base_controller.rb
module Accounts
  class BaseController < ApplicationController
    before_action :set_current_account
    ...
    def set_current_account
      if request.subdomain.present?
        @current_account = Account.find_by(subdomain: request.subdomain)
        render_404 if !@current_account
      end
    end

    def render_404
      render file: "#{Rails.root}/public/404.html", status: 404
    end
    ...
  end
end
#app/controllers/accounts/base_controller.rb
模块帐户
类BaseController<应用程序控制器
操作前:设置当前账户
...
def set_活期账户
如果request.subdomain.present存在?
@当前帐户=帐户。查找方式(子域:request.subdomain)
渲染404如果@经常账户
结束
结束
def渲染器404
呈现文件:“#{Rails.root}/public/404.html”,状态:404
结束
...
结束
结束
对于DNS设置,我遵循了Shopify等公司的做法。看起来他们让客户创建了一个指向其应用程序主IP地址的A记录。他们让他们为“www”子域创建一个CNAME,该子域指向一个子域(
sites.myapp.com
),设置为应用程序子域的CNAME(在我的例子中,由DigitalOcean的应用程序平台管理)

如果DNS设置按预期工作,我想知道当客户登陆我的应用程序时,如何将请求指向正确的方向。因此,例如,如果流量点击
customer.com
,它有一个指向我的服务器主IP的记录,加上
sites.myapp.com
的CNAME,我如何处理请求并将其重定向到正确的子域,同时让用户保持在自定义域上

以下是我迄今为止检查过的资源:


谢谢你的帮助

好吧,这就是我在这期间想到的

首先,我留下了我的
config/routes.rb
,如上所述。那里的一切似乎都按计划进行

然后我调整了
app/constraints/subdomain\u required.rb
以不仅检查子域,还检查请求的域部分:

# app/constraints/subdomain_required.rb
class SubdomainRequired
  def self.matches?(request)
    request.subdomain.present? && request.subdomain != "www" || 
      request.domain.present? && request.domain != ENV["APP_DOMAIN"]
  end
end
最后但并非最不重要的是,我将
app/controllers/accounts/base\u controller.rb中的
set\u current\u account
方法更改为if语句的怪物:

# app/controllers/accounts/base_controller.rb
module Accounts
  class BaseController < ApplicationController
    before_action :set_current_account
    ...
    def set_current_account
      @current_account = if request.domain.present? && request.domain != ENV["APP_DOMAIN"]
                           # Do custom domain stuff
                           if request.subdomain == "www" || !request.subdomain.present?
                             # Set current account by custom domain, e.g. custom.com
                             Account.find_by(domain: request.domain)
                           else
                             # Set current account by custom subdomain, e.g. site.custom.com
                             Account.find_by(domain: "#{request.subdomain + "." + request.domain}")
                           end
                         elsif request.subdomain.present? && request.subdomain != "www"
                           # Set current account by subdomain, e.g. site.myapp.com
                           Account.find_by(subdomain: request.subdomain)
                         end
      render_404 unless @current_account
    end
    ...
  end
end
#app/controllers/accounts/base_controller.rb
模块帐户
类BaseController<应用程序控制器
操作前:设置当前账户
...
def set_活期账户
@当前账户=如果请求.domain.present?&&request.domain!=环境[“应用程序域”]
#做自定义域的东西
如果request.subdomain==“www”| |!request.subdomain.present?
#按自定义域设置当前帐户,例如custom.com
帐户.查找依据(域:request.domain)
其他的
#按自定义子域设置当前帐户,例如site.custom.com
Account.find_by(域:“#{request.subdomain+”“+request.domain}”)
结束
elsif request.subdomain.present?&&request.subdomain!=“www”
#按子域设置当前帐户,例如site.myapp.com
Account.find_by(子域:request.subdomain)
结束
除非@current\u account
结束
...
结束
结束
到目前为止,我仍在测试我的解决方案。可能需要更多的调整,当然也需要一点重构,但它似乎可以工作

我必须解决的另一个(意想不到的)问题是整个DNS设置。与heroku类似,DigitalOcean的应用程序平台不支持静态IP地址,因此客户端无法创建指向我的应用程序IP地址的a记录,因为它可以随时更改

现在,我已经使用NGINX设置了一个转发代理(您是这样称呼它的吗?),基于heroku的以下示例:

希望这能帮助任何人,寻找类似的解决方案


如果需要详细说明,请告诉我。

欢迎来到StackOverflow。不幸的是,我不能帮助你,但我认为你的问题问得很好,格式也很正确(大多数新成员的情况并非如此)。继续!谢谢@Hecke29欢迎我。我很高兴我的问题被问得很好,而且格式正确。事实上,在这段时间里,我自己解决了大部分问题,我将在测试完我的解决方案后立即更新它。