Facing very wired issue while using Paperclip (With s3 storage) and Apartment gem.
Apartment gem is being used for multi-tenancy with Postgres DB. ( i.e Separate Schema for every tenant )
For Storage, I would like to create a separate folder for every tenant.
class Media < ApplicationRecord
belongs_to :user
has_attached_file :file,
:storage => :s3,
:s3_credentials => {
:access_key_id => "ACCESSKEY",
:secret_access_key => "SECRETE KEY"
},
:path => "#{Apartment::Tenant.current}/:class/:attachment/:id_partition/:style/:filename"
end
Above is my media folder, which belongs to User.
class User < ApplicationRecord
has_attached_file :avatar,
:storage => :s3,
:s3_credentials => {
:access_key_id => "ACCESSKEY",
:secret_access_key => "SECRETE KEY"
},
:path => "#{Apartment::Tenant.current}/:class/:attachment/:id_partition/:style/:filename"
end
Apartment.rb file looks like below
require 'apartment/elevators/subdomain'
require 'rescued_apartment_middleware'
Apartment.configure do |config|
config.excluded_models = %w{ Account }
config.tenant_names = lambda { Account.pluck :subdomain }
config.use_schemas = true
end
Rails.application.config.middleware.insert_before Warden::Manager, RescuedApartmentElevator
Apartment::Elevators::Subdomain.excluded_subdomains = ['www','admin']
rescued_apartment_elevator.rb file looks like below
class RescuedApartmentElevator < Apartment::Elevators::Subdomain
def call(env)
begin
super
rescue Apartment::TenantNotFound
env[:apartment_tenant_not_found] = true # to be later referenced in your ApplicationController
@app.call(env) # the middleware call method should return this, but it was probably short-circuited by the raise
end
end
end
In application_controller.rb I have handled the code something like below
class ApplicationController < ActionController::Base
before_action :load_schema, :authenticate_user!, :set_mailer_host
before_action :configure_permitted_parameters, if: :devise_controller?
private
def load_schema
Apartment::Tenant.switch!
return if request.subdomain == "www"
if request.subdomain == ""
redirect_to root_url(subdomain: 'www')
else
if current_account
Apartment::Tenant.switch!(current_account.subdomain)
else
if request.env[:apartment_tenant_not_found]
redirect_to root_url(subdomain: 'www'), notice: 'Account you are looking for do not exists'
end
end
end
end
end
I start my server, go to one of the tenant e.g http://apple.lvh.me:3000 (apple is tenant name here)
1. If I go to edit user profile and upload the file for user profile, Apartment::Tenant.current is returning "public" in model and hence the file is getting uploaded in public folder instead of "apple" folder.
I am using devise gem (latest version) below is how my user_controller.rb looks like
class UsersController < ApplicationController
before_action : set_user, only: [:edit,:update]
def update
if @user.update(user_params)
if @user.pending_reconfirmation?
redirect_to edit_user_path(current_user), notice: "Your profile is successfully updated! But if you changed the email address, then please confirm it before!"
else
redirect_to edit_user_path(current_user), notice: "Your profile is successfully updated!"
end
else
render "edit"
end
end
end
Problem 2 **When I go to media and add some media, they would go properly to "apple" folder on s3 bucket. I would sign out, go to different tanent i.e http://google.lvh.me:3000/ (google is tenant name here). If I go to media listing, URL will continue to point to Apartment::Tenant.current -> apple only. This results in wrong URL for google tenant and images wont display.
If I shut down and restart the server, then rails will start returning google for Apartment::Tenant.current method and hence URL pointing correctly to correct folder. **
To Summerise, below are my problems.
- Using Apartment::Tenant.current in model for paperclip custom path - is it right way? Is there some other method that could return me current tanent so I can use it for creating folder on s3 bucket.
- Have I configured the middleware correctly in my apartment.rb file?
- Is there different way of doing this?