Rails - Separate Database per Subdomain

后端 未结 4 1920
逝去的感伤
逝去的感伤 2020-12-07 16:52

I am about to begin writing a Rails application that will allow clients to have a separate subdomain for their access to our application. Thinking from a data security stand

相关标签:
4条回答
  • 2020-12-07 17:39

    I found a different solution that works a little easier, but makes the assumption you have a database for each Subdomain:

    application_controller.rb

    before_filter :subdomain_change_database
    
    def subdomain_change_database
      if request.subdomain.present? && request.subdomain != "www"
        # 'SOME_PREFIX_' + is optional, but would make DBs easier to delineate
        ActiveRecord::Base.establish_connection(website_connection('SOME_PREFIX_' + request.subdomain ))
      end
    end
    
    # Return regular connection hash but with database name changed
    # The database name is a attribute (column in the database)
    def website_connection(subdomain)
      default_connection.dup.update(:database => subdomain)
    end
    
    # Regular database.yml configuration hash
    def default_connection
      @default_config ||= ActiveRecord::Base.connection.instance_variable_get("@config").dup
    end
    

    This will switch to a database like mydb_subdomain. This is a complete replacement database option, but it makes it super easy to roll out multiple versions.

    0 讨论(0)
  • 2020-12-07 17:47

    Turns out I just asked a really similar question but quite a bit further along in development - I've included three ideas for how to go about securely using a single database in there.

    0 讨论(0)
  • 2020-12-07 17:49

    Here is some code I use for this very problem:

    application_controller.rb

    before_filter :set_database
    
    helper_method :current_website
    
    # I use the entire domain, just change to find_by_subdomain and pass only the subdomain
    def current_website    
      @website ||= Website.find_by_domain(request.host)
    end
    
    def set_database
      current_website.use_database
    end
    
    # Bonus - add view_path
    def set_paths
      self.prepend_view_path current_website.view_path unless current_website.view_path.blank?
    end
    

    Website.rb

    def use_database
      ActiveRecord::Base.establish_connection(website_connection)
    end
    
    # Revert back to the shared database
    def revert_database
      ActiveRecord::Base.establish_connection(default_connection)
    end
    
    private
    
    # Regular database.yml configuration hash
    def default_connection
      @default_config ||= ActiveRecord::Base.connection.instance_variable_get("@config").dup
    end
    
    # Return regular connection hash but with database name changed
    # The database name is a attribute (column in the database)
    def website_connection
      default_connection.dup.update(:database => database_name)
    end
    

    Hope this helps!

    0 讨论(0)
  • 2020-12-07 17:52

    Off the top of my head you could run a new server instance for each subdomain using different environment.

    But that won't scale very well.

    However the first few google hits for multiple rails databases turn up some new suggestions. Putting together the information in those links provides this wholly untested solution for a single server instance.

    You'll need to add a database entry for each subdomain in your databases.yml. Then add a before_filter to your application controller

    Update! Example reloads the database configurations dynamically. Unfortunately there's no good way to make the update rails wide without messing with your server's internals. So the database configuration will have to be reloaded on every request.

    This example assumes database entries in databases.yml are named after subdomains.

    config/database.yml

    login: &login
      adapter: mysql
      username: rails
      password: IamAStrongPassword!
      host:  localhost
    
    production:
      <<: *login
      database: mysite_www
    
    subdomain1:
      <<: *login
      database: mysite_subdomain1
    
    subdomain2:
      <<: *login
      database: mysite_subdomain2
    ...
    

    app/controllers/application_controller.rb require 'erb' before_filter :switch_db_connection

    def switch_db_connection
      subdomain = request.subdomains.first
      ActiveRecord::Base.configurations = YAML::load(ERB.new(IO.read(Rails.configuration.database_configuration_file)).result)
      ActiveRecord::Base.establish_connection("mysite_#{subdomain}") 
    end
    

    As I said it's completely untested. But I don't foresee any major problems. If it doesn't work hopefully it puts you on the right track.

    0 讨论(0)
提交回复
热议问题