Ruby on rails 具有域/子域体系结构(如Shopify)的多租户rails应用程序
我正在构建一个多租户rails应用程序,类似于Shopify。每当客户注册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
帐户时,他们都会在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
最后是myapp/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欢迎我。我很高兴我的问题被问得很好,而且格式正确。事实上,在这段时间里,我自己解决了大部分问题,我将在测试完我的解决方案后立即更新它。