我正在构建一个多租户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
希望这能帮助任何寻求类似解决方案的人。
如果需要详细说明,请告诉我。