多租户轨道应用程序与域/子域架构,如Shopify



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

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

这就是我为处理子域设置config/routes.rb的方式:

# config/routes.rb
Rails.application.routes.draw do
...
constraints(SubdomainRequired) do
scope module: :accounts do
...
end
end
end

app/constraints/subdomain_required.rb看起来像这样:

# app/constraints/subdomain_required.rb
class SubdomainRequired
def self.matches?(request)
request.subdomain.present? && request.subdomain != "www"
end
end

最后我的app/controllers/accounts/base_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

对于DNS设置,我遵循了像Shopify这样的公司的方法。看起来他们让客户创建了一个指向应用程序主IP地址的A记录。他们让他们创建一个CNAME;www";子域,指向子域(sites.myapp.com(,设置为应用程序子域的CNAME(在我的情况下,由DigitalOcean的应用程序平台管理(。

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

这些是我迄今为止检查过的资源:

  • 具有子域的Laravel应用程序,可以通过CNAME链接到其他域
  • rails上具有自定义域的多租户
  • 多租户动态多域,SSL指向单个主机/CDN
  • https://answers.netlify.com/t/subdomain-and-custom-domain-based-multi-tenant-application/30482/2
  • https://www.indiehackers.com/post/how-do-i-setup-a-domain-to-subdomain-architecture-like-shopify-70413820da
  • https://help.shopify.com/en/manual/online-store/domains/add-a-domain/using-existing-domains/connecting-domains#set-升级您的现有域以连接到shopify

感谢您的帮助!

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

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

然后我调整了app/constraints/subdomain_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_controller.rb中的set_current_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

到目前为止,我仍在测试我的解决方案。可能需要更多的调整,当然也需要一点重构,但它似乎正在发挥作用。

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

现在,我已经用NGINX建立了一个转发代理(你是这样称呼它的吗?(,基于以下heroku的例子:https://help.heroku.com/YTWRHLVH/how-do-i-make-my-nginx-proxy-connect-to-a-heroku-app-behind-heroku-ssl

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

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

最新更新